cosmos 5.0.2 → 5.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +183 -42
  3. data/data/config/microservice.yaml +47 -35
  4. data/data/config/plugins.yaml +10 -147
  5. data/data/config/target.yaml +70 -0
  6. data/data/config/tool.yaml +37 -31
  7. data/ext/cosmos/ext/cosmos_io/cosmos_io.c +14 -14
  8. data/ext/cosmos/ext/packet/packet.c +3 -3
  9. data/ext/cosmos/ext/structure/structure.c +31 -31
  10. data/lib/cosmos/api/api.rb +1 -25
  11. data/lib/cosmos/api/cmd_api.rb +17 -6
  12. data/lib/cosmos/api/config_api.rb +10 -4
  13. data/lib/cosmos/api/limits_api.rb +1 -1
  14. data/lib/cosmos/api/settings_api.rb +19 -7
  15. data/lib/cosmos/api/target_api.rb +2 -2
  16. data/lib/cosmos/api/tlm_api.rb +65 -41
  17. data/lib/cosmos/config/config_parser.rb +19 -22
  18. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  19. data/lib/cosmos/conversions/generic_conversion.rb +2 -2
  20. data/lib/cosmos/conversions/polynomial_conversion.rb +5 -8
  21. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +26 -9
  22. data/lib/cosmos/io/json_drb.rb +5 -1
  23. data/lib/cosmos/logs/log_writer.rb +78 -29
  24. data/lib/cosmos/microservices/cleanup_microservice.rb +28 -29
  25. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  26. data/lib/cosmos/microservices/interface_microservice.rb +0 -16
  27. data/lib/cosmos/microservices/microservice.rb +3 -3
  28. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  29. data/lib/cosmos/models/cvt_model.rb +6 -6
  30. data/lib/cosmos/models/gem_model.rb +9 -3
  31. data/lib/cosmos/models/info_model.rb +1 -1
  32. data/lib/cosmos/models/interface_model.rb +16 -7
  33. data/lib/cosmos/models/interface_status_model.rb +1 -1
  34. data/lib/cosmos/models/metadata_model.rb +69 -219
  35. data/lib/cosmos/models/metric_model.rb +2 -2
  36. data/lib/cosmos/models/microservice_model.rb +7 -4
  37. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  38. data/lib/cosmos/models/model.rb +23 -16
  39. data/lib/cosmos/models/note_model.rb +122 -0
  40. data/lib/cosmos/models/ping_model.rb +2 -1
  41. data/lib/cosmos/models/plugin_model.rb +108 -48
  42. data/lib/cosmos/models/process_status_model.rb +1 -1
  43. data/lib/cosmos/models/scope_model.rb +10 -25
  44. data/lib/cosmos/models/settings_model.rb +55 -0
  45. data/lib/cosmos/models/sorted_model.rb +167 -0
  46. data/lib/cosmos/models/target_model.rb +143 -27
  47. data/lib/cosmos/models/tool_config_model.rb +38 -0
  48. data/lib/cosmos/models/tool_model.rb +9 -9
  49. data/lib/cosmos/models/widget_model.rb +11 -11
  50. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  51. data/lib/cosmos/packets/packet.rb +24 -1
  52. data/lib/cosmos/packets/packet_config.rb +2 -2
  53. data/lib/cosmos/packets/packet_item.rb +57 -0
  54. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  55. data/lib/cosmos/packets/parsers/packet_item_parser.rb +1 -1
  56. data/lib/cosmos/packets/parsers/packet_parser.rb +1 -1
  57. data/lib/cosmos/packets/parsers/xtce_parser.rb +1 -1
  58. data/lib/cosmos/packets/structure.rb +30 -33
  59. data/lib/cosmos/packets/structure_item.rb +10 -1
  60. data/lib/cosmos/script/api_shared.rb +30 -25
  61. data/lib/cosmos/script/calendar.rb +37 -15
  62. data/lib/cosmos/script/commands.rb +5 -7
  63. data/lib/cosmos/script/script.rb +19 -39
  64. data/lib/cosmos/script/storage.rb +92 -105
  65. data/lib/cosmos/system/system.rb +2 -1
  66. data/lib/cosmos/tools/table_manager/table_config.rb +16 -1
  67. data/lib/cosmos/tools/table_manager/table_item.rb +1 -1
  68. data/lib/cosmos/tools/table_manager/table_manager_core.rb +213 -309
  69. data/lib/cosmos/top_level.rb +5 -1
  70. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  71. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  72. data/lib/cosmos/topics/command_decom_topic.rb +35 -1
  73. data/lib/cosmos/topics/command_topic.rb +6 -4
  74. data/lib/cosmos/topics/config_topic.rb +68 -0
  75. data/lib/cosmos/topics/interface_topic.rb +8 -8
  76. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  77. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  78. data/lib/cosmos/topics/router_topic.rb +9 -9
  79. data/lib/cosmos/topics/telemetry_decom_topic.rb +5 -1
  80. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  81. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  82. data/lib/cosmos/topics/topic.rb +23 -8
  83. data/lib/cosmos/utilities/logger.rb +4 -3
  84. data/lib/cosmos/utilities/metric.rb +32 -26
  85. data/lib/cosmos/utilities/s3.rb +61 -0
  86. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  87. data/lib/cosmos/utilities/store.rb +1 -0
  88. data/lib/cosmos/utilities/store_autoload.rb +25 -134
  89. data/lib/cosmos/version.rb +5 -4
  90. data/templates/plugin-template/plugin.gemspec +0 -2
  91. metadata +12 -10
  92. data/bin/xtce_converter +0 -92
  93. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -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',
@@ -108,11 +111,11 @@ module Cosmos
108
111
 
109
112
  # Injects a packet into the system as if it was received from an interface
110
113
  #
111
- # @param target_name[String] Target name of the packet
112
- # @param packet_name[String] Packet name of the packet
113
- # @param item_hash[Hash] Hash of item_name and value for each item you want to change from the current value table
114
+ # @param target_name [String] Target name of the packet
115
+ # @param packet_name [String] Packet name of the packet
116
+ # @param item_hash [Hash] Hash of item_name and value for each item you want to change from the current value table
114
117
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
115
- def inject_tlm(target_name, packet_name, item_hash = nil, log: true, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token)
118
+ def inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token)
116
119
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
117
120
  unless CvtModel::VALUE_TYPES.include?(type.intern)
118
121
  raise "Unknown type '#{type}' for #{target_name} #{packet_name}"
@@ -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
 
@@ -401,7 +425,7 @@ module Cosmos
401
425
  TargetModel.packets(target_name, scope: scope).each do |packet|
402
426
  item = packet['items'].find { |item| item['name'] == item_name }
403
427
  if item
404
- _, 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']}")
405
429
  if msg_hash && msg_hash['time'] && msg_hash['time'].to_i > latest
406
430
  packet_name = packet['packet_name']
407
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
@@ -32,9 +32,41 @@ module Cosmos
32
32
  # @return [true/false] Whether logging is enabled
33
33
  attr_reader :logging_enabled
34
34
 
35
+ # @return cycle_time [Integer] The amount of time in seconds before creating
36
+ # a new log file. This can be combined with cycle_size but is better used
37
+ # independently.
38
+ attr_reader :cycle_time
39
+
40
+ # @return cycle_hour [Integer] The time at which to cycle the log. Combined with
41
+ # cycle_minute to cycle the log daily at the specified time. If nil, the log
42
+ # will be cycled hourly at the specified cycle_minute.
43
+ attr_reader :cycle_hour
44
+
45
+ # @return cycle_minute [Integer] The time at which to cycle the log. See cycle_hour
46
+ # for more information.
47
+ attr_reader :cycle_minute
48
+
49
+ # @return [Time] Time that the current log file started
50
+ attr_reader :start_time
51
+
52
+ # @return [Mutex] Instance mutex protecting file
53
+ attr_reader :mutex
54
+
35
55
  # The cycle time interval. Cycle times are only checked at this level of
36
56
  # granularity.
37
- CYCLE_TIME_INTERVAL = 2
57
+ CYCLE_TIME_INTERVAL = 10
58
+
59
+ # Mutex protecting class variables
60
+ @@mutex = Mutex.new
61
+
62
+ # Array of instances used to keep track of cycling logs
63
+ @@instances = []
64
+
65
+ # Thread used to cycle logs across all log writers
66
+ @@cycle_thread = nil
67
+
68
+ # Sleeper used to delay cycle thread
69
+ @@cycle_sleeper = nil
38
70
 
39
71
  # @param remote_log_directory [String] The s3 path to store the log files
40
72
  # @param logging_enabled [Boolean] Whether to start with logging enabled
@@ -89,11 +121,15 @@ module Cosmos
89
121
  # each time we create an entry which we do a LOT!
90
122
  @entry = String.new
91
123
 
92
- @cycle_thread = nil
93
124
  if @cycle_time or @cycle_hour or @cycle_minute
94
- @cycle_sleeper = Sleeper.new
95
- @cycle_thread = Cosmos.safe_thread("Log cycle") do
96
- cycle_thread_body()
125
+ @@mutex.synchronize do
126
+ @@instances << self
127
+
128
+ unless @@cycle_thread
129
+ @@cycle_thread = Cosmos.safe_thread("Log cycle") do
130
+ cycle_thread_body()
131
+ end
132
+ end
97
133
  end
98
134
  end
99
135
  end
@@ -113,10 +149,13 @@ module Cosmos
113
149
  # Stop all logging, close the current log file, and kill the logging threads.
114
150
  def shutdown
115
151
  stop()
116
- if @cycle_thread
117
- @cycle_sleeper.cancel
118
- Cosmos.kill_thread(self, @cycle_thread)
119
- @cycle_thread = nil
152
+ @@mutex.synchronize do
153
+ @@instances.delete(self)
154
+ if @@instances.length <= 0
155
+ @@cycle_sleeper.cancel if @@cycle_sleeper
156
+ Cosmos.kill_thread(self, @@cycle_thread) if @@cycle_thread
157
+ @@cycle_thread = nil
158
+ end
120
159
  end
121
160
  end
122
161
 
@@ -143,28 +182,38 @@ module Cosmos
143
182
  end
144
183
 
145
184
  def cycle_thread_body
185
+ @@cycle_sleeper = Sleeper.new
146
186
  while true
147
- # The check against start_time needs to be mutex protected to prevent a packet coming in between the check
148
- # and closing the file
149
- @mutex.synchronize do
150
- utc_now = Time.now.utc
151
- # Logger.debug("start:#{@start_time.to_f} now:#{utc_now.to_f} cycle:#{@cycle_time} new:#{(utc_now - @start_time) > @cycle_time}")
152
- if @logging_enabled and
153
- (
154
- # Cycle based on total time logging
155
- (@cycle_time and (utc_now - @start_time) > @cycle_time) or
156
-
157
- # Cycle daily at a specific time
158
- (@cycle_hour and @cycle_minute and utc_now.hour == @cycle_hour and utc_now.min == @cycle_minute and @start_time.yday != utc_now.yday) or
159
-
160
- # Cycle hourly at a specific time
161
- (@cycle_minute and not @cycle_hour and utc_now.min == @cycle_minute and @start_time.hour != utc_now.hour)
162
- )
163
- close_file(false)
187
+ start_time = Time.now
188
+ @@mutex.synchronize do
189
+ @@instances.each do |instance|
190
+ # The check against start_time needs to be mutex protected to prevent a packet coming in between the check
191
+ # and closing the file
192
+ instance.mutex.synchronize do
193
+ utc_now = Time.now.utc
194
+ # Logger.debug("start:#{@start_time.to_f} now:#{utc_now.to_f} cycle:#{@cycle_time} new:#{(utc_now - @start_time) > @cycle_time}")
195
+ if instance.logging_enabled and
196
+ (
197
+ # Cycle based on total time logging
198
+ (instance.cycle_time and (utc_now - instance.start_time) > instance.cycle_time) or
199
+
200
+ # Cycle daily at a specific time
201
+ (instance.cycle_hour and instance.cycle_minute and utc_now.hour == instance.cycle_hour and utc_now.min == instance.cycle_minute and instance.start_time.yday != utc_now.yday) or
202
+
203
+ # Cycle hourly at a specific time
204
+ (instance.cycle_minute and not instance.cycle_hour and utc_now.min == instance.cycle_minute and instance.start_time.hour != utc_now.hour)
205
+ )
206
+ instance.close_file(false)
207
+ end
208
+ end
164
209
  end
165
210
  end
211
+
166
212
  # Only check whether to cycle at a set interval
167
- break if @cycle_sleeper.sleep(CYCLE_TIME_INTERVAL)
213
+ run_time = Time.now - start_time
214
+ sleep_time = CYCLE_TIME_INTERVAL - run_time
215
+ sleep_time = 0 if sleep_time < 0
216
+ break if @@cycle_sleeper.sleep(sleep_time)
168
217
  end
169
218
  end
170
219
 
@@ -211,7 +260,7 @@ module Cosmos
211
260
  S3Utilities.move_log_file_to_s3(@filename, s3_key)
212
261
  # Now that the file is in S3, trim the Redis stream up until the previous file.
213
262
  # 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
263
+ Topic.trim_topic(@redis_topic, @previous_file_redis_offset) if @redis_topic and @previous_file_redis_offset
215
264
  @previous_file_redis_offset = @last_offset
216
265
  rescue Exception => err
217
266
  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