cosmos 5.0.2 → 5.0.3

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/data/config/microservice.yaml +47 -35
  3. data/data/config/plugins.yaml +3 -150
  4. data/data/config/target.yaml +70 -0
  5. data/data/config/tool.yaml +37 -31
  6. data/lib/cosmos/api/cmd_api.rb +11 -0
  7. data/lib/cosmos/api/tlm_api.rb +56 -32
  8. data/lib/cosmos/config/config_parser.rb +17 -20
  9. data/lib/cosmos/conversions/generic_conversion.rb +2 -2
  10. data/lib/cosmos/conversions/polynomial_conversion.rb +5 -8
  11. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +26 -9
  12. data/lib/cosmos/io/json_drb.rb +5 -1
  13. data/lib/cosmos/microservices/cleanup_microservice.rb +28 -29
  14. data/lib/cosmos/microservices/microservice.rb +1 -1
  15. data/lib/cosmos/models/gem_model.rb +1 -1
  16. data/lib/cosmos/models/scope_model.rb +0 -20
  17. data/lib/cosmos/models/target_model.rb +110 -3
  18. data/lib/cosmos/packets/packet.rb +23 -0
  19. data/lib/cosmos/packets/packet_config.rb +2 -2
  20. data/lib/cosmos/packets/packet_item.rb +57 -0
  21. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  22. data/lib/cosmos/packets/parsers/packet_item_parser.rb +1 -1
  23. data/lib/cosmos/packets/parsers/packet_parser.rb +1 -1
  24. data/lib/cosmos/packets/parsers/xtce_parser.rb +1 -1
  25. data/lib/cosmos/packets/structure_item.rb +10 -1
  26. data/lib/cosmos/script/api_shared.rb +30 -25
  27. data/lib/cosmos/script/commands.rb +5 -7
  28. data/lib/cosmos/script/script.rb +19 -39
  29. data/lib/cosmos/script/storage.rb +92 -105
  30. data/lib/cosmos/tools/table_manager/table_item.rb +1 -1
  31. data/lib/cosmos/topics/command_decom_topic.rb +4 -0
  32. data/lib/cosmos/topics/telemetry_decom_topic.rb +4 -0
  33. data/lib/cosmos/topics/topic.rb +10 -0
  34. data/lib/cosmos/utilities/logger.rb +1 -0
  35. data/lib/cosmos/utilities/s3.rb +61 -0
  36. data/lib/cosmos/utilities/store_autoload.rb +0 -10
  37. data/lib/cosmos/version.rb +5 -4
  38. data/templates/plugin-template/plugin.gemspec +0 -2
  39. metadata +3 -3
@@ -486,6 +486,63 @@ module Cosmos
486
486
  config
487
487
  end
488
488
 
489
+ def self.from_json(hash)
490
+ # Convert strings to symbols
491
+ endianness = hash['endianness'] ? hash['endianness'].intern : nil
492
+ data_type = hash['data_type'] ? hash['data_type'].intern : nil
493
+ overflow = hash['overflow'] ? hash['overflow'].intern : nil
494
+ item = PacketItem.new(hash['name'], hash['bit_offset'], hash['bit_size'],
495
+ data_type, endianness, hash['array_size'], overflow)
496
+ item.description = hash['description']
497
+ item.id_value = hash['id_value']
498
+ item.default = hash['default']
499
+ item.range = (hash['minimum']..hash['maximum']) if hash['minimum'] && hash['maximum']
500
+ item.required = hash['required']
501
+ item.format_string = hash['format_string']
502
+ item.units = hash['units']
503
+ item.units_full = hash['units_full']
504
+ if hash['states']
505
+ item.states = {}
506
+ item.hazardous = {}
507
+ item.state_colors = {}
508
+ hash['states'].each do |state_name, state|
509
+ item.states[state_name] = state['value']
510
+ item.hazardous[state_name] = state['hazardous']
511
+ item.state_colors[state_name] = state['color'].to_sym if state['color']
512
+ end
513
+ end
514
+ # Recreate COSMOS built-in conversions
515
+ if hash['read_conversion']
516
+ begin
517
+ item.read_conversion = Cosmos::const_get(hash['read_conversion']['class']).new(*hash['read_conversion']['params'])
518
+ rescue => error
519
+ Logger.instance.error "#{item.name} read_conversion of #{hash['read_conversion']} could not be instantiated due to #{error}"
520
+ end
521
+ end
522
+ if hash['write_conversion']
523
+ begin
524
+ item.write_conversion = Cosmos::const_get(hash['write_conversion']['class']).new(*hash['write_conversion']['params'])
525
+ rescue => error
526
+ Logger.instance.error "#{item.name} write_conversion of #{hash['write_conversion']} could not be instantiated due to #{error}"
527
+ end
528
+ end
529
+
530
+ if hash['limits']
531
+ item.limits = PacketItemLimits.new
532
+ # Delete these keys so the only ones left are limits sets
533
+ item.limits.persistence_setting = hash['limits'].delete('persistence_setting')
534
+ item.limits.enabled = true if hash['limits'].delete('enabled')
535
+ values = {}
536
+ hash['limits'].each do |set, items|
537
+ values[set.to_sym] = [items['red_low'], items['yellow_low'], items['yellow_high'], items['red_high']]
538
+ values[set.to_sym].concat([items['green_low'], items['green_high']]) if items['green_low'] && items['green_high']
539
+ end
540
+ item.limits.values = values
541
+ end
542
+ item.meta = hash['meta']
543
+ item
544
+ end
545
+
489
546
  protected
490
547
 
491
548
  def parameter_config
@@ -132,7 +132,7 @@ module Cosmos
132
132
  end
133
133
  alias dup clone
134
134
 
135
- def to_hash
135
+ def as_json
136
136
  hash = {}
137
137
  hash['values'] = self.values
138
138
  hash['enabled'] = self.enabled
@@ -146,5 +146,17 @@ module Cosmos
146
146
  hash['persistence_count'] = self.persistence_count
147
147
  hash
148
148
  end
149
+
150
+ def self.from_json(hash)
151
+ limits = PacketItemLimits.new
152
+ limits.values = hash['values'].transform_keys(&:to_sym) if hash['values']
153
+ limits.enabled = hash['enabled']
154
+ limits.state = hash['state'] ? hash['state'].to_sym : nil
155
+ # Can't recreate a LimitsResponse class
156
+ # limits.response = hash['response']
157
+ limits.persistence_setting = hash['persistence_setting'] if hash['persistence_setting']
158
+ limits.persistence_count = hash['persistence_count'] if hash['persistence_count']
159
+ limits
160
+ end
149
161
  end
150
- end # module Cosmos
162
+ end
@@ -56,7 +56,7 @@ module Cosmos
56
56
  max_options = @usage.count("<")
57
57
  # The last two options (description and endianness) are optional
58
58
  @parser.verify_num_parameters(max_options - 2, max_options, @usage)
59
- @parser.verify_parameters_underscores(1) # Item name is the 1st parameter
59
+ @parser.verify_parameter_naming(1) # Item name is the 1st parameter
60
60
  end
61
61
 
62
62
  def create_packet_item(packet, cmd_or_tlm)
@@ -72,7 +72,7 @@ module Cosmos
72
72
  def verify_parameters
73
73
  @usage = "#{@parser.keyword} <TARGET NAME> <PACKET NAME> <ENDIANNESS: BIG_ENDIAN/LITTLE_ENDIAN> <DESCRIPTION (Optional)>"
74
74
  @parser.verify_num_parameters(3, 4, @usage)
75
- @parser.verify_parameters_underscores(2) # Packet name is the 2nd parameter
75
+ @parser.verify_parameter_naming(2) # Packet name is the 2nd parameter
76
76
  end
77
77
 
78
78
  def create_command(target_name, commands, warnings)
@@ -293,7 +293,7 @@ module Cosmos
293
293
  xtce_recurse_element(element) do |block_element|
294
294
  if block_element.name == 'Term'
295
295
  exponent = Float(block_element['exponent']).to_i
296
- @current_type.conversion ||= PolynomialConversion.new([])
296
+ @current_type.conversion ||= PolynomialConversion.new()
297
297
  @current_type.conversion.coeffs[exponent] = Float(block_element['coefficient'])
298
298
  @current_type.conversion.coeffs.each_with_index do |value, index|
299
299
  @current_type.conversion.coeffs[index] = 0.0 if value.nil?
@@ -292,7 +292,16 @@ module Cosmos
292
292
  end
293
293
  alias dup clone
294
294
 
295
- def to_hash
295
+ def self.from_json(hash)
296
+ # Convert strings to symbols
297
+ endianness = hash['endianness'] ? hash['endianness'].intern : nil
298
+ data_type = hash['data_type'] ? hash['data_type'].intern : nil
299
+ overflow = hash['overflow'] ? hash['overflow'].intern : nil
300
+ StructureItem.new(hash['name'], hash['bit_offset'], hash['bit_size'], data_type,
301
+ endianness, hash['array_size'], overflow)
302
+ end
303
+
304
+ def as_json
296
305
  hash = {}
297
306
  hash['name'] = self.name
298
307
  hash['bit_offset'] = self.bit_offset
@@ -300,10 +300,10 @@ module Cosmos
300
300
  #
301
301
  # @param args [String|Array<String>] See the description for calling style
302
302
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
303
- def wait_check(*args, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token)
303
+ def wait_check(*args, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token, &block)
304
304
  target_name, packet_name, item_name, comparison_to_eval, timeout, polling_rate = _wait_check_process_args(args, scope: scope, token: token)
305
305
  start_time = Time.now.sys
306
- success, value = cosmos_script_wait_implementation(target_name, packet_name, item_name, type, comparison_to_eval, timeout, polling_rate, scope: scope, token: token)
306
+ success, value = cosmos_script_wait_implementation(target_name, packet_name, item_name, type, comparison_to_eval, timeout, polling_rate, scope: scope, token: token, &block)
307
307
  time = Time.now.sys - start_time
308
308
  check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}"
309
309
  with_value_str = "with value == #{value} after waiting #{time} seconds"
@@ -321,8 +321,8 @@ module Cosmos
321
321
  end
322
322
 
323
323
  # @deprecated use wait_check with type: :RAW
324
- def wait_check_raw(*args, scope: $cosmos_scope, token: $cosmos_token)
325
- wait_check(*args, type: :RAW, scope: scope, token: token)
324
+ def wait_check_raw(*args, scope: $cosmos_scope, token: $cosmos_token, &block)
325
+ wait_check(*args, type: :RAW, scope: scope, token: token, &block)
326
326
  end
327
327
 
328
328
  # Wait for the value of a telmetry item to be within a tolerance of a value
@@ -334,7 +334,7 @@ module Cosmos
334
334
  #
335
335
  # @param args [String|Array<String>] See the description for calling style
336
336
  # @param type [Symbol] Telemetry type, :RAW or :CONVERTED (default)
337
- def wait_check_tolerance(*args, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token)
337
+ def wait_check_tolerance(*args, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token, &block)
338
338
  raise "Invalid type '#{type}' for wait_check_tolerance" unless %i(RAW CONVERTED).include?(type)
339
339
 
340
340
  target_name, packet_name, item_name, expected_value, tolerance, timeout, polling_rate = _wait_tolerance_process_args(args, scope: scope, token: token)
@@ -343,7 +343,7 @@ module Cosmos
343
343
  if value.is_a?(Array)
344
344
  expected_value, tolerance = array_tolerance_process_args(value.size, expected_value, tolerance, 'wait_check_tolerance', scope: scope, token: token)
345
345
 
346
- success, value = cosmos_script_wait_implementation_array_tolerance(value.size, target_name, packet_name, item_name, type, expected_value, tolerance, timeout, polling_rate, scope: scope, token: token)
346
+ success, value = cosmos_script_wait_implementation_array_tolerance(value.size, target_name, packet_name, item_name, type, expected_value, tolerance, timeout, polling_rate, scope: scope, token: token, &block)
347
347
  time = Time.now.sys - start_time
348
348
 
349
349
  message = ""
@@ -388,8 +388,8 @@ module Cosmos
388
388
  end
389
389
 
390
390
  # @deprecated Use wait_check_tolerance with type: :RAW
391
- def wait_check_tolerance_raw(*args, scope: $cosmos_scope, token: $cosmos_token)
392
- wait_check_tolerance(*args, type: :RAW, scope: scope, token: token)
391
+ def wait_check_tolerance_raw(*args, scope: $cosmos_scope, token: $cosmos_token, &block)
392
+ wait_check_tolerance(*args, type: :RAW, scope: scope, token: token, &block)
393
393
  end
394
394
 
395
395
  # Wait on an expression to be true. On a timeout, the script will pause.
@@ -397,12 +397,12 @@ module Cosmos
397
397
  timeout,
398
398
  polling_rate = DEFAULT_TLM_POLLING_RATE,
399
399
  context = nil,
400
- scope: $cosmos_scope, token: $cosmos_token)
400
+ scope: $cosmos_scope, token: $cosmos_token, &block)
401
401
  start_time = Time.now.sys
402
402
  success = cosmos_script_wait_implementation_expression(exp_to_eval,
403
403
  timeout,
404
404
  polling_rate,
405
- context, scope: scope, token: token)
405
+ context, scope: scope, token: token, &block)
406
406
  time = Time.now.sys - start_time
407
407
  if success
408
408
  Logger.info "CHECK: #{exp_to_eval} is TRUE after waiting #{time} seconds"
@@ -691,15 +691,20 @@ module Cosmos
691
691
  return [target_name, packet_name, item_name, comparison_to_eval, timeout, polling_rate]
692
692
  end
693
693
 
694
- def _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, scope: $cosmos_scope, token: $cosmos_token)
694
+ def _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: $cosmos_scope, token: $cosmos_token, &block)
695
695
  end_time = Time.now.sys + timeout
696
- exp_to_eval = yield
697
696
 
698
697
  while true
699
698
  work_start = Time.now.sys
700
699
  value = tlm(target_name, packet_name, item_name, type: value_type, scope: scope, token: token)
701
- if eval(exp_to_eval)
702
- return true, value
700
+ if not block.nil?
701
+ if block.call(value)
702
+ return true, value
703
+ end
704
+ else
705
+ if eval(exp_to_eval)
706
+ return true, value
707
+ end
703
708
  end
704
709
  break if Time.now.sys >= end_time
705
710
 
@@ -724,25 +729,25 @@ module Cosmos
724
729
  end
725
730
 
726
731
  # Wait for a converted telemetry item to pass a comparison
727
- def cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, scope: $cosmos_scope, token: $cosmos_token)
728
- _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, scope: scope, token: token) do
729
- "value " + comparison_to_eval
732
+ def cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, scope: $cosmos_scope, token: $cosmos_token, &block)
733
+ if comparison_to_eval
734
+ exp_to_eval = "value " + comparison_to_eval
735
+ else
736
+ exp_to_eval = nil
730
737
  end
738
+ _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: scope, token: token, &block)
731
739
  end
732
740
 
733
- def cosmos_script_wait_implementation_tolerance(target_name, packet_name, item_name, value_type, expected_value, tolerance, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, scope: $cosmos_scope, token: $cosmos_token)
734
- _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, scope: scope, token: token) do
735
- "((#{expected_value} - #{tolerance})..(#{expected_value} + #{tolerance})).include? value"
736
- end
741
+ def cosmos_script_wait_implementation_tolerance(target_name, packet_name, item_name, value_type, expected_value, tolerance, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, scope: $cosmos_scope, token: $cosmos_token, &block)
742
+ exp_to_eval = "((#{expected_value} - #{tolerance})..(#{expected_value} + #{tolerance})).include? value"
743
+ _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: scope, token: token, &block)
737
744
  end
738
745
 
739
- def cosmos_script_wait_implementation_array_tolerance(array_size, target_name, packet_name, item_name, value_type, expected_value, tolerance, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, scope: $cosmos_scope, token: $cosmos_token)
746
+ def cosmos_script_wait_implementation_array_tolerance(array_size, target_name, packet_name, item_name, value_type, expected_value, tolerance, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, scope: $cosmos_scope, token: $cosmos_token, &block)
740
747
  statements = []
741
748
  array_size.times { |i| statements << "(((#{expected_value[i]} - #{tolerance[i]})..(#{expected_value[i]} + #{tolerance[i]})).include? value[#{i}])" }
742
749
  exp_to_eval = statements.join(" && ")
743
- _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, scope: scope, token: token) do
744
- exp_to_eval
745
- end
750
+ _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: scope, token: token, &block)
746
751
  end
747
752
 
748
753
  # Wait on an expression to be true.
@@ -112,13 +112,11 @@ module Cosmos
112
112
  target_name, cmd_name, cmd_params = $api_server.method_missing(cmd, *args)
113
113
  _log_cmd(target_name, cmd_name, cmd_params, raw, no_range, no_hazardous)
114
114
  rescue HazardousError => e
115
- ok_to_proceed = prompt_for_hazardous(e.target_name, e.cmd_name, e.hazardous_description)
116
- if ok_to_proceed
117
- target_name, cmd_name, cmd_params = $api_server.method_missing(cmd_no_hazardous, *args)
118
- _log_cmd(target_name, cmd_name, cmd_params, raw, no_range, no_hazardous)
119
- else
120
- retry unless prompt_for_script_abort()
121
- end
115
+ # This opens a prompt at which point they can cancel and stop the script
116
+ # or say Yes and send the command. Thus we don't care about the return value.
117
+ prompt_for_hazardous(e.target_name, e.cmd_name, e.hazardous_description)
118
+ target_name, cmd_name, cmd_params = $api_server.method_missing(cmd_no_hazardous, *args)
119
+ _log_cmd(target_name, cmd_name, cmd_params, raw, no_range, no_hazardous)
122
120
  end
123
121
  end
124
122
  end
@@ -129,58 +129,38 @@ module Cosmos
129
129
  message_box(string, *items, **options)
130
130
  end
131
131
 
132
- def _file_dialog(message, directory, filter, select_files = true)
132
+ def _file_dialog(title, message, filter:)
133
133
  answer = ''
134
- files = Dir["#{directory}/#{filter}"]
135
- if select_files
136
- files.select! { |f| !File.directory? f }
137
- else
138
- files.select! { |f| File.directory? f }
139
- end
134
+ path = "./*"
135
+ path += filter if filter
136
+ files = Dir[path]
137
+ files.select! { |f| !File.directory? f }
140
138
  while answer.empty?
141
- print message + "\n" + files.join("\n") + "\n<Type file name>:"
139
+ print "#{title}\n#{message}\n#{files.join("\n")}\n<Type file name>:"
142
140
  answer = gets
143
141
  answer.chomp!
144
142
  end
145
143
  return answer
146
144
  end
147
145
 
148
- def save_file_dialog(directory, message = "Save File", filter = "*")
149
- _file_dialog(message, directory, filter)
150
- end
151
-
152
- def open_file_dialog(directory, message = "Open File", filter = "*")
153
- _file_dialog(message, directory, filter)
154
- end
155
-
156
- def open_files_dialog(directory, message = "Open File(s)", filter = "*")
157
- _file_dialog(message, directory, filter)
146
+ def open_file_dialog(title, message = "Open File", filter:)
147
+ _file_dialog(title, message, filter)
158
148
  end
159
149
 
160
- def open_directory_dialog(directory, message = "Open Directory")
161
- _file_dialog(message, directory, "*", false)
150
+ def open_files_dialog(title, message = "Open File(s)", filter:)
151
+ _file_dialog(title, message, filter)
162
152
  end
163
153
 
164
154
  def prompt_for_hazardous(target_name, cmd_name, hazardous_description)
165
- message = "Warning: Command #{target_name} #{cmd_name} is Hazardous. "
166
- message << "\n#{hazardous_description}\n" if hazardous_description
167
- message << "Send? (y,n): "
168
- print message
169
- answer = gets.chomp
170
- if answer.downcase == 'y'
171
- return true
172
- else
173
- return false
174
- end
175
- end
176
-
177
- def prompt_for_script_abort
178
- print "Stop running script? (y,n): "
179
- answer = gets.chomp
180
- if answer.downcase == 'y'
181
- exit
182
- else
183
- return false # Not aborted - Retry
155
+ loop do
156
+ message = "Warning: Command #{target_name} #{cmd_name} is Hazardous. "
157
+ message << "\n#{hazardous_description}\n" if hazardous_description
158
+ message << "Send? (y): "
159
+ print message
160
+ answer = gets.chomp
161
+ if answer.downcase == 'y'
162
+ return true
163
+ end
184
164
  end
185
165
  end
186
166
 
@@ -23,137 +23,124 @@ module Cosmos
23
23
  module Script
24
24
  private
25
25
 
26
- # Get a handle to access a target file
26
+ # Delete a file on a target
27
+ #
28
+ # @param [String] Path to a file in a target directory
29
+ def delete_target_file(path, scope: $cosmos_scope)
30
+ begin
31
+ # Only delete from the targets_modified
32
+ delete_path = "#{scope}/targets_modified/#{path}"
33
+ endpoint = "/cosmos-api/storage/delete/#{delete_path}"
34
+ Cosmos::Logger.info "Deleting #{delete_path}"
35
+ response = $api_server.request('delete', endpoint, query: {bucket: 'config'})
36
+ if response.nil? || response.code != 200
37
+ raise "Failed to delete #{delete_path}. Note: #{scope}/targets is read-only."
38
+ end
39
+ rescue => error
40
+ raise "Failed deleting #{path} due to #{error.message}"
41
+ end
42
+ nil
43
+ end
44
+
45
+ # Get a handle to write a target file
27
46
  #
28
47
  # @param path [String] Path to a file in a target directory
29
- # @param original [Boolean] Whether to get the original or modified file
30
- # @return [File|nil]
31
- def get_target_file(path, original: false, scope: $cosmos_scope)
32
- # Create Tempfile to store data
33
- file = Tempfile.new('target', binmode: true)
48
+ # @param io_or_string [Io or String] IO object
49
+ def put_target_file(path, io_or_string, scope: $cosmos_scope)
50
+ raise "Disallowed path modifier '..' found in #{path}" if path.include?('..')
51
+ upload_path = "#{scope}/targets_modified/#{path}"
52
+ endpoint = "/cosmos-api/storage/upload/#{upload_path}"
53
+ Cosmos::Logger.info "Writing #{upload_path}"
54
+ result = _get_presigned_request(endpoint)
34
55
 
35
- # Get presigned url
36
- if original
37
- part = "targets"
38
- else
39
- part = "targets_modified"
40
- end
41
- # Loop to allow redo
42
- loop do
43
- endpoint = "/cosmos-api/storage/download/#{scope}/#{part}/#{path}"
44
- Cosmos::Logger.info "Reading #{scope}/#{part}/#{path}"
45
- if $cosmos_in_cluster
46
- response = $api_server.request('get', endpoint, query: {bucket: 'config', internal: true})
47
- else
48
- response = $api_server.request('get', endpoint, query: {bucket: 'config'})
49
- end
50
- if response.nil? || response.code != 201
51
- Cosmos::Logger.error "Failed Get Presigned URL for #{scope}/#{part}/#{path}"
52
- if part == "targets_modified"
53
- part = "targets"
54
- redo
56
+ # Try to put the file
57
+ success = false
58
+ begin
59
+ uri = _get_uri(result['url'])
60
+ Net::HTTP.start(uri.host, uri.port) do |http|
61
+ request = Net::HTTP::Put.new(uri, {'Content-Length' => io_or_string.length.to_s})
62
+ if String === io_or_string
63
+ request.body = io_or_string
55
64
  else
56
- raise "#{path} not found"
65
+ request.body_stream = io_or_string
66
+ end
67
+ result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
68
+ http.request(request)
57
69
  end
70
+ return result
58
71
  end
59
- result = JSON.parse(response.body)
72
+ rescue => error
73
+ raise "Failed to write #{upload_path}"
74
+ end
75
+ nil
76
+ end
60
77
 
61
- # Try to get the file
78
+ # Get a handle to access a target file
79
+ #
80
+ # @param path [String] Path to a file in a target directory, e.g. "INST/procedures/test.rb"
81
+ # @param original [Boolean] Whether to get the original or modified file
82
+ # @return [File|nil]
83
+ def get_target_file(path, original: false, scope: $cosmos_scope)
84
+ part = "targets"
85
+ part += "_modified" unless original
86
+ # Loop to allow redo when switching from modified to original
87
+ loop do
62
88
  begin
63
- if $cosmos_in_cluster
64
- uri = URI.parse("http://cosmos-minio:9000" + result['url'])
65
- else
66
- uri = URI.parse($api_server.generate_url + result['url'])
67
- end
68
- Net::HTTP.start(uri.host, uri.port) do |http|
69
- request = Net::HTTP::Get.new uri
70
-
71
- http.request request do |response|
72
- response.read_body do |chunk|
73
- puts chunk.length
74
- file.write chunk
75
- end
76
- end
77
- file.rewind
78
- end
79
- return file
89
+ return _get_storage_file("#{part}/#{path}", scope: scope)
80
90
  rescue => error
81
- Cosmos::Logger.info("#{scope}/#{part}/#{path} not found")
82
91
  if part == "targets_modified"
83
92
  part = "targets"
84
93
  redo
85
94
  else
86
- raise "#{path} not found"
95
+ raise error
87
96
  end
88
97
  end
89
98
  break
90
99
  end
91
100
  end
92
101
 
93
- # Get a handle to write a target file
94
- #
95
- # @param path [String] Path to a file in a target directory
96
- # @param io_or_string [Io or String] IO object
97
- def put_target_file(path, io_or_string, scope: $cosmos_scope)
98
- # Get presigned url
99
- part = "targets_modified"
100
- begin
101
- endpoint = "/cosmos-api/storage/upload/#{scope}/#{part}/#{path}"
102
- Cosmos::Logger.info "Writing #{scope}/#{part}/#{path}"
103
- if $cosmos_in_cluster
104
- response = $api_server.request('get', endpoint, query: {bucket: 'config', internal: true})
105
- else
106
- response = $api_server.request('get', endpoint, query: {bucket: 'config'})
107
- end
108
- if response.nil? || response.code != 201
109
- Cosmos::Logger.error "Failed Get Presigned URL for #{scope}/#{part}/#{path}"
110
- return nil
111
- end
112
- result = JSON.parse(response.body)
102
+ # These are helper methods ... should not be used directly
113
103
 
114
- # Try to put the file
115
- success = false
116
- begin
117
- if $cosmos_in_cluster
118
- uri = URI.parse("http://cosmos-minio:9000" + result['url'])
119
- else
120
- uri = URI.parse($api_server.generate_url + result['url'])
121
- end
122
- Net::HTTP.start(uri.host, uri.port) do |http|
123
- request = Net::HTTP::Put.new(uri, {'Content-Length' => io_or_string.length.to_s})
124
- if String === io_or_string
125
- request.body = io_or_string
126
- else
127
- request.body_stream = io_or_string
128
- end
129
- result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
130
- http.request(request)
131
- end
132
- return result
104
+ def _get_storage_file(path, scope: $cosmos_scope)
105
+ # Create Tempfile to store data
106
+ file = Tempfile.new('target', binmode: true)
107
+
108
+ endpoint = "/cosmos-api/storage/download/#{scope}/#{path}"
109
+ Cosmos::Logger.info "Reading #{scope}/#{path}"
110
+ result = _get_presigned_request(endpoint)
111
+
112
+ # Try to get the file
113
+ uri = _get_uri(result['url'])
114
+ Net::HTTP.start(uri.host, uri.port) do |http|
115
+ request = Net::HTTP::Get.new uri
116
+ http.request request do |response|
117
+ response.read_body do |chunk|
118
+ file.write chunk
133
119
  end
134
- rescue => error
135
- raise "Failed to write #{scope}/#{part}/#{path}"
136
120
  end
121
+ file.rewind
137
122
  end
138
- nil
123
+ return file
139
124
  end
140
125
 
141
- # Delete a file on a target
142
- #
143
- # @param [String] Path to a file in a target directory
144
- def delete_target_file(path, scope: $cosmos_scope)
145
- begin
146
- # Only delete from the targets_modified
147
- endpoint = "/cosmos-api/storage/delete/#{scope}/targets_modified/#{path}"
148
- Cosmos::Logger.info "Deleting #{scope}/targets_modified/#{path}"
149
- response = $api_server.request('delete', endpoint, query: {bucket: 'config'})
150
- if response.nil? || response.code != 200
151
- raise "Failed to delete #{scope}/targets_modified/#{path}"
152
- end
153
- rescue => error
154
- raise "Failed deleting #{path} due to #{error.message}"
126
+ def _get_uri(url)
127
+ if $cosmos_in_cluster
128
+ uri = URI.parse("http://cosmos-minio:9000" + url)
129
+ else
130
+ uri = URI.parse($api_server.generate_url + url)
155
131
  end
156
- nil
132
+ end
133
+
134
+ def _get_presigned_request(endpoint)
135
+ if $cosmos_in_cluster
136
+ response = $api_server.request('get', endpoint, query: { bucket: 'config', internal: true })
137
+ else
138
+ response = $api_server.request('get', endpoint, query: { bucket: 'config' })
139
+ end
140
+ if response.nil? || response.code != 201
141
+ raise "Failed to get presigned URL for #{endpoint}"
142
+ end
143
+ JSON.parse(response.body)
157
144
  end
158
145
  end
159
146
  end
@@ -80,7 +80,7 @@ module Cosmos
80
80
  alias dup clone
81
81
 
82
82
  # Create a hash of this item's attributes
83
- def to_hash
83
+ def as_json
84
84
  hash = super()
85
85
  hash['editable'] = self.editable
86
86
  hash['hidden'] = self.hidden
@@ -21,6 +21,10 @@ require 'cosmos/topics/topic'
21
21
 
22
22
  module Cosmos
23
23
  class CommandDecomTopic < Topic
24
+ def self.topics(scope:)
25
+ super(scope, 'DECOMCMD')
26
+ end
27
+
24
28
  def self.write_packet(packet, scope:)
25
29
  topic = "#{scope}__DECOMCMD__{#{packet.target_name}}__#{packet.packet_name}"
26
30
  msg_hash = { time: packet.received_time.to_nsec_from_epoch,
@@ -21,6 +21,10 @@ require 'cosmos/topics/topic'
21
21
 
22
22
  module Cosmos
23
23
  class TelemetryDecomTopic < Topic
24
+ def self.topics(scope:)
25
+ super(scope, 'DECOM')
26
+ end
27
+
24
28
  def self.write_packet(packet, id: nil, scope:)
25
29
  # Need to build a JSON hash of the decommutated data
26
30
  # Support "downward typing"
@@ -34,5 +34,15 @@ module Cosmos
34
34
  Store.xtrim(topic, maxlen)
35
35
  end
36
36
  end
37
+
38
+ def self.topics(scope, key)
39
+ topics = []
40
+ loop do
41
+ token, streams = Store.scan(0, :match => "#{scope}__#{key}__*", :count => 1000)
42
+ topics.concat(streams)
43
+ break if token == 0
44
+ end
45
+ topics
46
+ end
37
47
  end
38
48
  end