cosmos 5.0.2.pre.beta2 → 5.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -19,8 +19,6 @@
19
19
 
20
20
  module Cosmos
21
21
  module Api
22
- SETTINGS_KEY = "cosmos__settings"
23
-
24
22
  WHITELIST ||= []
25
23
  WHITELIST.concat([
26
24
  'list_settings',
@@ -31,28 +29,42 @@ module Cosmos
31
29
  ])
32
30
 
33
31
  def list_settings(scope: $cosmos_scope, token: $cosmos_token)
34
- Store.instance.hkeys(SETTINGS_KEY)
32
+ authorize(permission: 'system', scope: scope, token: token)
33
+ SettingsModel.names(scope: scope)
35
34
  end
36
35
 
37
36
  def get_all_settings(scope: $cosmos_scope, token: $cosmos_token)
38
- Store.instance.hgetall(SETTINGS_KEY)
37
+ authorize(permission: 'system', scope: scope, token: token)
38
+ SettingsModel.all(scope: scope)
39
39
  end
40
40
 
41
41
  def get_setting(name, scope: $cosmos_scope, token: $cosmos_token)
42
- Store.instance.hget(SETTINGS_KEY, name)
42
+ authorize(permission: 'system', scope: scope, token: token)
43
+ setting = SettingsModel.get(name: name, scope: scope)
44
+ if setting
45
+ return setting["data"]
46
+ else
47
+ return nil
48
+ end
43
49
  end
44
50
 
45
51
  def get_settings(*args, scope: $cosmos_scope, token: $cosmos_token)
52
+ authorize(permission: 'system', scope: scope, token: token)
46
53
  ret = []
47
54
  args.each do |name|
48
- ret << Store.instance.hget(SETTINGS_KEY, name)
55
+ setting = SettingsModel.get(name: name, scope: scope)
56
+ if setting
57
+ ret << setting["data"]
58
+ else
59
+ ret << nil
60
+ end
49
61
  end
50
62
  return ret
51
63
  end
52
64
 
53
65
  def save_setting(name, data, scope: $cosmos_scope, token: $cosmos_token)
54
66
  authorize(permission: 'admin', scope: scope, token: token)
55
- Store.instance.hset(SETTINGS_KEY, name, data)
67
+ SettingsModel.set({ name: name, data: data }, scope: scope)
56
68
  end
57
69
  end
58
70
  end
@@ -56,12 +56,12 @@ module Cosmos
56
56
  cmd_cnt = 0
57
57
  packets = TargetModel.packets(target_name, type: :CMD, scope: scope)
58
58
  packets.each do |packet|
59
- cmd_cnt += _get_cnt("#{scope}__COMMAND__{#{target_name}}__#{packet['packet_name']}")
59
+ cmd_cnt += Topic.get_cnt("#{scope}__COMMAND__{#{target_name}}__#{packet['packet_name']}")
60
60
  end
61
61
  tlm_cnt = 0
62
62
  packets = TargetModel.packets(target_name, type: :TLM, scope: scope)
63
63
  packets.each do |packet|
64
- tlm_cnt += _get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet['packet_name']}")
64
+ tlm_cnt += Topic.get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet['packet_name']}")
65
65
  end
66
66
  interface_name = ''
67
67
  InterfaceModel.all(scope: scope).each do |name, interface|
@@ -19,6 +19,8 @@
19
19
 
20
20
  require 'cosmos/models/target_model'
21
21
  require 'cosmos/models/cvt_model'
22
+ require 'cosmos/packets/packet'
23
+ require 'cosmos/topics/telemetry_topic'
22
24
  require 'cosmos/utilities/s3'
23
25
 
24
26
  module Cosmos
@@ -38,10 +40,11 @@ module Cosmos
38
40
  'get_tlm_packet',
39
41
  'get_tlm_values',
40
42
  'get_all_telemetry',
43
+ 'get_all_telemetry_list',
41
44
  'get_telemetry',
42
45
  'get_item',
43
46
  'subscribe_packets',
44
- 'get_packet',
47
+ 'get_packets',
45
48
  'get_all_tlm_info',
46
49
  'get_tlm_cnt',
47
50
  'get_packet_derived_items',
@@ -125,19 +128,18 @@ module Cosmos
125
128
  # Check that the packet exists ... exceptions are raised if not
126
129
  TargetModel.packet(target_name, packet_name, scope: scope)
127
130
  end
128
- inject = {}
129
- inject['inject_tlm'] = true
130
- inject['log'] = log
131
- inject['target_name'] = target_name
132
- inject['packet_name'] = packet_name
133
- inject['item_hash'] = JSON.generate(item_hash) if item_hash
134
- inject['type'] = type
135
-
136
- InterfaceModel.all(scope: scope).each do |name, interface|
137
- if interface['target_names'].include? target_name
138
- Store.write_topic("{#{scope}__CMD}INTERFACE__#{interface['name']}", inject, '*', 100)
131
+
132
+ packet_hash = get_telemetry(target_name, packet_name, scope: scope, token: token)
133
+ packet = Packet.from_json(packet_hash)
134
+ if item_hash
135
+ item_hash.each do |name, value|
136
+ packet.write(name.to_s, value, type)
139
137
  end
140
138
  end
139
+ packet.received_time = Time.now.sys
140
+ # TODO: New packet so received_count is not correct
141
+ packet.received_count += 1
142
+ TelemetryTopic.write_packet(packet, scope: scope)
141
143
  end
142
144
 
143
145
  # Override the current value table such that a particular item always
@@ -188,7 +190,7 @@ module Cosmos
188
190
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
189
191
  TargetModel.packet(target_name, packet_name, scope: scope)
190
192
  topic = "#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}"
191
- msg_id, msg_hash = Store.instance.read_topic_last(topic)
193
+ msg_id, msg_hash = Topic.get_newest_message(topic)
192
194
  if msg_id
193
195
  msg_hash['buffer'] = msg_hash['buffer'].b
194
196
  return msg_hash
@@ -245,7 +247,17 @@ module Cosmos
245
247
  # @return [Array<Hash>] Array of all telemetry packet hashes
246
248
  def get_all_telemetry(target_name, scope: $cosmos_scope, token: $cosmos_token)
247
249
  authorize(permission: 'tlm', target_name: target_name, scope: scope, token: token)
248
- TargetModel.packets(target_name, scope: scope)
250
+ TargetModel.packets(target_name, type: :TLM, scope: scope)
251
+ end
252
+
253
+ # Returns an array of all telemetry packet's name and descriptions
254
+ #
255
+ # @since 5.0.3
256
+ # @param target_name [String] Name of the target
257
+ # @return [Array<Hash>] Array of all telemetry packet name and descriptions
258
+ def get_all_telemetry_list(target_name, scope: $cosmos_scope, token: $cosmos_token)
259
+ authorize(permission: 'tlm', target_name: target_name, scope: scope, token: token)
260
+ TargetModel.all_packet_name_descriptions(target_name, type: :TLM, scope: scope)
249
261
  end
250
262
 
251
263
  # Returns a telemetry packet hash
@@ -271,41 +283,53 @@ module Cosmos
271
283
  TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
272
284
  end
273
285
 
286
+ # 2x double underscore since __ is reserved
287
+ SUBSCRIPTION_DELIMITER = '____'
288
+
274
289
  # Subscribe to a list of packets. An ID is returned which is passed to
275
- # get_packet(id) to yield packets back to a block.
290
+ # get_packets(id) to return packets.
276
291
  #
277
292
  # @param packets [Array<Array<String, String>>] Array of arrays consisting of target name, packet name
278
- # @return [String] ID which should be passed to get_packet
293
+ # @return [String] ID which should be passed to get_packets
279
294
  def subscribe_packets(packets, scope: $cosmos_scope, token: $cosmos_token)
280
295
  if !packets.is_a?(Array) || !packets[0].is_a?(Array)
281
296
  raise ArgumentError, "packets must be nested array: [['TGT','PKT'],...]"
282
297
  end
283
298
 
284
- result = [Time.now.to_nsec_from_epoch]
299
+ result = {}
285
300
  packets.each do |target_name, packet_name|
286
301
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
287
- result << "#{scope}__DECOM__{#{target_name}}__#{packet_name}"
302
+ topic = "#{scope}__DECOM__{#{target_name}}__#{packet_name}"
303
+ id, _ = Topic.get_newest_message(topic)
304
+ result[topic] = id ? id : '0-0'
288
305
  end
289
- result.join("\n")
306
+ result.to_a.join(SUBSCRIPTION_DELIMITER)
290
307
  end
291
308
  # Alias the singular as well since that matches COSMOS 4
292
309
  alias subscribe_packet subscribe_packets
293
310
 
294
- # Get a packet which was previously subscribed to by subscribe_packet.
295
- # This method takes a block and yields back packet hashes.
296
- def get_packet(id, scope: $cosmos_scope, token: $cosmos_token)
311
+ # Get packets based on ID returned from subscribe_packet.
312
+ # @param id [String] ID returned from subscribe_packets or last call to get_packets
313
+ # @param block [Integer] Number of milliseconds to block when requesting packets
314
+ # @param count [Integer] Maximum number of packets to return from EACH packet stream
315
+ # @return [Array<String, Array<Hash>] Array of the ID and array of all packets found
316
+ def get_packets(id, block: nil, count: 1000, scope: $cosmos_scope, token: $cosmos_token)
297
317
  authorize(permission: 'tlm', scope: scope, token: token)
298
- offset, *topics = id.split("\n")
299
- offsets = []
300
- # Create a common array of offsets for each of the topics
301
- topics.length.times do
302
- offsets << (offset.to_i / 1_000_000).to_s + '-0'
303
- end
304
- Topic.read_topics(topics, offsets) do |topic, msg_id, msg_hash, redis|
305
- json_hash = JSON.parse(msg_hash['json_data'])
306
- msg_hash.delete('json_data')
307
- yield msg_hash.merge(json_hash)
318
+ # Split the list of topic, ID values and turn it into a hash for easy updates
319
+ lookup = Hash[*id.split(SUBSCRIPTION_DELIMITER)]
320
+ xread = Topic.read_topics(lookup.keys, lookup.values, block, count)
321
+ # Return the original ID and nil if we didn't get anything
322
+ return [id, nil] if xread.empty?
323
+ packets = []
324
+ xread.each do |topic, data|
325
+ data.each do |id, msg_hash|
326
+ lookup[topic] = id # save the new ID
327
+ json_hash = JSON.parse(msg_hash['json_data'])
328
+ msg_hash.delete('json_data')
329
+ packets << msg_hash.merge(json_hash)
330
+ end
308
331
  end
332
+ return [lookup.to_a.join(SUBSCRIPTION_DELIMITER), packets]
309
333
  end
310
334
 
311
335
  # Get the receive count for a telemetry packet
@@ -316,7 +340,7 @@ module Cosmos
316
340
  def get_tlm_cnt(target_name, packet_name, scope: $cosmos_scope, token: $cosmos_token)
317
341
  authorize(permission: 'system', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
318
342
  TargetModel.packet(target_name, packet_name, scope: scope)
319
- _get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}")
343
+ Topic.get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}")
320
344
  end
321
345
 
322
346
  # Get information on all telemetry packets
@@ -329,14 +353,14 @@ module Cosmos
329
353
  TargetModel.packets(target_name, scope: scope).each do | packet |
330
354
  packet_name = packet['packet_name']
331
355
  key = "#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}"
332
- result << [target_name, packet_name, _get_cnt(key)]
356
+ result << [target_name, packet_name, Topic.get_cnt(key)]
333
357
  end
334
358
  end
335
359
  ['UNKNOWN'].each do | x |
336
360
  key = "#{scope}__TELEMETRY__{#{x}}__#{x}"
337
- result << [x, x, _get_cnt(key)]
361
+ result << [x, x, Topic.get_cnt(key)]
338
362
  end
339
- # Return the results sorted by target, packet
363
+ # Return the result sorted by target, packet
340
364
  result.sort_by { |a| [a[0], a[1]] }
341
365
  end
342
366
 
@@ -358,10 +382,14 @@ module Cosmos
358
382
  # Request the path by calling the key method. Returns something like this:
359
383
  # DEFAULT/decom_logs/tlm/INST2/MECH/20220104/20220104165449021942700__20220104170449148642700__DEFAULT__INST2__MECH__rt__decom.bin
360
384
  # Thus we split and take the start date/time part of the filename
361
- start = list[0].key.split('/')[-1].split('__')[0]
362
- # Format as YYYY-MM-DD HH:MM:SS for use by the frontend
363
- # utc_time = Time.utc(start[0,4], start[4,2], start[6,2], start[8,2], start[10,2], start[12,2])
364
- return "#{start[0,4]}-#{start[4,2]}-#{start[6,2]} #{start[8,2]}:#{start[10,2]}:#{start[12,2]}"
385
+ if list and list[0]
386
+ start = list[0].key.split('/')[-1].split('__')[0]
387
+ # Format as YYYY-MM-DD HH:MM:SS for use by the frontend
388
+ # utc_time = Time.utc(start[0,4], start[4,2], start[6,2], start[8,2], start[10,2], start[12,2])
389
+ return "#{start[0,4]}-#{start[4,2]}-#{start[6,2]} #{start[8,2]}:#{start[10,2]}:#{start[12,2]}"
390
+ else
391
+ return Time.now.utc.to_s[0..18]
392
+ end
365
393
  end
366
394
 
367
395
  # PRIVATE
@@ -397,7 +425,7 @@ module Cosmos
397
425
  TargetModel.packets(target_name, scope: scope).each do |packet|
398
426
  item = packet['items'].find { |item| item['name'] == item_name }
399
427
  if item
400
- _, msg_hash = Store.instance.get_oldest_message("#{scope}__DECOM__{#{target_name}}__#{packet['packet_name']}")
428
+ _, msg_hash = Topic.get_oldest_message("#{scope}__DECOM__{#{target_name}}__#{packet['packet_name']}")
401
429
  if msg_hash && msg_hash['time'] && msg_hash['time'].to_i > latest
402
430
  packet_name = packet['packet_name']
403
431
  latest = msg_hash['time'].to_i
@@ -168,7 +168,7 @@ module Cosmos
168
168
  path = File.join(File.dirname(@filename), template_name)
169
169
  end
170
170
  Cosmos.set_working_dir(File.dirname(path)) do
171
- return ERB.new(File.read(path)).result(b)
171
+ return ERB.new(File.read(path), trim_mode: "-").result(b)
172
172
  end
173
173
  end
174
174
 
@@ -238,27 +238,24 @@ module Cosmos
238
238
  end
239
239
  end
240
240
 
241
- # Verifies the indicated parameter(s) in the config don't start or end
242
- # with an underscore and don't contain a double underscore. Raises an
243
- # Error if they do.
241
+ # Verifies the indicated parameter in the config doesn't start or end
242
+ # with an underscore, doesn't contain a double underscore, doesn't contain
243
+ # spaces and doesn't start with a close bracket.
244
244
  #
245
- # @param [Integer|Range<Integer>|Array<Integer>] indeces_to_check The
246
- # indeces of the parameters that must follow underscore rules
247
- def verify_parameters_underscores(indeces_to_check, usage = "")
248
- indeces_to_check = (1..@parameters.length) if indeces_to_check.nil?
249
- indeces_to_check = [indeces_to_check] unless indeces_to_check.respond_to? 'each'
250
-
251
- indeces_to_check.each do |index|
252
- param = @parameters[index - 1]
253
- if param.end_with? '_'
254
- raise Error.new(self, "Parameter #{index} (#{@parameters[index - 1]}) for #{@keyword} cannot end with an underscore ('_').", usage, @url)
255
- end
256
- if param.include? '__'
257
- raise Error.new(self, "Parameter #{index} (#{@parameters[index - 1]}) for #{@keyword} cannot contain a double underscore ('__').", usage, @url)
258
- end
259
- if param.include? ' '
260
- raise Error.new(self, "Parameter #{index} (#{@parameters[index - 1]}) for #{@keyword} cannot contain a space (' ').", usage, @url)
261
- end
245
+ # @param [Integer] index The index of the parameter to check
246
+ def verify_parameter_naming(index, usage = "")
247
+ param = @parameters[index - 1]
248
+ if param.end_with? '_'
249
+ raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot end with an underscore ('_').", usage, @url)
250
+ end
251
+ if param.include? '__'
252
+ raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain a double underscore ('__').", usage, @url)
253
+ end
254
+ if param.include? ' '
255
+ raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain a space (' ').", usage, @url)
256
+ end
257
+ if param.start_with?('}')
258
+ raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot start with a close bracket ('}').", usage, @url)
262
259
  end
263
260
  end
264
261
 
@@ -380,7 +377,7 @@ module Cosmos
380
377
  output = nil
381
378
  if run_erb
382
379
  Cosmos.set_working_dir(File.dirname(filename)) do
383
- output = ERB.new(File.read(filename)).result(binding.set_variables(variables))
380
+ output = ERB.new(File.read(filename), trim_mode: "-").result(binding.set_variables(variables))
384
381
  end
385
382
  else
386
383
  output = File.read(filename)
@@ -50,7 +50,7 @@ module Cosmos
50
50
 
51
51
  output = nil
52
52
  Cosmos.set_working_dir(File.dirname(path)) do
53
- output = ERB.new(File.read(path)).result(binding)
53
+ output = ERB.new(File.read(path), trim_mode: "-").result(binding)
54
54
  end
55
55
  tf.write(output)
56
56
  tf.close
@@ -73,5 +73,5 @@ module Cosmos
73
73
  def as_json
74
74
  { 'class' => self.class.name.to_s, 'params' => [@code_to_eval, @converted_type, @converted_bit_size] }
75
75
  end
76
- end # class GenericConversion
77
- end # module Cosmos
76
+ end
77
+ end
@@ -29,13 +29,10 @@ module Cosmos
29
29
  # Initializes the conversion with the given polynomial coefficients. Sets
30
30
  # the converted_type to :FLOAT and the converted_bit_size to 64.
31
31
  #
32
- # @param coeff_array [Array<Float>] The polynomial coefficients
33
- def initialize(coeff_array)
32
+ # @param coeffs [Array<Float>] The polynomial coefficients
33
+ def initialize(*coeffs)
34
34
  super()
35
- @coeffs = []
36
- coeff_array.each do |coeff|
37
- @coeffs << coeff.to_f
38
- end
35
+ @coeffs = coeffs.map { |coeff| coeff.to_f }
39
36
  @converted_type = :FLOAT
40
37
  @converted_bit_size = 64
41
38
  end
@@ -84,5 +81,5 @@ module Cosmos
84
81
  def as_json
85
82
  { 'class' => self.class.name.to_s, 'params' => @coeffs }
86
83
  end
87
- end # class PolynomialConversion
88
- end # module Cosmos
84
+ end
85
+ end
@@ -23,6 +23,9 @@ module Cosmos
23
23
  # Segmented polynomial conversions consist of polynomial conversions that are
24
24
  # applied for a range of values.
25
25
  class SegmentedPolynomialConversion < Conversion
26
+ # @return [Array<Segment>] Segments which make up this conversion
27
+ attr_reader :segments
28
+
26
29
  # A polynomial conversion segment which applies the conversion from the
27
30
  # lower bound (inclusive) until another segment's lower bound is
28
31
  # encountered.
@@ -31,6 +34,7 @@ module Cosmos
31
34
  # should apply. All values >= to this value will be converted using the
32
35
  # given coefficients.
33
36
  attr_reader :lower_bound
37
+
34
38
  # @return [Array<Integer>] The polynomial coefficients
35
39
  attr_reader :coeffs
36
40
 
@@ -57,6 +61,14 @@ module Cosmos
57
61
  return other_segment.lower_bound <=> @lower_bound
58
62
  end
59
63
 
64
+ # Implement equality operator primarily for ease of testing
65
+ #
66
+ # @param segment [Segment] Other segment
67
+ def ==(other_segment)
68
+ @lower_bound == other_segment.lower_bound &&
69
+ @coeffs == other_segment.coeffs
70
+ end
71
+
60
72
  # Perform the polynomial conversion
61
73
  #
62
74
  # @param value [Numeric] The value to convert
@@ -71,9 +83,15 @@ module Cosmos
71
83
  end
72
84
 
73
85
  # Initialize the converted_type to :FLOAT and converted_bit_size to 64.
74
- def initialize
86
+ #
87
+ # @param segments [Array] Array of segments typically generated by as_json
88
+ # Format similar to the following: [[15, [3, 2]], [10, [1, 2]]]
89
+ # Where each entry is an array with the first value as the lower_bound
90
+ # and the other entry is an array of the coefficients for that segment.
91
+ def initialize(segments = [])
75
92
  super()
76
93
  @segments = []
94
+ segments.each { |lower_bound, coeffs| add_segment(lower_bound, *coeffs) }
77
95
  @converted_type = :FLOAT
78
96
  @converted_bit_size = 64
79
97
  end
@@ -95,9 +113,7 @@ module Cosmos
95
113
  def call(value, packet, buffer)
96
114
  # Try to find correct segment
97
115
  @segments.each do |segment|
98
- if value >= segment.lower_bound
99
- return segment.calculate(value)
100
- end
116
+ return segment.calculate(value) if value >= segment.lower_bound
101
117
  end
102
118
 
103
119
  # Default to using segment with smallest lower_bound
@@ -112,7 +128,7 @@ module Cosmos
112
128
  # @return [String] The name of the class followed by a description of all
113
129
  # the polynomial segments.
114
130
  def to_s
115
- result = ""
131
+ result = ''
116
132
  count = 0
117
133
  @segments.each do |segment|
118
134
  result << "\n" if count > 0
@@ -136,7 +152,8 @@ module Cosmos
136
152
  def to_config(read_or_write)
137
153
  config = ''
138
154
  @segments.each do |segment|
139
- config << " SEG_POLY_#{read_or_write}_CONVERSION #{segment.lower_bound} #{segment.coeffs.join(' ')}\n"
155
+ config <<
156
+ " SEG_POLY_#{read_or_write}_CONVERSION #{segment.lower_bound} #{segment.coeffs.join(' ')}\n"
140
157
  end
141
158
  config
142
159
  end
@@ -146,7 +163,7 @@ module Cosmos
146
163
  @segments.each do |segment|
147
164
  params << [segment.lower_bound, segment.coeffs]
148
165
  end
149
- { 'class' => self.class.name.to_s, 'params' => params }
166
+ { 'class' => self.class.name.to_s, 'params' => [params] }
150
167
  end
151
- end # class SegmentedPolynomialConversion
152
- end # module Cosmos
168
+ end
169
+ end
@@ -269,7 +269,11 @@ module Cosmos
269
269
  response = JsonRpcSuccessResponse.new(result, request.id)
270
270
  end
271
271
  rescue Exception => error
272
- Logger.error error.formatted
272
+ # Filter out the framework stack trace (rails, rack, puma etc)
273
+ lines = error.formatted.split("\n")
274
+ i = lines.find_index { |row| row.include?('actionpack') || row.include?('activesupport') }
275
+ Logger.error lines[0...i].join("\n")
276
+
273
277
  if request.id
274
278
  if NoMethodError === error
275
279
  error_code = JsonRpcError::ErrorCode::METHOD_NOT_FOUND
@@ -19,7 +19,7 @@
19
19
 
20
20
  require 'thread'
21
21
  require 'cosmos/config/config_parser'
22
- require 'cosmos/utilities/store'
22
+ require 'cosmos/topics/topic'
23
23
  require 'cosmos/utilities/s3'
24
24
 
25
25
  module Cosmos
@@ -211,7 +211,7 @@ module Cosmos
211
211
  S3Utilities.move_log_file_to_s3(@filename, s3_key)
212
212
  # Now that the file is in S3, trim the Redis stream up until the previous file.
213
213
  # This keeps one file worth of data in Redis as a safety buffer
214
- Cosmos::Store.trim_topic(@redis_topic, @previous_file_redis_offset) if @redis_topic and @previous_file_redis_offset
214
+ Topic.trim_topic(@redis_topic, @previous_file_redis_offset) if @redis_topic and @previous_file_redis_offset
215
215
  @previous_file_redis_offset = @last_offset
216
216
  rescue Exception => err
217
217
  Logger.instance.error "Error closing #{@filename} : #{err.formatted}"
@@ -17,50 +17,49 @@
17
17
  # enterprise edition license of COSMOS if purchased from the
18
18
  # copyright holder
19
19
 
20
+ require 'cosmos/models/target_model'
20
21
  require 'cosmos/microservices/microservice'
21
22
  require 'cosmos/utilities/s3'
22
23
 
23
24
  module Cosmos
24
25
  class CleanupMicroservice < Microservice
25
26
  def run
26
- # Update settings from config
27
- @config['options'].each do |option|
28
- case option[0].upcase
29
- when 'SIZE' # Max size to use in S3 in bytes
30
- @size = option[1].to_i
31
- when 'DELAY' # Delay between size checks
32
- @delay = option[1].to_i
33
- when 'BUCKET' # Which bucket to monitor
34
- @bucket = option[1]
35
- when 'PREFIX' # Path into bucket to monitor
36
- @prefix = option[1]
37
- else
38
- Logger.error("Unknown option passed to microservice #{@name}: #{option}")
39
- end
40
- end
41
-
42
- raise "Microservice #{@name} not fully configured" unless @size and @delay and @bucket and @prefix
27
+ split_name = @name.split("__")
28
+ target_name = split_name[-1]
29
+ target = TargetModel.get_model(name: target_name, scope: @scope)
43
30
 
44
31
  rubys3_client = Aws::S3::Client.new
45
32
  while true
46
33
  break if @cancel_thread
47
34
 
48
35
  @state = 'GETTING_OBJECTS'
49
- total_size, oldest_list = S3Utilities.get_total_size_and_oldest_list(@bucket, @prefix)
50
- delete_items = []
51
- oldest_list.each do |item|
52
- break if total_size <= @size
53
-
54
- delete_items << { :key => item.key }
55
- total_size -= item.size
56
- end
57
- if delete_items.length > 0
58
- @state = 'DELETING_OBJECTS'
59
- rubys3_client.delete_objects({ bucket: @bucket, delete: { objects: delete_items } })
36
+ start_time = Time.now
37
+ [
38
+ ["#{@scope}/raw_logs/cmd/#{target_name}/", target.cmd_log_retain_time],
39
+ ["#{@scope}/decom_logs/cmd/#{target_name}/", target.cmd_decom_log_retain_time],
40
+ ["#{@scope}/raw_logs/tlm/#{target_name}/", target.tlm_log_retain_time],
41
+ ["#{@scope}/decom_logs/tlm/#{target_name}/", target.tlm_decom_log_retain_time],
42
+ ["#{@scope}/reduced_minute_logs/tlm/#{target_name}/", target.reduced_minute_log_retain_time],
43
+ ["#{@scope}/reduced_hour_logs/tlm/#{target_name}/", target.reduced_hour_log_retain_time],
44
+ ["#{@scope}/reduced_day_logs/tlm/#{target_name}/", target.reduced_day_log_retain_time],
45
+ ].each do |prefix, retain_time|
46
+ next unless retain_time
47
+ time = start_time - retain_time
48
+ total_size, oldest_list = S3Utilities.list_files_before_time('logs', prefix, time)
49
+ delete_items = []
50
+ oldest_list.each do |item|
51
+ delete_items << { :key => item.key }
52
+ end
53
+ if delete_items.length > 0
54
+ @state = 'DELETING_OBJECTS'
55
+ rubys3_client.delete_objects({ bucket: 'logs', delete: { objects: delete_items } })
56
+ Logger.info("Deleted #{delete_items.length} #{target_name} log files")
57
+ end
60
58
  end
59
+
61
60
  @count += 1
62
61
  @state = 'SLEEPING'
63
- break if @microservice_sleeper.sleep(@delay)
62
+ break if @microservice_sleeper.sleep(target.cleanup_poll_time)
64
63
  end
65
64
  end
66
65
  end
@@ -31,7 +31,7 @@ module Cosmos
31
31
 
32
32
  def initialize(*args)
33
33
  super(*args)
34
- Store.update_topic_offsets(@topics)
34
+ Topic.update_topic_offsets(@topics)
35
35
  System.telemetry.limits_change_callback = method(:limits_change_callback)
36
36
  end
37
37
 
@@ -143,7 +143,6 @@ module Cosmos
143
143
  hazardous, hazardous_description = System.commands.cmd_pkt_hazardous?(command)
144
144
  # Return back the error, description, and the formatted command
145
145
  # This allows the error handler to simply re-send the command
146
- # TODO: Should we set target_name, cmd_name, and cmd_params instead?
147
146
  next "HazardousError\n#{hazardous_description}\n#{System.commands.format(command)}" if hazardous
148
147
  end
149
148
 
@@ -40,7 +40,7 @@ module Cosmos
40
40
  attr_accessor :scope
41
41
 
42
42
  def self.run
43
- microservice = self.new(ARGV[0])
43
+ microservice = self.new(ENV['COSMOS_MICROSERVICE_NAME'])
44
44
  begin
45
45
  MicroserviceStatusModel.set(microservice.as_json, scope: microservice.scope)
46
46
  microservice.state = 'RUNNING'
@@ -52,7 +52,7 @@ module Cosmos
52
52
  else
53
53
  microservice.error = err
54
54
  microservice.state = 'DIED_ERROR'
55
- Logger.fatal("Microservice #{ARGV[0]} dying from exception\n#{err.formatted}")
55
+ Logger.fatal("Microservice #{ENV['COSMOS_MICROSERVICE_NAME']} dying from exception\n#{err.formatted}")
56
56
  end
57
57
  ensure
58
58
  MicroserviceStatusModel.set(microservice.as_json, scope: microservice.scope)
@@ -75,7 +75,7 @@ module Cosmos
75
75
 
76
76
  @name = name
77
77
  split_name = name.split("__")
78
- raise "Microservice names should be scope, type, and then name" if split_name.length != 3
78
+ raise "Name #{name} doesn't match convention of SCOPE__TYPE__NAME" if split_name.length != 3
79
79
 
80
80
  @scope = split_name[0]
81
81
  $cosmos_scope = @scope