openc3 5.0.8 → 5.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- rubys3_client = Aws::S3::Client.new
91
- token = nil
92
- while true
93
- resp = rubys3_client.list_objects_v2({
94
- bucket: 'config',
95
- max_keys: 1000,
96
- # The trailing slash is important!
97
- prefix: "#{scope}/targets_modified/",
98
- delimiter: '/',
99
- continuation_token: token
100
- })
101
- resp.common_prefixes.each do |item|
102
- # Results look like DEFAULT/targets_modified/INST/
103
- # so split on '/' and pull out the last value
104
- target_name = item.prefix.split('/')[-1]
105
- # A target could have been deleted without removing the modified files
106
- # Thus we have to check for the existance of the target_name key
107
- if targets.has_key?(target_name)
108
- targets[target_name]['modified'] = true
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(name, scope:)
128
+ def self.modified_files(target_name, scope:)
121
129
  modified = []
122
- rubys3_client = Aws::S3::Client.new
123
- token = nil
124
- while true
125
- resp = rubys3_client.list_objects_v2({
126
- bucket: 'config',
127
- max_keys: 1000,
128
- # The trailing slash is important!
129
- prefix: "#{scope}/targets_modified/#{name}/",
130
- continuation_token: token
131
- })
132
- resp.contents.each do |item|
133
- # Results look like DEFAULT/targets_modified/INST/procedures/new.rb
134
- # so split on '/' and ignore the first two values
135
- modified << item.key.split('/')[2..-1].join('/')
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(name, scope:)
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/#{name}/",
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(name, scope:)
181
+ def self.download(target_name, scope:)
169
182
  tmp_dir = Dir.mktmpdir
170
- zip_filename = File.join(tmp_dir, "#{name}.zip")
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
- rubys3_client = Aws::S3::Client.new
175
- token = nil
176
- # The trailing slash is important!
177
- prefix = "#{scope}/targets_modified/#{name}/"
178
- while true
179
- resp = rubys3_client.list_objects_v2({
180
- bucket: 'config',
181
- max_keys: 1000,
182
- prefix: prefix,
183
- continuation_token: token
184
- })
185
- resp.contents.each do |item|
186
- # item.key looks like DEFAULT/targets_modified/INST/screens/blah.txt
187
- base_path = item.key.sub(prefix, '') # remove prefix
188
- local_path = File.join(tmp_dir, base_path)
189
- # Ensure dir structure exists, get_object fails if not
190
- FileUtils.mkdir_p(File.dirname(local_path))
191
- rubys3_client.get_object(bucket: 'config', key: item.key, response_target: local_path)
192
- zip.add(base_path, local_path)
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 "SizeInBits"
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}. Note: #{scope}/targets is read-only."
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"