openc3 5.0.8 → 5.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/openc3cli +128 -95
- data/data/config/widgets.yaml +49 -73
- 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/interface_model.rb +41 -0
- data/lib/openc3/models/microservice_model.rb +7 -1
- data/lib/openc3/models/plugin_model.rb +1 -1
- data/lib/openc3/models/target_model.rb +85 -68
- 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/target_file.rb +146 -0
- data/lib/openc3/version.rb +5 -5
- metadata +9 -7
@@ -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'
|
@@ -87,29 +88,36 @@ module OpenC3
|
|
87
88
|
targets = self.all(scope: scope)
|
88
89
|
targets.each { |target_name, target| target['modified'] = false }
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
109
117
|
end
|
118
|
+
break unless resp.is_truncated
|
119
|
+
token = resp.next_continuation_token
|
110
120
|
end
|
111
|
-
break unless resp.is_truncated
|
112
|
-
token = resp.next_continuation_token
|
113
121
|
end
|
114
122
|
# Sort (which turns hash to array) and return hash
|
115
123
|
# This enables a consistent listing of the targets
|
@@ -117,31 +125,41 @@ module OpenC3
|
|
117
125
|
end
|
118
126
|
|
119
127
|
# Given target's modified file list
|
120
|
-
def self.modified_files(
|
128
|
+
def self.modified_files(target_name, scope:)
|
121
129
|
modified = []
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
136
151
|
end
|
137
|
-
break unless resp.is_truncated
|
138
|
-
token = resp.next_continuation_token
|
139
152
|
end
|
140
153
|
# Sort to enable a consistent listing of the modified files
|
141
154
|
modified.sort
|
142
155
|
end
|
143
156
|
|
144
|
-
def self.delete_modified(
|
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
|
145
163
|
rubys3_client = Aws::S3::Client.new
|
146
164
|
token = nil
|
147
165
|
while true
|
@@ -149,7 +167,7 @@ module OpenC3
|
|
149
167
|
bucket: 'config',
|
150
168
|
max_keys: 1000,
|
151
169
|
# The trailing slash is important!
|
152
|
-
prefix: "#{scope}/targets_modified/#{
|
170
|
+
prefix: "#{scope}/targets_modified/#{target_name}/",
|
153
171
|
continuation_token: token
|
154
172
|
})
|
155
173
|
resp.contents.each do |item|
|
@@ -158,41 +176,40 @@ module OpenC3
|
|
158
176
|
break unless resp.is_truncated
|
159
177
|
token = resp.next_continuation_token
|
160
178
|
end
|
161
|
-
# rubys3_client = Aws::S3::Client.new
|
162
|
-
# prefix = "#{scope}/targets_modified/#{name}/"
|
163
|
-
# rubys3_client.list_objects(bucket: 'config', prefix: prefix).contents.each do |object|
|
164
|
-
# rubys3_client.delete_object(bucket: 'config', key: object.key)
|
165
|
-
# end
|
166
179
|
end
|
167
180
|
|
168
|
-
def self.download(
|
181
|
+
def self.download(target_name, scope:)
|
169
182
|
tmp_dir = Dir.mktmpdir
|
170
|
-
zip_filename = File.join(tmp_dir, "#{
|
183
|
+
zip_filename = File.join(tmp_dir, "#{target_name}.zip")
|
171
184
|
Zip.continue_on_exists_proc = true
|
172
185
|
zip = Zip::File.open(zip_filename, Zip::File::CREATE)
|
173
186
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
193
212
|
end
|
194
|
-
break unless resp.is_truncated
|
195
|
-
token = resp.next_continuation_token
|
196
213
|
end
|
197
214
|
zip.close
|
198
215
|
|
@@ -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"
|