openc3 5.0.6 → 5.0.9
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 +133 -69
- data/data/config/command_modifiers.yaml +2 -2
- data/data/config/tool.yaml +8 -0
- data/data/config/widgets.yaml +78 -71
- data/lib/openc3/api/cmd_api.rb +5 -2
- data/lib/openc3/api/interface_api.rb +15 -0
- data/lib/openc3/api/limits_api.rb +3 -3
- data/lib/openc3/core_ext/file.rb +5 -0
- data/lib/openc3/io/json_rpc.rb +3 -3
- data/lib/openc3/microservices/cleanup_microservice.rb +7 -5
- data/lib/openc3/models/cvt_model.rb +6 -6
- data/lib/openc3/models/interface_model.rb +41 -0
- data/lib/openc3/models/microservice_model.rb +7 -1
- data/lib/openc3/models/plugin_model.rb +4 -2
- data/lib/openc3/models/process_status_model.rb +3 -1
- data/lib/openc3/models/scope_model.rb +41 -1
- data/lib/openc3/models/target_model.rb +138 -4
- data/lib/openc3/models/tool_model.rb +3 -0
- data/lib/openc3/operators/microservice_operator.rb +2 -2
- data/lib/openc3/operators/operator.rb +17 -2
- data/lib/openc3/packets/parsers/xtce_parser.rb +68 -3
- data/lib/openc3/script/storage.rb +18 -1
- data/lib/openc3/utilities/local_mode.rb +518 -0
- data/lib/openc3/utilities/s3.rb +16 -0
- data/lib/openc3/utilities/target_file.rb +146 -0
- data/lib/openc3/version.rb +5 -5
- metadata +15 -13
@@ -40,10 +40,47 @@ module OpenC3
|
|
40
40
|
super(PRIMARY_KEY)
|
41
41
|
end
|
42
42
|
|
43
|
+
def self.from_json(json, scope: nil)
|
44
|
+
json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
|
45
|
+
raise "json data is nil" if json.nil?
|
46
|
+
|
47
|
+
json.transform_keys!(&:to_sym)
|
48
|
+
self.new(**json, scope: scope)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.get_model(name:, scope: nil)
|
52
|
+
json = get(name: name)
|
53
|
+
if json
|
54
|
+
return from_json(json)
|
55
|
+
else
|
56
|
+
return nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
43
60
|
def initialize(name:, updated_at: nil, scope: nil)
|
44
61
|
super(PRIMARY_KEY, name: name, scope: name, updated_at: updated_at)
|
45
62
|
end
|
46
63
|
|
64
|
+
def create(update: false, force: false)
|
65
|
+
# Ensure there are no "." in the scope name - prevents gems accidently becoming scope names
|
66
|
+
raise "Invalid scope name: #{@name}" if @name !~ /^[a-zA-Z0-9_-]+$/
|
67
|
+
@name = @name.upcase
|
68
|
+
super(update: update, force: force)
|
69
|
+
end
|
70
|
+
|
71
|
+
def destroy
|
72
|
+
if @name != 'DEFAULT'
|
73
|
+
# Remove all the plugins for this scope
|
74
|
+
plugins = PluginModel.get_all_models(scope: @name)
|
75
|
+
plugins.each do |plugin_name, plugin|
|
76
|
+
plugin.destroy
|
77
|
+
end
|
78
|
+
super()
|
79
|
+
else
|
80
|
+
raise "DEFAULT scope cannot be destroyed"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
47
84
|
def as_json(*a)
|
48
85
|
{ 'name' => @name,
|
49
86
|
'updated_at' => @updated_at }
|
@@ -51,9 +88,12 @@ module OpenC3
|
|
51
88
|
|
52
89
|
def deploy(gem_path, variables)
|
53
90
|
seed_database()
|
54
|
-
|
55
91
|
ConfigTopic.initialize_stream(@scope)
|
56
92
|
|
93
|
+
# Create UNKNOWN target for display of unknown data
|
94
|
+
model = TargetModel.new(name: "UNKNOWN", scope: @scope)
|
95
|
+
model.create
|
96
|
+
|
57
97
|
# OpenC3 Log Microservice
|
58
98
|
microservice_name = "#{@scope}__OPENC3__LOG"
|
59
99
|
microservice = MicroserviceModel.new(
|
@@ -24,6 +24,7 @@ require 'openc3/models/microservice_model'
|
|
24
24
|
require 'openc3/topics/limits_event_topic'
|
25
25
|
require 'openc3/topics/config_topic'
|
26
26
|
require 'openc3/system'
|
27
|
+
require 'openc3/utilities/local_mode'
|
27
28
|
require 'openc3/utilities/s3'
|
28
29
|
require 'openc3/utilities/zip'
|
29
30
|
require 'fileutils'
|
@@ -82,13 +83,149 @@ module OpenC3
|
|
82
83
|
super("#{scope}__#{PRIMARY_KEY}")
|
83
84
|
end
|
84
85
|
|
86
|
+
# All targets with indication of modified targets
|
87
|
+
def self.all_modified(scope:)
|
88
|
+
targets = self.all(scope: scope)
|
89
|
+
targets.each { |target_name, target| target['modified'] = false }
|
90
|
+
|
91
|
+
if ENV['OPENC3_LOCAL_MODE']
|
92
|
+
modified_targets = OpenC3::LocalMode.modified_targets(scope: scope)
|
93
|
+
modified_targets.each do |target_name|
|
94
|
+
targets[target_name]['modified'] = true if targets[target_name]
|
95
|
+
end
|
96
|
+
else
|
97
|
+
rubys3_client = Aws::S3::Client.new
|
98
|
+
token = nil
|
99
|
+
while true
|
100
|
+
resp = rubys3_client.list_objects_v2({
|
101
|
+
bucket: 'config',
|
102
|
+
max_keys: 1000,
|
103
|
+
# The trailing slash is important!
|
104
|
+
prefix: "#{scope}/targets_modified/",
|
105
|
+
delimiter: '/',
|
106
|
+
continuation_token: token
|
107
|
+
})
|
108
|
+
resp.common_prefixes.each do |item|
|
109
|
+
# Results look like DEFAULT/targets_modified/INST/
|
110
|
+
# so split on '/' and pull out the last value
|
111
|
+
target_name = item.prefix.split('/')[-1]
|
112
|
+
# A target could have been deleted without removing the modified files
|
113
|
+
# Thus we have to check for the existance of the target_name key
|
114
|
+
if targets.has_key?(target_name)
|
115
|
+
targets[target_name]['modified'] = true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
break unless resp.is_truncated
|
119
|
+
token = resp.next_continuation_token
|
120
|
+
end
|
121
|
+
end
|
122
|
+
# Sort (which turns hash to array) and return hash
|
123
|
+
# This enables a consistent listing of the targets
|
124
|
+
targets.sort.to_h
|
125
|
+
end
|
126
|
+
|
127
|
+
# Given target's modified file list
|
128
|
+
def self.modified_files(target_name, scope:)
|
129
|
+
modified = []
|
130
|
+
|
131
|
+
if ENV['OPENC3_LOCAL_MODE']
|
132
|
+
modified = OpenC3::LocalMode.modified_files(target_name, scope: scope)
|
133
|
+
else
|
134
|
+
rubys3_client = Aws::S3::Client.new
|
135
|
+
token = nil
|
136
|
+
while true
|
137
|
+
resp = rubys3_client.list_objects_v2({
|
138
|
+
bucket: 'config',
|
139
|
+
max_keys: 1000,
|
140
|
+
# The trailing slash is important!
|
141
|
+
prefix: "#{scope}/targets_modified/#{target_name}/",
|
142
|
+
continuation_token: token
|
143
|
+
})
|
144
|
+
resp.contents.each do |item|
|
145
|
+
# Results look like DEFAULT/targets_modified/INST/procedures/new.rb
|
146
|
+
# so split on '/' and ignore the first two values
|
147
|
+
modified << item.key.split('/')[2..-1].join('/')
|
148
|
+
end
|
149
|
+
break unless resp.is_truncated
|
150
|
+
token = resp.next_continuation_token
|
151
|
+
end
|
152
|
+
end
|
153
|
+
# Sort to enable a consistent listing of the modified files
|
154
|
+
modified.sort
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.delete_modified(target_name, scope:)
|
158
|
+
if ENV['OPENC3_LOCAL_MODE']
|
159
|
+
OpenC3::LocalMode.delete_modified(target_name, scope: scope)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Delete the remote files as well
|
163
|
+
rubys3_client = Aws::S3::Client.new
|
164
|
+
token = nil
|
165
|
+
while true
|
166
|
+
resp = rubys3_client.list_objects_v2({
|
167
|
+
bucket: 'config',
|
168
|
+
max_keys: 1000,
|
169
|
+
# The trailing slash is important!
|
170
|
+
prefix: "#{scope}/targets_modified/#{target_name}/",
|
171
|
+
continuation_token: token
|
172
|
+
})
|
173
|
+
resp.contents.each do |item|
|
174
|
+
rubys3_client.delete_object(bucket: 'config', key: item.key)
|
175
|
+
end
|
176
|
+
break unless resp.is_truncated
|
177
|
+
token = resp.next_continuation_token
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.download(target_name, scope:)
|
182
|
+
tmp_dir = Dir.mktmpdir
|
183
|
+
zip_filename = File.join(tmp_dir, "#{target_name}.zip")
|
184
|
+
Zip.continue_on_exists_proc = true
|
185
|
+
zip = Zip::File.open(zip_filename, Zip::File::CREATE)
|
186
|
+
|
187
|
+
if ENV['OPENC3_LOCAL_MODE']
|
188
|
+
OpenC3::LocalMode.zip_target(target_name, zip, scope: scope)
|
189
|
+
else
|
190
|
+
rubys3_client = Aws::S3::Client.new
|
191
|
+
token = nil
|
192
|
+
# The trailing slash is important!
|
193
|
+
prefix = "#{scope}/targets_modified/#{target_name}/"
|
194
|
+
while true
|
195
|
+
resp = rubys3_client.list_objects_v2({
|
196
|
+
bucket: 'config',
|
197
|
+
max_keys: 1000,
|
198
|
+
prefix: prefix,
|
199
|
+
continuation_token: token
|
200
|
+
})
|
201
|
+
resp.contents.each do |item|
|
202
|
+
# item.key looks like DEFAULT/targets_modified/INST/screens/blah.txt
|
203
|
+
base_path = item.key.sub(prefix, '') # remove prefix
|
204
|
+
local_path = File.join(tmp_dir, base_path)
|
205
|
+
# Ensure dir structure exists, get_object fails if not
|
206
|
+
FileUtils.mkdir_p(File.dirname(local_path))
|
207
|
+
rubys3_client.get_object(bucket: 'config', key: item.key, response_target: local_path)
|
208
|
+
zip.add(base_path, local_path)
|
209
|
+
end
|
210
|
+
break unless resp.is_truncated
|
211
|
+
token = resp.next_continuation_token
|
212
|
+
end
|
213
|
+
end
|
214
|
+
zip.close
|
215
|
+
|
216
|
+
result = OpenStruct.new
|
217
|
+
result.filename = File.basename(zip_filename)
|
218
|
+
result.contents = File.read(zip_filename, mode: 'rb')
|
219
|
+
return result
|
220
|
+
end
|
221
|
+
|
85
222
|
# @return [Array] Array of all the packet names
|
86
223
|
def self.packet_names(target_name, type: :TLM, scope:)
|
87
224
|
raise "Unknown type #{type} for #{target_name}" unless VALID_TYPES.include?(type)
|
88
225
|
# If the key doesn't exist or if there are no packets we return empty array
|
89
226
|
Store.hkeys("#{scope}__openc3#{type.to_s.downcase}__#{target_name}").sort
|
90
227
|
end
|
91
|
-
|
228
|
+
|
92
229
|
# @return [Hash] Packet hash or raises an exception
|
93
230
|
def self.packet(target_name, packet_name, type: :TLM, scope:)
|
94
231
|
raise "Unknown type #{type} for #{target_name} #{packet_name}" unless VALID_TYPES.include?(type)
|
@@ -420,9 +557,6 @@ module OpenC3
|
|
420
557
|
end
|
421
558
|
|
422
559
|
def undeploy
|
423
|
-
# Note: The plugin_model undeploy method removes all the microservices first
|
424
|
-
# so we don't need to destroy them here
|
425
|
-
|
426
560
|
rubys3_client = Aws::S3::Client.new
|
427
561
|
prefix = "#{@scope}/targets/#{@name}/"
|
428
562
|
rubys3_client.list_objects(bucket: 'config', prefix: prefix).contents.each do |object|
|
@@ -203,6 +203,9 @@ module OpenC3
|
|
203
203
|
when 'SHOWN'
|
204
204
|
parser.verify_num_parameters(1, 1, "SHOWN <true/false>")
|
205
205
|
@shown = ConfigParser.handle_true_false(parameters[0])
|
206
|
+
when 'POSITION'
|
207
|
+
parser.verify_num_parameters(1, 1, "POSITION <value>")
|
208
|
+
@position = parameters[0].to_i
|
206
209
|
else
|
207
210
|
raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Tool: #{keyword} #{parameters.join(" ")}")
|
208
211
|
end
|
@@ -90,7 +90,7 @@ module OpenC3
|
|
90
90
|
@new_microservices.each do |microservice_name, microservice_config|
|
91
91
|
cmd_array, work_dir, env, scope, container = convert_microservice_to_process_definition(microservice_name, microservice_config)
|
92
92
|
if cmd_array
|
93
|
-
process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container)
|
93
|
+
process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container, config: microservice_config)
|
94
94
|
@new_processes[microservice_name] = process
|
95
95
|
@processes[microservice_name] = process
|
96
96
|
end
|
@@ -108,7 +108,7 @@ module OpenC3
|
|
108
108
|
@changed_processes[microservice_name] = process
|
109
109
|
else # TODO: How is this even possible?
|
110
110
|
Logger.error("Changed microservice #{microservice_name} does not exist. Creating new...", scope: scope)
|
111
|
-
process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container)
|
111
|
+
process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container, config: microservice_config)
|
112
112
|
@new_processes[microservice_name] = process
|
113
113
|
@processes[microservice_name] = process
|
114
114
|
end
|
@@ -35,7 +35,8 @@ module OpenC3
|
|
35
35
|
# Perform any setup steps necessary
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
# container is not used, it's just here for Enterprise
|
39
|
+
def initialize(process_definition, work_dir: '/openc3/lib/openc3/microservices', temp_dir: nil, env: {}, scope:, container: nil, config: nil)
|
39
40
|
@process = nil
|
40
41
|
@process_definition = process_definition
|
41
42
|
@work_dir = work_dir
|
@@ -43,12 +44,26 @@ module OpenC3
|
|
43
44
|
@new_temp_dir = temp_dir
|
44
45
|
@env = env
|
45
46
|
@scope = scope
|
47
|
+
# @config only used in start to help print a better Logger message
|
48
|
+
@config = config
|
46
49
|
end
|
47
50
|
|
48
51
|
def start
|
49
52
|
@temp_dir = @new_temp_dir
|
50
53
|
@new_temp_dir = nil
|
51
|
-
|
54
|
+
|
55
|
+
# In ProcessManager processes, the process_definition is the actual thing run
|
56
|
+
# e.g. OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "load", ...])
|
57
|
+
# However, if the MicroserviceOperator is spawning the proceses it sets
|
58
|
+
# process_definition = ["ruby", "plugin_microservice.rb"]
|
59
|
+
# which then calls exec(*@config["cmd"]) to actually run
|
60
|
+
# So check if the @config['cmd'] is defined to give the user more info in the log
|
61
|
+
cmd = @process_definition.join(' ')
|
62
|
+
if @config && @config['cmd']
|
63
|
+
cmd = @config['cmd'].join(' ')
|
64
|
+
end
|
65
|
+
Logger.info("Starting: #{cmd}", scope: @scope)
|
66
|
+
|
52
67
|
@process = ChildProcess.build(*@process_definition)
|
53
68
|
# This lets the ChildProcess use the parent IO ... but it breaks unit tests
|
54
69
|
# @process.io.inherit!
|
@@ -178,14 +178,40 @@ module OpenC3
|
|
178
178
|
when 'ParameterTypeSet', 'EnumerationList', 'ParameterSet', 'ContainerSet',
|
179
179
|
'EntryList', 'DefaultCalibrator', 'DefaultAlarm', 'RestrictionCriteria',
|
180
180
|
'ComparisonList', 'MetaCommandSet', 'ArgumentTypeSet', 'ArgumentList',
|
181
|
-
'ArgumentAssignmentList', 'LocationInContainerInBits'
|
181
|
+
'ArgumentAssignmentList', 'LocationInContainerInBits', 'ReferenceTime'
|
182
182
|
# Do Nothing
|
183
183
|
|
184
|
+
when 'ErrorDetectCorrect'
|
185
|
+
# TODO: Setup an algorithm to calculate the CRC
|
186
|
+
exponents = []
|
187
|
+
xtce_recurse_element(element) do |crc_element|
|
188
|
+
if crc_element.name == 'CRC'
|
189
|
+
xtce_recurse_element(crc_element) do |poly_element|
|
190
|
+
if poly_element['Polynomial']
|
191
|
+
xtce_recurse_element(poly_element) do |term_element|
|
192
|
+
if term_element['Term']
|
193
|
+
exponents << term_element['exponent'].to_i
|
194
|
+
end
|
195
|
+
true
|
196
|
+
end
|
197
|
+
end
|
198
|
+
true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
true
|
202
|
+
end
|
203
|
+
@current_type.xtce_encoding = 'IntegerDataEncoding'
|
204
|
+
@current_type.signed = 'false'
|
205
|
+
return false # Already recursed
|
206
|
+
|
184
207
|
when 'EnumeratedParameterType', 'EnumeratedArgumentType',
|
185
208
|
'IntegerParameterType', 'IntegerArgumentType',
|
186
209
|
'FloatParameterType', 'FloatArgumentType',
|
187
210
|
'StringParameterType', 'StringArgumentType',
|
188
|
-
'BinaryParameterType', 'BinaryArgumentType'
|
211
|
+
'BinaryParameterType', 'BinaryArgumentType',
|
212
|
+
'BooleanParameterType', 'BooleanArgumentType',
|
213
|
+
'AbsoluteTimeParameterType', 'AbsoluteTimeArgumentType',
|
214
|
+
'RelativeTimeParameterType', 'RelativeTimeArgumentType'
|
189
215
|
@current_type = create_new_type(element)
|
190
216
|
@current_type.endianness = :BIG_ENDIAN
|
191
217
|
|
@@ -204,6 +230,20 @@ module OpenC3
|
|
204
230
|
when 'BinaryParameterType', 'BinaryArgumentType'
|
205
231
|
@current_type.xtce_encoding = 'BinaryDataEncoding'
|
206
232
|
@current_type.sizeInBits = 8 # This is undocumented but appears to be the design
|
233
|
+
when 'BooleanParameterType', 'BooleanArgumentType'
|
234
|
+
@current_type.xtce_encoding = 'StringDataEncoding'
|
235
|
+
@current_type.sizeInBits = 8 # This is undocumented but appears to be the design
|
236
|
+
@current_type.states ||= {}
|
237
|
+
if element.attributes['zeroStringValue'] && element.attributes['oneStringValue']
|
238
|
+
@current_type.states[element.attributes['zeroStringValue'].value] = 0
|
239
|
+
@current_type.states[element.attributes['oneStringValue'].value] = 1
|
240
|
+
else
|
241
|
+
@current_type.states['FALSE'] = 0
|
242
|
+
@current_type.states['TRUE'] = 1
|
243
|
+
end
|
244
|
+
# No defaults for the time types
|
245
|
+
# when 'AbsoluteTimeParameterType', 'AbsoluteTimeArgumentType',
|
246
|
+
# when'RelativeTimeParameterType', 'RelativeTimeArgumentType'
|
207
247
|
end
|
208
248
|
|
209
249
|
when 'ArrayParameterType', 'ArrayArgumentType'
|
@@ -251,7 +291,7 @@ module OpenC3
|
|
251
291
|
|
252
292
|
return false # Already recursed
|
253
293
|
|
254
|
-
when
|
294
|
+
when 'SizeInBits'
|
255
295
|
xtce_recurse_element(element) do |block_element|
|
256
296
|
if block_element.name == 'FixedValue'
|
257
297
|
@current_type.sizeInBits = Integer(block_element.text)
|
@@ -326,6 +366,26 @@ module OpenC3
|
|
326
366
|
@current_type.states ||= {}
|
327
367
|
@current_type.states[element['label']] = Integer(element['value'])
|
328
368
|
|
369
|
+
when 'Encoding'
|
370
|
+
if element.attributes['units']
|
371
|
+
@current_type.units_full = element.attributes['units'].value
|
372
|
+
# Could do a better job mapping units to abbreviations
|
373
|
+
@current_type.units = element.attributes['units'].value[0]
|
374
|
+
end
|
375
|
+
# TODO: Not sure if this is correct
|
376
|
+
# if @current_type.attributes['scale'] || @current_type.attributes['offset']
|
377
|
+
# @current_type.conversion ||= PolynomialConversion.new()
|
378
|
+
# if @current_type.attributes['offset']
|
379
|
+
# @current_type.conversion.coeffs[0] = @current_type.attributes['offset']
|
380
|
+
# end
|
381
|
+
# if @current_type.attributes['scale']
|
382
|
+
# @current_type.conversion.coeffs[1] = @current_type.attributes['scale']
|
383
|
+
# end
|
384
|
+
# end
|
385
|
+
|
386
|
+
when 'Epoch'
|
387
|
+
# TODO: How to handle this ... it affects the conversion applied
|
388
|
+
|
329
389
|
when 'IntegerDataEncoding', 'FloatDataEncoding', 'StringDataEncoding', 'BinaryDataEncoding'
|
330
390
|
@current_type.xtce_encoding = element.name
|
331
391
|
element.attributes.each do |att_name, att|
|
@@ -340,6 +400,11 @@ module OpenC3
|
|
340
400
|
@current_type.endianness = :LITTLE_ENDIAN
|
341
401
|
end
|
342
402
|
|
403
|
+
# Ensure if the encoding is a string we convert state values to strings
|
404
|
+
if @current_type.xtce_encoding == 'StringDataEncoding' && @current_type.states
|
405
|
+
@current_type.states.transform_values!(&:to_s)
|
406
|
+
end
|
407
|
+
|
343
408
|
when 'Parameter'
|
344
409
|
@current_parameter = OpenStruct.new
|
345
410
|
element.attributes.each do |att_name, att|
|
@@ -34,7 +34,7 @@ module OpenC3
|
|
34
34
|
OpenC3::Logger.info "Deleting #{delete_path}"
|
35
35
|
response = $api_server.request('delete', endpoint, query: {bucket: 'config'}, scope: scope)
|
36
36
|
if response.nil? || response.code != 200
|
37
|
-
raise "Failed to delete #{delete_path}
|
37
|
+
raise "Failed to delete #{delete_path}"
|
38
38
|
end
|
39
39
|
rescue => error
|
40
40
|
raise "Failed deleting #{path} due to #{error.message}"
|
@@ -48,7 +48,13 @@ module OpenC3
|
|
48
48
|
# @param io_or_string [Io or String] IO object
|
49
49
|
def put_target_file(path, io_or_string, scope: $openc3_scope)
|
50
50
|
raise "Disallowed path modifier '..' found in #{path}" if path.include?('..')
|
51
|
+
|
51
52
|
upload_path = "#{scope}/targets_modified/#{path}"
|
53
|
+
|
54
|
+
if ENV['OPENC3_LOCAL_MODE'] and $openc3_in_cluster
|
55
|
+
OpenC3::LocalMode.put_target_file(upload_path, io_or_string, scope: scope)
|
56
|
+
end
|
57
|
+
|
52
58
|
endpoint = "/openc3-api/storage/upload/#{upload_path}"
|
53
59
|
OpenC3::Logger.info "Writing #{upload_path}"
|
54
60
|
result = _get_presigned_request(endpoint, scope: scope)
|
@@ -86,6 +92,17 @@ module OpenC3
|
|
86
92
|
# Loop to allow redo when switching from modified to original
|
87
93
|
loop do
|
88
94
|
begin
|
95
|
+
if part == "targets_modified" and ENV['OPENC3_LOCAL_MODE']
|
96
|
+
local_file = OpenC3::LocalMode.open_local_file(path, scope: scope)
|
97
|
+
if local_file
|
98
|
+
file = Tempfile.new('target', binmode: true)
|
99
|
+
file.write(local_file.read)
|
100
|
+
local_file.close
|
101
|
+
file.rewind
|
102
|
+
return file if local_file
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
89
106
|
return _get_storage_file("#{part}/#{path}", scope: scope)
|
90
107
|
rescue => error
|
91
108
|
if part == "targets_modified"
|