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
@@ -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