cosmos 5.0.2.pre.beta2 → 5.0.4

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +1 -1
  3. data/data/config/microservice.yaml +47 -35
  4. data/data/config/plugins.yaml +3 -150
  5. data/data/config/target.yaml +70 -0
  6. data/data/config/tool.yaml +37 -31
  7. data/lib/cosmos/api/api.rb +1 -25
  8. data/lib/cosmos/api/cmd_api.rb +17 -6
  9. data/lib/cosmos/api/config_api.rb +10 -4
  10. data/lib/cosmos/api/limits_api.rb +1 -1
  11. data/lib/cosmos/api/settings_api.rb +19 -7
  12. data/lib/cosmos/api/target_api.rb +2 -2
  13. data/lib/cosmos/api/tlm_api.rb +69 -41
  14. data/lib/cosmos/config/config_parser.rb +19 -22
  15. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  16. data/lib/cosmos/conversions/generic_conversion.rb +2 -2
  17. data/lib/cosmos/conversions/polynomial_conversion.rb +5 -8
  18. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +26 -9
  19. data/lib/cosmos/io/json_drb.rb +5 -1
  20. data/lib/cosmos/logs/log_writer.rb +2 -2
  21. data/lib/cosmos/microservices/cleanup_microservice.rb +28 -29
  22. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  23. data/lib/cosmos/microservices/interface_microservice.rb +0 -1
  24. data/lib/cosmos/microservices/microservice.rb +3 -3
  25. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  26. data/lib/cosmos/models/cvt_model.rb +6 -6
  27. data/lib/cosmos/models/gem_model.rb +3 -3
  28. data/lib/cosmos/models/info_model.rb +1 -1
  29. data/lib/cosmos/models/interface_status_model.rb +1 -1
  30. data/lib/cosmos/models/metadata_model.rb +42 -216
  31. data/lib/cosmos/models/metric_model.rb +2 -2
  32. data/lib/cosmos/models/microservice_model.rb +1 -1
  33. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  34. data/lib/cosmos/models/model.rb +16 -16
  35. data/lib/cosmos/models/note_model.rb +124 -0
  36. data/lib/cosmos/models/ping_model.rb +2 -1
  37. data/lib/cosmos/models/plugin_model.rb +1 -1
  38. data/lib/cosmos/models/process_status_model.rb +1 -1
  39. data/lib/cosmos/models/scope_model.rb +9 -26
  40. data/lib/cosmos/models/settings_model.rb +55 -0
  41. data/lib/cosmos/models/sorted_model.rb +165 -0
  42. data/lib/cosmos/models/target_model.rb +120 -13
  43. data/lib/cosmos/models/tool_config_model.rb +38 -0
  44. data/lib/cosmos/models/tool_model.rb +1 -1
  45. data/lib/cosmos/models/widget_model.rb +1 -1
  46. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  47. data/lib/cosmos/packets/packet.rb +23 -0
  48. data/lib/cosmos/packets/packet_config.rb +2 -2
  49. data/lib/cosmos/packets/packet_item.rb +57 -0
  50. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  51. data/lib/cosmos/packets/parsers/packet_item_parser.rb +1 -1
  52. data/lib/cosmos/packets/parsers/packet_parser.rb +1 -1
  53. data/lib/cosmos/packets/parsers/xtce_parser.rb +1 -1
  54. data/lib/cosmos/packets/structure_item.rb +10 -1
  55. data/lib/cosmos/script/api_shared.rb +30 -25
  56. data/lib/cosmos/script/calendar.rb +26 -15
  57. data/lib/cosmos/script/commands.rb +5 -7
  58. data/lib/cosmos/script/script.rb +19 -39
  59. data/lib/cosmos/script/storage.rb +92 -105
  60. data/lib/cosmos/system/system.rb +2 -1
  61. data/lib/cosmos/tools/table_manager/table_item.rb +1 -1
  62. data/lib/cosmos/top_level.rb +5 -1
  63. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  64. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  65. data/lib/cosmos/topics/command_decom_topic.rb +35 -1
  66. data/lib/cosmos/topics/command_topic.rb +6 -4
  67. data/lib/cosmos/topics/interface_topic.rb +8 -8
  68. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  69. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  70. data/lib/cosmos/topics/router_topic.rb +9 -9
  71. data/lib/cosmos/topics/telemetry_decom_topic.rb +5 -1
  72. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  73. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  74. data/lib/cosmos/topics/topic.rb +23 -8
  75. data/lib/cosmos/utilities/logger.rb +4 -3
  76. data/lib/cosmos/utilities/metric.rb +32 -26
  77. data/lib/cosmos/utilities/s3.rb +61 -0
  78. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  79. data/lib/cosmos/utilities/store.rb +1 -0
  80. data/lib/cosmos/utilities/store_autoload.rb +25 -134
  81. data/lib/cosmos/version.rb +6 -5
  82. data/templates/plugin-template/plugin.gemspec +0 -2
  83. metadata +9 -6
  84. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -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.
@@ -18,6 +18,7 @@
18
18
  # copyright holder
19
19
 
20
20
  require 'cosmos/script/extract'
21
+ require 'time'
21
22
 
22
23
  module Cosmos
23
24
  module Script
@@ -25,38 +26,48 @@ module Cosmos
25
26
 
26
27
  private
27
28
 
28
- # Sets the metadata value for a target
29
+ # Gets the current metadata
29
30
  #
30
- # @param target [String] Target to set metadata on
31
31
  # @return The result of the method call.
32
- def get_metadata(target)
33
- endpoint = "/cosmos-api/metadata/_get/#{target}"
34
- response = $api_server.request('get', endpoint)
32
+ def get_metadata()
33
+ response = $api_server.request('get', "/cosmos-api/metadata/latest")
35
34
  return nil if response.nil? || response.code != 200
36
35
  return JSON.parse(response.body)
37
36
  end
38
37
 
39
- # Sets the metadata value for a target
38
+ # Sets the metadata
40
39
  #
41
- # @param target [String] Target to set metadata on
42
40
  # @param metadata [Hash<Symbol, Variable>] A hash of metadata
43
- # @param color [String] Events color to show on Calendar tool
44
- # @param start (optional) [String] Metadata time value if nil will default to current time
41
+ # @param color [String] Events color to show on Calendar tool, if nil will be blue
42
+ # @param start [Time] Metadata time value, if nil will be current time
45
43
  # @return The result of the method call.
46
- def set_metadata(target, metadata, color: nil, start: nil)
44
+ def set_metadata(metadata, color: nil, start: nil)
47
45
  color = color.nil? ? '#003784' : color
48
- data = {:color => color, :metadata => metadata, :target => target}
49
- data[:start] = start unless start.nil?
46
+ data = { color: color, metadata: metadata }
47
+ data[:start] = start.iso8601 unless start.nil?
50
48
  response = $api_server.request('post', '/cosmos-api/metadata', data: data, json: true)
51
49
  return nil if response.nil? || response.code != 201
52
50
  return JSON.parse(response.body)
53
51
  end
54
52
 
55
- # Requests the metadata from the user for a target
53
+ # Updates the metadata
56
54
  #
57
- def input_metadata(*args, **kwargs)
58
- rasie StandardError "can only be used in script-runner"
55
+ # @param start [Integer] Metadata time value as integer seconds from epoch
56
+ # @param metadata [Hash<Symbol, Variable>] A hash of metadata
57
+ # @param color [String] Events color to show on Calendar tool, if nil will be blue
58
+ # @return The result of the method call.
59
+ def update_metadata(start, metadata, color: nil)
60
+ color = color.nil? ? '#003784' : color
61
+ data = { :color => color, :metadata => metadata }
62
+ data[:start] = Time.at(start).iso8601
63
+ response = $api_server.request('put', "/cosmos-api/metadata/#{start}", data: data, json: true)
64
+ return nil if response.nil? || response.code != 201
65
+ return JSON.parse(response.body)
59
66
  end
60
67
 
68
+ # Requests the metadata from the user for a target
69
+ def input_metadata(*args, **kwargs)
70
+ raise StandardError "can only be used in Script Runner"
71
+ end
61
72
  end
62
73
  end
@@ -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
@@ -26,6 +26,7 @@ require 'cosmos/packets/limits'
26
26
  require 'cosmos/system/target'
27
27
  require 'cosmos/utilities/s3'
28
28
  require 'cosmos/utilities/zip'
29
+ require 'cosmos/models/scope_model'
29
30
  require 'thread'
30
31
  require 'fileutils'
31
32
 
@@ -54,7 +55,7 @@ module Cosmos
54
55
 
55
56
  # @return [Symbol] The current limits_set of the system returned from Redis
56
57
  def self.limits_set
57
- Store.hget("#{$cosmos_scope}__cosmos_system", 'limits_set').intern
58
+ ScopeModel.limits_set(scope: $cosmos_scope)
58
59
  end
59
60
 
60
61
  def self.setup_targets(target_names, base_dir, scope:)
@@ -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
@@ -37,9 +37,13 @@ class HazardousError < StandardError
37
37
  attr_accessor :cmd_name
38
38
  attr_accessor :cmd_params
39
39
  attr_accessor :hazardous_description
40
+ attr_accessor :formatted # formatted command for use in resending original
40
41
 
41
42
  def to_s
42
- "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous due to #{hazardous_description}"
43
+ string = "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous"
44
+ string << "due to '#{hazardous_description}'" if hazardous_description
45
+ # Pass along the original formatted command so it can be resent
46
+ string << ".\n#{formatted}"
43
47
  end
44
48
  end
45
49
 
@@ -26,7 +26,7 @@ module Cosmos
26
26
  # Notify to the topic
27
27
  #
28
28
  # ```json
29
- # {
29
+ # {
30
30
  # "kind" => "created",
31
31
  # "type" => "trigger",
32
32
  # "data" => {
@@ -46,7 +46,7 @@ module Cosmos
46
46
  # }
47
47
  # ```
48
48
  def self.write_notification(notification, scope:)
49
- Store.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
49
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
50
50
  end
51
51
  end
52
52
  end
@@ -38,7 +38,7 @@ module Cosmos
38
38
  # }
39
39
  # ```
40
40
  def self.write_entry(entry, scope:)
41
- Store.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
41
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
42
42
  end
43
43
  end
44
44
  end
@@ -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,
@@ -36,7 +40,37 @@ module Cosmos
36
40
  json_hash[item.name + "__U"] = packet.read_item(item, :WITH_UNITS) if item.units
37
41
  end
38
42
  msg_hash['json_data'] = JSON.generate(json_hash.as_json)
39
- Store.write_topic(topic, msg_hash)
43
+ Topic.write_topic(topic, msg_hash)
44
+ end
45
+
46
+ def self.get_cmd_item(target_name, packet_name, param_name, type: :WITH_UNITS, scope: $cosmos_scope)
47
+ msg_id, msg_hash = Topic.get_newest_message("#{scope}__DECOMCMD__{#{target_name}}__#{packet_name}")
48
+ if msg_id
49
+ # TODO: We now have these reserved items directly on command packets
50
+ # Do we still calculate from msg_hash['time'] or use the times directly?
51
+ #
52
+ # if param_name == 'RECEIVED_TIMESECONDS' || param_name == 'PACKET_TIMESECONDS'
53
+ # Time.from_nsec_from_epoch(msg_hash['time'].to_i).to_f
54
+ # elsif param_name == 'RECEIVED_TIMEFORMATTED' || param_name == 'PACKET_TIMEFORMATTED'
55
+ # Time.from_nsec_from_epoch(msg_hash['time'].to_i).formatted
56
+ if param_name == 'RECEIVED_COUNT'
57
+ msg_hash['received_count'].to_i
58
+ else
59
+ json = msg_hash['json_data']
60
+ hash = JSON.parse(json)
61
+ # Start from the most complex down to the basic raw value
62
+ value = hash["#{param_name}__U"]
63
+ return value if value && type == :WITH_UNITS
64
+
65
+ value = hash["#{param_name}__F"]
66
+ return value if value && (type == :WITH_UNITS || type == :FORMATTED)
67
+
68
+ value = hash["#{param_name}__C"]
69
+ return value if value && (type == :WITH_UNITS || type == :FORMATTED || type == :CONVERTED)
70
+
71
+ return hash[param_name]
72
+ end
73
+ end
40
74
  end
41
75
  end
42
76
  end