openc3 5.2.0 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/openc3cli +108 -105
- data/data/config/interface_modifiers.yaml +22 -4
- data/data/config/item_modifiers.yaml +4 -2
- data/data/config/microservice.yaml +18 -0
- data/data/config/table_manager.yaml +2 -2
- data/data/config/tool.yaml +1 -1
- data/ext/openc3/ext/config_parser/config_parser.c +17 -2
- data/lib/openc3/api/api.rb +1 -0
- data/lib/openc3/api/interface_api.rb +12 -0
- data/lib/openc3/api/metrics_api.rb +97 -0
- data/lib/openc3/api/router_api.rb +14 -2
- data/lib/openc3/api/target_api.rb +24 -3
- data/lib/openc3/api/tlm_api.rb +5 -4
- data/lib/openc3/config/config_parser.rb +29 -4
- data/lib/openc3/core_ext/time.rb +6 -1
- data/lib/openc3/interfaces/interface.rb +27 -26
- data/lib/openc3/interfaces/mqtt_interface.rb +240 -0
- data/lib/openc3/interfaces/protocols/override_protocol.rb +2 -61
- data/lib/openc3/interfaces/protocols/protocol.rb +6 -1
- data/lib/openc3/interfaces/simulated_target_interface.rb +1 -3
- data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -11
- data/lib/openc3/interfaces.rb +2 -3
- data/lib/openc3/logs/buffered_packet_log_reader.rb +2 -2
- data/lib/openc3/microservices/cleanup_microservice.rb +17 -1
- data/lib/openc3/microservices/decom_microservice.rb +12 -9
- data/lib/openc3/microservices/interface_microservice.rb +93 -9
- data/lib/openc3/microservices/log_microservice.rb +11 -5
- data/lib/openc3/microservices/microservice.rb +10 -9
- data/lib/openc3/microservices/periodic_microservice.rb +7 -0
- data/lib/openc3/microservices/reaction_microservice.rb +0 -33
- data/lib/openc3/microservices/reducer_microservice.rb +14 -10
- data/lib/openc3/microservices/text_log_microservice.rb +12 -3
- data/lib/openc3/microservices/timeline_microservice.rb +0 -6
- data/lib/openc3/microservices/trigger_group_microservice.rb +0 -20
- data/lib/openc3/models/cvt_model.rb +103 -47
- data/lib/openc3/models/interface_model.rb +23 -0
- data/lib/openc3/models/metric_model.rb +53 -6
- data/lib/openc3/models/microservice_model.rb +15 -1
- data/lib/openc3/models/model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +6 -1
- data/lib/openc3/models/secret_model.rb +53 -0
- data/lib/openc3/models/target_model.rb +2 -2
- data/lib/openc3/models/tool_model.rb +17 -8
- data/lib/openc3/operators/microservice_operator.rb +25 -0
- data/lib/openc3/operators/operator.rb +5 -1
- data/lib/openc3/packets/packet.rb +21 -7
- data/lib/openc3/packets/packet_item.rb +3 -2
- data/lib/openc3/script/api_shared.rb +18 -2
- data/lib/openc3/script/script.rb +8 -0
- data/lib/openc3/script/script_runner.rb +1 -2
- data/lib/openc3/script/storage.rb +2 -1
- data/lib/openc3/script/suite.rb +15 -11
- data/lib/openc3/system/system.rb +6 -3
- data/lib/openc3/topics/interface_topic.rb +17 -1
- data/lib/openc3/topics/router_topic.rb +17 -1
- data/lib/openc3/utilities/aws_bucket.rb +20 -3
- data/lib/openc3/utilities/bucket.rb +1 -1
- data/lib/openc3/utilities/bucket_file_cache.rb +1 -1
- data/lib/openc3/utilities/bucket_utilities.rb +1 -1
- data/lib/openc3/utilities/local_mode.rb +1 -0
- data/lib/openc3/utilities/metric.rb +77 -101
- data/lib/openc3/utilities/redis_secrets.rb +46 -0
- data/lib/openc3/utilities/s3_autoload.rb +19 -9
- data/lib/openc3/utilities/secrets.rb +63 -0
- data/lib/openc3/utilities/target_file.rb +3 -1
- data/lib/openc3/version.rb +5 -5
- data/templates/plugin-template/LICENSE.txt +7 -0
- data/templates/plugin-template/README.md +4 -3
- data/templates/plugin-template/plugin.gemspec +4 -4
- metadata +22 -3
- data/data/config/_interfaces.yaml.err +0 -1017
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
|
16
|
+
# This file may also be used under the terms of a commercial license
|
17
|
+
# if purchased from OpenC3, Inc.
|
18
|
+
|
19
|
+
require 'openc3/models/model'
|
20
|
+
|
21
|
+
module OpenC3
|
22
|
+
class SecretModel < Model
|
23
|
+
PRIMARY_KEY = 'openc3__secrets'
|
24
|
+
|
25
|
+
# NOTE: The following three class methods are used by the ModelController
|
26
|
+
# and are reimplemented to enable various Model class methods to work
|
27
|
+
def self.get(name:, scope:)
|
28
|
+
super("#{scope}__#{PRIMARY_KEY}", name: name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.names(scope:)
|
32
|
+
super("#{scope}__#{PRIMARY_KEY}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.all(scope:)
|
36
|
+
super("#{scope}__#{PRIMARY_KEY}")
|
37
|
+
end
|
38
|
+
# END NOTE
|
39
|
+
|
40
|
+
def initialize(name:, value:, scope:)
|
41
|
+
super("#{scope}__#{PRIMARY_KEY}", name: name, scope: scope)
|
42
|
+
@value = value
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Hash] JSON encoding of this model
|
46
|
+
def as_json(*a)
|
47
|
+
{
|
48
|
+
'name' => @name,
|
49
|
+
'value' => @value.as_json(*a),
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -101,7 +101,7 @@ module OpenC3
|
|
101
101
|
targets[target_name]['modified'] = true if targets[target_name]
|
102
102
|
end
|
103
103
|
else
|
104
|
-
modified_targets = Bucket.getClient().
|
104
|
+
modified_targets = Bucket.getClient().list_files(bucket: ENV['OPENC3_CONFIG_BUCKET'], path: "DEFAULT/targets_modified/", only_directories: true)
|
105
105
|
modified_targets.each do |target_name|
|
106
106
|
# A target could have been deleted without removing the modified files
|
107
107
|
# Thus we have to check for the existance of the target_name key
|
@@ -313,7 +313,7 @@ module OpenC3
|
|
313
313
|
reduced_day_log_retain_time: nil,
|
314
314
|
cleanup_poll_time: 900,
|
315
315
|
needs_dependencies: false,
|
316
|
-
target_microservices: {},
|
316
|
+
target_microservices: {'REDUCER' => [[]]},
|
317
317
|
scope:
|
318
318
|
)
|
319
319
|
super("#{scope}__#{PRIMARY_KEY}", name: name, plugin: plugin, updated_at: updated_at,
|
@@ -93,8 +93,15 @@ module OpenC3
|
|
93
93
|
# The ToolsTab.vue calls the ToolsController which uses this method to reorder the tools
|
94
94
|
# Position is index in the list starting with 0 = first
|
95
95
|
def self.set_position(name:, position:, scope:)
|
96
|
-
|
97
|
-
|
96
|
+
moving = from_json(get(name: name, scope: scope), scope: scope)
|
97
|
+
old_pos = moving.position
|
98
|
+
new_pos = Integer(position)
|
99
|
+
direction = :down
|
100
|
+
if (old_pos == new_pos)
|
101
|
+
return # we're not doing anything
|
102
|
+
elsif (new_pos > old_pos)
|
103
|
+
direction = :up
|
104
|
+
end
|
98
105
|
|
99
106
|
# Go through all the tools and reorder
|
100
107
|
all(scope: scope).each do |_tool_name, tool|
|
@@ -102,12 +109,14 @@ module OpenC3
|
|
102
109
|
# Update the requested model to the new position
|
103
110
|
if tool_model.name == name
|
104
111
|
tool_model.position = position
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
112
|
+
elsif direction == :down
|
113
|
+
if tool_model.position >= new_pos && tool_model.position < old_pos
|
114
|
+
tool_model.position += 1
|
115
|
+
end
|
116
|
+
else # up
|
117
|
+
if tool_model.position > old_pos && tool_model.position <= new_pos
|
118
|
+
tool_model.position -= 1
|
119
|
+
end
|
111
120
|
end
|
112
121
|
tool_model.update
|
113
122
|
end
|
@@ -23,8 +23,10 @@
|
|
23
23
|
require 'openc3'
|
24
24
|
require 'openc3/models/microservice_model'
|
25
25
|
require 'openc3/operators/operator'
|
26
|
+
require 'openc3/utilities/secrets'
|
26
27
|
require 'redis'
|
27
28
|
require 'open3'
|
29
|
+
require 'fileutils'
|
28
30
|
|
29
31
|
module OpenC3
|
30
32
|
# Creates new OperatorProcess objects based on querying the Redis key value store.
|
@@ -34,6 +36,7 @@ module OpenC3
|
|
34
36
|
Logger.microservice_name = "MicroserviceOperator"
|
35
37
|
super
|
36
38
|
|
39
|
+
@secrets = Secrets.getClient
|
37
40
|
@microservices = {}
|
38
41
|
@previous_microservices = {}
|
39
42
|
@new_microservices = {}
|
@@ -53,6 +56,28 @@ module OpenC3
|
|
53
56
|
env['OPENC3_MICROSERVICE_NAME'] = microservice_name
|
54
57
|
container = microservice_config["container"]
|
55
58
|
scope = microservice_name.split("__")[0]
|
59
|
+
|
60
|
+
# Setup secrets for microservice
|
61
|
+
secrets = microservice_config["secrets"]
|
62
|
+
if secrets
|
63
|
+
secrets.each do |type, secret_name, env_name_or_path|
|
64
|
+
secret_value = @secrets.get(secret_name, scope: scope)
|
65
|
+
if secret_value
|
66
|
+
case type
|
67
|
+
when 'ENV'
|
68
|
+
env[env_name_or_path] = secret_value
|
69
|
+
when 'FILE'
|
70
|
+
FileUtils.mkdir_p(File.dirname(env_name_or_path))
|
71
|
+
File.open(env_name_or_path, 'wb') do |file|
|
72
|
+
file.write(secret_value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
else
|
76
|
+
Logger.error("Microservice #{microservice_name} references unknown secret: #{secret_name}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
56
81
|
return process_definition, work_dir, env, scope, container
|
57
82
|
end
|
58
83
|
|
@@ -160,7 +160,11 @@ module OpenC3
|
|
160
160
|
def soft_stop
|
161
161
|
Thread.new do
|
162
162
|
Logger.info("Soft shutting down process: #{cmd_line()}", scope: @scope)
|
163
|
-
|
163
|
+
begin
|
164
|
+
Process.kill("SIGINT", @process.pid) if @process # Signal the process to stop
|
165
|
+
rescue Errno::ESRCH
|
166
|
+
# Process already gone
|
167
|
+
end
|
164
168
|
end
|
165
169
|
end
|
166
170
|
|
@@ -33,7 +33,7 @@ module OpenC3
|
|
33
33
|
# as managing PacketItem's limit states.
|
34
34
|
class Packet < Structure
|
35
35
|
RESERVED_ITEM_NAMES = ['PACKET_TIMESECONDS'.freeze, 'PACKET_TIMEFORMATTED'.freeze, 'RECEIVED_TIMESECONDS'.freeze, 'RECEIVED_TIMEFORMATTED'.freeze, 'RECEIVED_COUNT'.freeze]
|
36
|
-
|
36
|
+
ANY_STATE = 'ANY'
|
37
37
|
|
38
38
|
# @return [String] Name of the target this packet is associated with
|
39
39
|
attr_reader :target_name
|
@@ -627,8 +627,8 @@ module OpenC3
|
|
627
627
|
value = value.map do |val, index|
|
628
628
|
if item.states.key(val)
|
629
629
|
item.states.key(val)
|
630
|
-
elsif item.states.values.include?(
|
631
|
-
item.states.key(
|
630
|
+
elsif item.states.values.include?(ANY_STATE)
|
631
|
+
item.states.key(ANY_STATE)
|
632
632
|
else
|
633
633
|
apply_format_string_and_units(item, val, value_type)
|
634
634
|
end
|
@@ -637,8 +637,8 @@ module OpenC3
|
|
637
637
|
state_value = item.states.key(value)
|
638
638
|
if state_value
|
639
639
|
value = state_value
|
640
|
-
elsif item.states.values.include?(
|
641
|
-
value = item.states.key(
|
640
|
+
elsif item.states.values.include?(ANY_STATE)
|
641
|
+
value = item.states.key(ANY_STATE)
|
642
642
|
else
|
643
643
|
value = apply_format_string_and_units(item, value, value_type)
|
644
644
|
end
|
@@ -653,7 +653,14 @@ module OpenC3
|
|
653
653
|
end
|
654
654
|
end
|
655
655
|
else
|
656
|
-
|
656
|
+
# Trim a potentially long string (like if they accidentally pass buffer as value_type)
|
657
|
+
if value_type.to_s.length > 10
|
658
|
+
value_type = value_type.to_s[0...10]
|
659
|
+
# Ensure we're not trying to output binary
|
660
|
+
value_type = value_type.simple_formatted unless value_type.is_printable?
|
661
|
+
value_type += '...'
|
662
|
+
end
|
663
|
+
raise ArgumentError, "Unknown value type '#{value_type}', must be :RAW, :CONVERTED, :FORMATTED, or :WITH_UNITS"
|
657
664
|
end
|
658
665
|
return value
|
659
666
|
end
|
@@ -716,7 +723,14 @@ module OpenC3
|
|
716
723
|
when :FORMATTED, :WITH_UNITS
|
717
724
|
raise ArgumentError, "Invalid value type on write: #{value_type}"
|
718
725
|
else
|
719
|
-
|
726
|
+
# Trim potentially long string (like if they accidentally pass buffer as value_type)
|
727
|
+
if value_type.to_s.length > 10
|
728
|
+
value_type = value_type.to_s[0...10]
|
729
|
+
# Ensure we're not trying to output binary
|
730
|
+
value_type = value_type.simple_formatted unless value_type.is_printable?
|
731
|
+
value_type += '...'
|
732
|
+
end
|
733
|
+
raise ArgumentError, "Unknown value type '#{value_type}', must be :RAW, :CONVERTED, :FORMATTED, or :WITH_UNITS"
|
720
734
|
end
|
721
735
|
if @read_conversion_cache
|
722
736
|
synchronize() do
|
@@ -493,7 +493,7 @@ module OpenC3
|
|
493
493
|
if self.limits.values
|
494
494
|
config['limits'] ||= {}
|
495
495
|
config['limits']['persistence_setting'] = self.limits.persistence_setting
|
496
|
-
config['limits']['
|
496
|
+
config['limits']['response'] = self.limits.response.to_s if self.limits.response
|
497
497
|
self.limits.values.each do |limits_set, limits_values|
|
498
498
|
limits = {}
|
499
499
|
limits['red_low'] = limits_values[0]
|
@@ -557,9 +557,10 @@ module OpenC3
|
|
557
557
|
|
558
558
|
item.limits = PacketItemLimits.new
|
559
559
|
if hash['limits']
|
560
|
-
# Delete
|
560
|
+
# Delete the keys so the only ones left are limits sets
|
561
561
|
persistence_setting = hash['limits'].delete('persistence_setting')
|
562
562
|
item.limits.persistence_setting = persistence_setting if persistence_setting
|
563
|
+
hash['limits'].delete('response') # Can't round trip response
|
563
564
|
item.limits.enabled = true if hash['limits'].delete('enabled')
|
564
565
|
values = {}
|
565
566
|
hash['limits'].each do |set, items|
|
@@ -17,7 +17,7 @@
|
|
17
17
|
# All changes Copyright 2022, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'openc3/script/extract'
|
@@ -500,7 +500,20 @@ module OpenC3
|
|
500
500
|
def load_utility(procedure_name)
|
501
501
|
return start(procedure_name)
|
502
502
|
end
|
503
|
-
|
503
|
+
def require_utility(procedure_name)
|
504
|
+
@require_utility_cache ||= {}
|
505
|
+
if @require_utility_cache[procedure_name]
|
506
|
+
return false
|
507
|
+
else
|
508
|
+
@require_utility_cache[procedure_name] = true
|
509
|
+
begin
|
510
|
+
return start(procedure_name)
|
511
|
+
rescue LoadError
|
512
|
+
@require_utility_cache[procedure_name] = false
|
513
|
+
raise # reraise the error
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
504
517
|
|
505
518
|
###########################################################################
|
506
519
|
# Private implementation details
|
@@ -714,6 +727,7 @@ module OpenC3
|
|
714
727
|
|
715
728
|
def _openc3_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: $openc3_scope, token: $openc3_token, &block)
|
716
729
|
end_time = Time.now.sys + timeout
|
730
|
+
raise "Invalid comparison to non-ascii value" unless exp_to_eval.is_printable?
|
717
731
|
|
718
732
|
while true
|
719
733
|
work_start = Time.now.sys
|
@@ -792,6 +806,7 @@ module OpenC3
|
|
792
806
|
# Wait on an expression to be true.
|
793
807
|
def openc3_script_wait_implementation_expression(exp_to_eval, timeout, polling_rate, context, scope: $openc3_scope, token: $openc3_token)
|
794
808
|
end_time = Time.now.sys + timeout
|
809
|
+
raise "Invalid comparison to non-ascii value" unless exp_to_eval.is_printable?
|
795
810
|
|
796
811
|
while true
|
797
812
|
work_start = Time.now.sys
|
@@ -828,6 +843,7 @@ module OpenC3
|
|
828
843
|
end
|
829
844
|
|
830
845
|
def check_eval(target_name, packet_name, item_name, comparison_to_eval, value, scope: $openc3_scope, token: $openc3_token)
|
846
|
+
raise "Invalid comparison to non-ascii value" unless comparison_to_eval.is_printable?
|
831
847
|
string = "value " + comparison_to_eval
|
832
848
|
check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}"
|
833
849
|
# Show user the check against a quoted string
|
data/lib/openc3/script/script.rb
CHANGED
@@ -178,6 +178,14 @@ module OpenC3
|
|
178
178
|
print "Details: #{details}\n" if details
|
179
179
|
gets.chomp
|
180
180
|
end
|
181
|
+
|
182
|
+
def step_mode
|
183
|
+
# NOOP
|
184
|
+
end
|
185
|
+
|
186
|
+
def run_mode
|
187
|
+
# NOOP
|
188
|
+
end
|
181
189
|
end
|
182
190
|
|
183
191
|
# Provides a proxy to the JsonDRbObject which communicates with the API server
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
#
|
16
|
-
# This file may also be used under the terms of a commercial license
|
16
|
+
# This file may also be used under the terms of a commercial license
|
17
17
|
# if purchased from OpenC3, Inc.
|
18
18
|
|
19
19
|
module OpenC3
|
@@ -236,6 +236,5 @@ module OpenC3
|
|
236
236
|
return JSON.parse(response.body, :allow_nan => true, :create_additions => true)
|
237
237
|
end
|
238
238
|
end
|
239
|
-
|
240
239
|
end
|
241
240
|
end
|
@@ -157,7 +157,8 @@ module OpenC3
|
|
157
157
|
case ENV['OPENC3_CLOUD']
|
158
158
|
when 'local'
|
159
159
|
uri = URI.parse("http://openc3-minio:9000" + url)
|
160
|
-
|
160
|
+
when 'aws'
|
161
|
+
uri = URI.parse("https://s3.#{ENV['AWS_REGION']}.amazonaws.com" + url)
|
161
162
|
when 'gcp'
|
162
163
|
uri = URI.parse("https://storage.googleapis.com" + url)
|
163
164
|
# when 'azure'
|
data/lib/openc3/script/suite.rb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
# All changes Copyright 2022, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'openc3/script/exceptions'
|
@@ -37,14 +37,12 @@ module OpenC3
|
|
37
37
|
# START PUBLIC API
|
38
38
|
###########################################################################
|
39
39
|
|
40
|
-
#
|
41
|
-
def initialize
|
42
|
-
@scripts = {}
|
43
|
-
@plans = []
|
44
|
-
end
|
40
|
+
# Explictly avoid creating an initialize method which forces end users to call super()
|
45
41
|
|
46
42
|
# Add a group to the suite
|
47
43
|
def add_group(group_class)
|
44
|
+
@scripts ||= {}
|
45
|
+
@plans ||= []
|
48
46
|
group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
|
49
47
|
@scripts[group_class] = group_class.new unless @scripts[group_class]
|
50
48
|
@plans << [:GROUP, group_class, nil]
|
@@ -52,6 +50,8 @@ module OpenC3
|
|
52
50
|
|
53
51
|
# Add a script to the suite
|
54
52
|
def add_script(group_class, script)
|
53
|
+
@scripts ||= {}
|
54
|
+
@plans ||= []
|
55
55
|
group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
|
56
56
|
@scripts[group_class] = group_class.new unless @scripts[group_class]
|
57
57
|
@plans << [:SCRIPT, group_class, script]
|
@@ -59,6 +59,8 @@ module OpenC3
|
|
59
59
|
|
60
60
|
# Add a group setup to the suite
|
61
61
|
def add_group_setup(group_class)
|
62
|
+
@scripts ||= {}
|
63
|
+
@plans ||= []
|
62
64
|
group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
|
63
65
|
@scripts[group_class] = group_class.new unless @scripts[group_class]
|
64
66
|
@plans << [:GROUP_SETUP, group_class, nil]
|
@@ -66,6 +68,8 @@ module OpenC3
|
|
66
68
|
|
67
69
|
# Add a group teardown to the suite
|
68
70
|
def add_group_teardown(group_class)
|
71
|
+
@scripts ||= {}
|
72
|
+
@plans ||= []
|
69
73
|
group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
|
70
74
|
@scripts[group_class] = group_class.new unless @scripts[group_class]
|
71
75
|
@plans << [:GROUP_TEARDOWN, group_class, nil]
|
@@ -269,11 +273,7 @@ module OpenC3
|
|
269
273
|
@@abort_on_exception = false
|
270
274
|
@@current_result = nil
|
271
275
|
|
272
|
-
|
273
|
-
@output_io = StringIO.new('', 'r+')
|
274
|
-
$stdout = Stdout.instance
|
275
|
-
$stderr = Stderr.instance
|
276
|
-
end
|
276
|
+
# Explictly avoid creating an initialize method which forces end users to call super()
|
277
277
|
|
278
278
|
def self.abort_on_exception
|
279
279
|
@@abort_on_exception
|
@@ -350,7 +350,11 @@ module OpenC3
|
|
350
350
|
|
351
351
|
# Verify script method exists
|
352
352
|
if object.class.method_defined?(method_name)
|
353
|
+
@output_io ||= StringIO.new('', 'r+')
|
353
354
|
# Capture STDOUT and STDERR
|
355
|
+
# $stdout & $stderr must be set to change output
|
356
|
+
$stdout = Stdout.instance
|
357
|
+
$stderr = Stderr.instance
|
354
358
|
$stdout.add_stream(@output_io)
|
355
359
|
$stderr.add_stream(@output_io)
|
356
360
|
|
data/lib/openc3/system/system.rb
CHANGED
@@ -131,11 +131,14 @@ module OpenC3
|
|
131
131
|
|
132
132
|
target = Target.new(target_name, target_config_dir)
|
133
133
|
@targets[target.name] = target
|
134
|
+
errors = [] # Store all errors processing the cmd_tlm files
|
134
135
|
target.cmd_tlm_files.each do |cmd_tlm_file|
|
135
136
|
@packet_config.process_file(cmd_tlm_file, target.name)
|
136
|
-
rescue Exception =>
|
137
|
-
|
138
|
-
|
137
|
+
rescue Exception => error
|
138
|
+
errors << "Error processing #{cmd_tlm_file}:\n#{error.message}"
|
139
|
+
end
|
140
|
+
unless errors.empty?
|
141
|
+
raise errors.join("\n")
|
139
142
|
end
|
140
143
|
end
|
141
144
|
end
|
@@ -38,7 +38,7 @@ module OpenC3
|
|
38
38
|
def self.receive_commands(interface, scope:)
|
39
39
|
while true
|
40
40
|
Topic.read_topics(InterfaceTopic.topics(interface, scope: scope)) do |topic, msg_id, msg_hash, redis|
|
41
|
-
result = yield topic, msg_hash
|
41
|
+
result = yield topic, msg_id, msg_hash, redis
|
42
42
|
ack_topic = topic.split("__")
|
43
43
|
ack_topic[1] = 'ACK' + ack_topic[1]
|
44
44
|
ack_topic = ack_topic.join("__")
|
@@ -76,5 +76,21 @@ module OpenC3
|
|
76
76
|
sleep 1 # Give some time for the interface to shutdown
|
77
77
|
InterfaceTopic.clear_topics(InterfaceTopic.topics(interface, scope: scope))
|
78
78
|
end
|
79
|
+
|
80
|
+
def interface_cmd(interface_name, cmd_name, *cmd_params, scope:)
|
81
|
+
data = {}
|
82
|
+
data['cmd_name'] = cmd_name
|
83
|
+
data['cmd_params'] = cmd_params
|
84
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'interface_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
85
|
+
end
|
86
|
+
|
87
|
+
def protocol_cmd(interface_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
|
88
|
+
data = {}
|
89
|
+
data['cmd_name'] = cmd_name
|
90
|
+
data['cmd_params'] = cmd_params
|
91
|
+
data['read_write'] = read_write.to_s.upcase
|
92
|
+
data['index'] = index
|
93
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
94
|
+
end
|
79
95
|
end
|
80
96
|
end
|
@@ -40,7 +40,7 @@ module OpenC3
|
|
40
40
|
def self.receive_telemetry(router, scope:)
|
41
41
|
while true
|
42
42
|
Topic.read_topics(RouterTopic.topics(router, scope: scope)) do |topic, msg_id, msg_hash, redis|
|
43
|
-
result = yield topic, msg_hash
|
43
|
+
result = yield topic, msg_id, msg_hash, redis
|
44
44
|
if /CMD}ROUTER/.match?(topic)
|
45
45
|
ack_topic = topic.split("__")
|
46
46
|
ack_topic[1] = 'ACK' + ack_topic[1]
|
@@ -88,5 +88,21 @@ module OpenC3
|
|
88
88
|
sleep 1 # Give some time for the interface to shutdown
|
89
89
|
RouterTopic.clear_topics(RouterTopic.topics(router, scope: scope))
|
90
90
|
end
|
91
|
+
|
92
|
+
def router_cmd(router_name, cmd_name, *cmd_params, scope:)
|
93
|
+
data = {}
|
94
|
+
data['cmd_name'] = cmd_name
|
95
|
+
data['cmd_params'] = cmd_params
|
96
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
97
|
+
end
|
98
|
+
|
99
|
+
def protocol_cmd(router_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
|
100
|
+
data = {}
|
101
|
+
data['cmd_name'] = cmd_name
|
102
|
+
data['cmd_params'] = cmd_params
|
103
|
+
data['read_write'] = read_write.to_s.upcase
|
104
|
+
data['index'] = index
|
105
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
106
|
+
end
|
91
107
|
end
|
92
108
|
end
|
@@ -121,15 +121,20 @@ module OpenC3
|
|
121
121
|
result
|
122
122
|
end
|
123
123
|
|
124
|
-
# Lists the
|
125
|
-
def
|
124
|
+
# Lists the files under a specified path
|
125
|
+
def list_files(bucket:, path:, only_directories: false)
|
126
126
|
# Trailing slash is important in AWS S3 when listing files
|
127
127
|
# See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Types/ListObjectsV2Output.html#common_prefixes-instance_method
|
128
128
|
if path[-1] != '/'
|
129
129
|
path += '/'
|
130
130
|
end
|
131
|
+
# If we're searching for the root then kill the path or AWS will return nothing
|
132
|
+
path = nil if path == '/'
|
133
|
+
|
131
134
|
token = nil
|
132
135
|
result = []
|
136
|
+
dirs = []
|
137
|
+
files = []
|
133
138
|
while true
|
134
139
|
resp = @client.list_objects_v2({
|
135
140
|
bucket: bucket,
|
@@ -141,7 +146,19 @@ module OpenC3
|
|
141
146
|
resp.common_prefixes.each do |item|
|
142
147
|
# If path was DEFAULT/targets_modified/ then the
|
143
148
|
# results look like DEFAULT/targets_modified/INST/
|
144
|
-
|
149
|
+
dirs << item.prefix.split('/')[-1]
|
150
|
+
end
|
151
|
+
if only_directories
|
152
|
+
result = dirs
|
153
|
+
else
|
154
|
+
resp.contents.each do |aws_item|
|
155
|
+
item = {}
|
156
|
+
item['name'] = aws_item.key.split('/')[-1]
|
157
|
+
item['modified'] = aws_item.last_modified
|
158
|
+
item['size'] = aws_item.size
|
159
|
+
files << item
|
160
|
+
end
|
161
|
+
result = [dirs, files]
|
145
162
|
end
|
146
163
|
break unless resp.is_truncated
|
147
164
|
token = resp.next_continuation_token
|
@@ -57,7 +57,7 @@ module OpenC3
|
|
57
57
|
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
58
58
|
end
|
59
59
|
|
60
|
-
def
|
60
|
+
def list_files(bucket:, path:, only_directories: false)
|
61
61
|
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
62
62
|
end
|
63
63
|
|
@@ -73,7 +73,7 @@ class BucketFile
|
|
73
73
|
local_path = "#{BucketFileCache.instance.cache_dir}/#{File.basename(@bucket_path)}"
|
74
74
|
unless File.exist?(local_path)
|
75
75
|
OpenC3::Logger.debug "Retrieving #{@bucket_path} from logs bucket"
|
76
|
-
client.get_object(bucket:
|
76
|
+
client.get_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: @bucket_path, path: local_path)
|
77
77
|
if File.exist?(local_path)
|
78
78
|
basename = File.basename(local_path)
|
79
79
|
if uncompress and File.extname(basename) == ".gz"
|
@@ -46,7 +46,7 @@ module OpenC3
|
|
46
46
|
return oldest_list
|
47
47
|
end
|
48
48
|
|
49
|
-
directories = client.
|
49
|
+
directories = client.list_files(bucket: bucket, path: prefix, only_directories: true)
|
50
50
|
filtered_directories = filter_directories_to_time_range(directories, start_time, end_time)
|
51
51
|
filtered_directories.each do |directory|
|
52
52
|
directory_files = client.list_objects(bucket: bucket, prefix: "#{prefix}/#{directory}", max_request: max_request, max_total: max_total)
|