cosmos 5.0.3 → 5.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +1 -1
  3. data/lib/cosmos/api/api.rb +1 -25
  4. data/lib/cosmos/api/cmd_api.rb +6 -6
  5. data/lib/cosmos/api/config_api.rb +10 -4
  6. data/lib/cosmos/api/limits_api.rb +1 -1
  7. data/lib/cosmos/api/settings_api.rb +19 -7
  8. data/lib/cosmos/api/target_api.rb +2 -2
  9. data/lib/cosmos/api/tlm_api.rb +8 -8
  10. data/lib/cosmos/config/config_parser.rb +2 -2
  11. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  12. data/lib/cosmos/logs/log_writer.rb +2 -2
  13. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  14. data/lib/cosmos/microservices/interface_microservice.rb +0 -1
  15. data/lib/cosmos/microservices/microservice.rb +2 -2
  16. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  17. data/lib/cosmos/models/cvt_model.rb +6 -6
  18. data/lib/cosmos/models/gem_model.rb +2 -2
  19. data/lib/cosmos/models/info_model.rb +1 -1
  20. data/lib/cosmos/models/interface_status_model.rb +1 -1
  21. data/lib/cosmos/models/metadata_model.rb +42 -216
  22. data/lib/cosmos/models/metric_model.rb +2 -2
  23. data/lib/cosmos/models/microservice_model.rb +1 -1
  24. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  25. data/lib/cosmos/models/model.rb +16 -16
  26. data/lib/cosmos/models/note_model.rb +124 -0
  27. data/lib/cosmos/models/ping_model.rb +2 -1
  28. data/lib/cosmos/models/plugin_model.rb +1 -1
  29. data/lib/cosmos/models/process_status_model.rb +1 -1
  30. data/lib/cosmos/models/scope_model.rb +9 -6
  31. data/lib/cosmos/models/settings_model.rb +55 -0
  32. data/lib/cosmos/models/sorted_model.rb +165 -0
  33. data/lib/cosmos/models/target_model.rb +20 -20
  34. data/lib/cosmos/models/tool_config_model.rb +38 -0
  35. data/lib/cosmos/models/tool_model.rb +1 -1
  36. data/lib/cosmos/models/widget_model.rb +1 -1
  37. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  38. data/lib/cosmos/script/calendar.rb +26 -15
  39. data/lib/cosmos/system/system.rb +2 -1
  40. data/lib/cosmos/top_level.rb +5 -1
  41. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  42. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  43. data/lib/cosmos/topics/command_decom_topic.rb +31 -1
  44. data/lib/cosmos/topics/command_topic.rb +6 -4
  45. data/lib/cosmos/topics/interface_topic.rb +8 -8
  46. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  47. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  48. data/lib/cosmos/topics/router_topic.rb +9 -9
  49. data/lib/cosmos/topics/telemetry_decom_topic.rb +1 -1
  50. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  51. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  52. data/lib/cosmos/topics/topic.rb +21 -16
  53. data/lib/cosmos/utilities/logger.rb +3 -3
  54. data/lib/cosmos/utilities/metric.rb +32 -26
  55. data/lib/cosmos/utilities/s3.rb +1 -1
  56. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  57. data/lib/cosmos/utilities/store.rb +1 -0
  58. data/lib/cosmos/utilities/store_autoload.rb +27 -126
  59. data/lib/cosmos/version.rb +5 -5
  60. metadata +7 -4
  61. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -0,0 +1,55 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # This program may also be used under the terms of a commercial or
17
+ # enterprise edition license of COSMOS if purchased from the
18
+ # copyright holder
19
+
20
+ require 'cosmos/models/model'
21
+
22
+ module Cosmos
23
+ class SettingsModel < Model
24
+ PRIMARY_KEY = 'cosmos__settings'
25
+
26
+ # NOTE: The following three class methods are used by the ModelController
27
+ # and are reimplemented to enable various Model class methods to work
28
+ def self.get(name:, scope: nil)
29
+ super(PRIMARY_KEY, name: name)
30
+ end
31
+
32
+ def self.names(scope: nil)
33
+ super(PRIMARY_KEY)
34
+ end
35
+
36
+ def self.all(scope: nil)
37
+ super(PRIMARY_KEY)
38
+ end
39
+ # END NOTE
40
+
41
+ def initialize(name:, scope: nil, data:)
42
+ super(PRIMARY_KEY, name: name, scope: scope)
43
+ @data = data
44
+ end
45
+
46
+ # @return [Hash] JSON encoding of this model
47
+ def as_json
48
+ {
49
+ 'name' => @name,
50
+ 'data' => @data,
51
+ 'updated_at' => @updated_at
52
+ }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,165 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # This program may also be used under the terms of a commercial or
17
+ # enterprise edition license of COSMOS if purchased from the
18
+ # copyright holder
19
+
20
+ # https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
21
+ # https://redis.io/docs/manual/data-types/data-types-tutorial/#sorted-sets
22
+
23
+ require 'cosmos/models/model'
24
+ require 'cosmos/topics/calendar_topic'
25
+
26
+ module Cosmos
27
+ # Put these under the Cosmos module so they are easily accessed in the controller as
28
+ # Cosmos::SortedError vs Cosmos::SortedModel::Error
29
+ class SortedError < StandardError; end
30
+ class SortedInputError < SortedError; end
31
+ class SortedOverlapError < SortedError; end
32
+
33
+ class SortedModel < Model
34
+ SORTED_TYPE = 'sorted'.freeze # To be overriden by base class
35
+ PRIMARY_KEY = '__SORTED'.freeze # To be overriden by base class
36
+
37
+ # MUST be overriden by any subclasses
38
+ def self.pk(scope)
39
+ "#{scope}#{PRIMARY_KEY}"
40
+ end
41
+
42
+ # @return [String|nil] String of the saved json or nil if start not found
43
+ def self.get(start:, scope:)
44
+ result = Store.zrangebyscore(self.pk(scope), start, start)
45
+ return JSON.parse(result[0]) unless result.empty?
46
+ nil
47
+ end
48
+
49
+ # @return [Array<Hash>] Array up to the limit of the models (as Hash objects) stored under the primary key
50
+ def self.all(scope:, limit: 100)
51
+ result = Store.zrevrangebyscore(self.pk(scope), '+inf', '-inf', limit: [0, limit])
52
+ result.map { |item| JSON.parse(item) }
53
+ end
54
+
55
+ # @return [String|nil] json or nil if metadata empty
56
+ def self.get_current_value(scope:)
57
+ start = Time.now.to_i
58
+ array = Store.zrevrangebyscore(self.pk(scope), start, '-inf', limit: [0, 1])
59
+ return nil if array.empty?
60
+ return array[0]
61
+ end
62
+
63
+ # @param start [Integer] Start time to return values (inclusive)
64
+ # @param stop [Integer] Stop time to return values (inclusive)
65
+ # @return [Array|nil] Array up to 100 of this model or empty array
66
+ def self.range(start:, stop:, scope:, limit: 100)
67
+ if start > stop
68
+ raise SortedInputError.new "start: #{start} must be before stop: #{stop}"
69
+ end
70
+ result = Store.zrangebyscore(self.pk(scope), start, stop, limit: [0, limit])
71
+ result.map { |item| JSON.parse(item) }
72
+ end
73
+
74
+ # @return [Integer] count of the members stored under the primary key
75
+ def self.count(scope:)
76
+ Store.zcard(self.pk(scope))
77
+ end
78
+
79
+ # Remove member from a sorted set
80
+ # @return [Integer] count of the members removed, 0 if not found
81
+ def self.destroy(scope:, start:)
82
+ Store.zremrangebyscore(self.pk(scope), start, start)
83
+ end
84
+
85
+ # Remove members from min to max of the sorted set.
86
+ # @return [Integer] count of the members removed
87
+ def self.range_destroy(scope:, start:, stop:)
88
+ Store.zremrangebyscore(self.pk(scope), start, stop)
89
+ end
90
+
91
+ attr_reader :start
92
+
93
+ # @param [Integer] start - start used to store data
94
+ # @param [String] scope - Cosmos scope to track event to
95
+ # @param [Anything] kwargs - Any kwargs to store in the JSON
96
+ def initialize(start:, scope:, type: SORTED_TYPE, **kwargs)
97
+ # Name becomes the start in the base class
98
+ super(self.class.pk(scope), name: start.to_s, scope: scope, **kwargs)
99
+ @type = type # For the as_json, from_json round trip
100
+ @start = start
101
+ end
102
+
103
+ # start MUST be a positive integer
104
+ def validate_start(update: false)
105
+ unless @start.is_a?(Integer)
106
+ raise SortedInputError.new "start must be integer: #{@start}"
107
+ end
108
+ if @start.to_i < 0
109
+ raise SortedInputError.new "start must be positive: #{@start}"
110
+ end
111
+ if !update and self.class.get(start: @start, scope: @scope)
112
+ raise SortedOverlapError.new "duplicate, existing data at #{@start}"
113
+ end
114
+ @start = @start.to_i
115
+ end
116
+
117
+ # Update the Redis hash at primary_key based on the initial passed start
118
+ # The member is set to the JSON generated via calling as_json
119
+ def create(update: false)
120
+ validate_start(update: update)
121
+ @updated_at = Time.now.to_nsec_from_epoch
122
+ Store.zadd(@primary_key, @start, JSON.generate(as_json()))
123
+ if update
124
+ notify(kind: 'updated')
125
+ else
126
+ notify(kind: 'created')
127
+ end
128
+ end
129
+
130
+ # Update the Redis hash at primary_key
131
+ def update(start:)
132
+ @start = start
133
+ create(update: true)
134
+ end
135
+
136
+ # destroy the activity from the redis database
137
+ def destroy
138
+ self.class.destroy(scope: @scope, start: @start)
139
+ notify(kind: 'deleted')
140
+ end
141
+
142
+ # @return [] update the redis stream / timeline topic that something has changed
143
+ def notify(kind:, extra: nil)
144
+ notification = {
145
+ 'data' => JSON.generate(as_json()),
146
+ 'kind' => kind,
147
+ 'type' => 'calendar',
148
+ }
149
+ notification['extra'] = extra unless extra.nil?
150
+ begin
151
+ CalendarTopic.write_entry(notification, scope: @scope)
152
+ rescue StandardError => e
153
+ raise SortedError.new "Failed to write to stream: #{notification}, #{e}"
154
+ end
155
+ end
156
+
157
+ # @return [Hash] JSON encoding of this model
158
+ def as_json
159
+ { **super(),
160
+ 'start' => @start,
161
+ 'type' => SORTED_TYPE,
162
+ }
163
+ end
164
+ end
165
+ end
@@ -208,7 +208,7 @@ module Cosmos
208
208
  tlm_log_cycle_time: tlm_log_cycle_time, tlm_log_cycle_size: tlm_log_cycle_size,
209
209
  tlm_log_retain_time: tlm_log_retain_time,
210
210
  tlm_decom_log_cycle_time: tlm_decom_log_cycle_time, tlm_decom_log_cycle_size: tlm_decom_log_cycle_size,
211
- tlm_decom_log_retain_time: tlm_decom_log_retain_time,
211
+ tlm_decom_log_retain_time: tlm_decom_log_retain_time,
212
212
  reduced_minute_log_retain_time: reduced_minute_log_retain_time,
213
213
  reduced_hour_log_retain_time: reduced_hour_log_retain_time, reduced_day_log_retain_time: reduced_day_log_retain_time,
214
214
  cleanup_poll_time: cleanup_poll_time, needs_dependencies: needs_dependencies,
@@ -269,7 +269,7 @@ module Cosmos
269
269
  'tlm_decom_log_retain_time' => @tlm_decom_log_retain_time,
270
270
  'reduced_minute_log_retain_time' => @reduced_minute_log_retain_time,
271
271
  'reduced_hour_log_retain_time' => @reduced_hour_log_retain_time,
272
- 'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
272
+ 'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
273
273
  'cleanup_poll_time' => @cleanup_poll_time,
274
274
  'needs_dependencies' => @needs_dependencies,
275
275
  }
@@ -301,7 +301,7 @@ module Cosmos
301
301
  when 'CMD_DECOM_LOG_RETAIN_TIME'
302
302
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for cmd decom log files in seconds - nil = Forever>")
303
303
  @cmd_decom_log_retain_time = ConfigParser.handle_nil(parameters[0])
304
- @cmd_decom_log_retain_time = @cmd_decom_log_retain_time.to_i if @cmd_decom_log_retain_time
304
+ @cmd_decom_log_retain_time = @cmd_decom_log_retain_time.to_i if @cmd_decom_log_retain_time
305
305
  when 'TLM_LOG_CYCLE_TIME'
306
306
  parser.verify_num_parameters(1, 1, "#{keyword} <Maximum time between files in seconds>")
307
307
  @tlm_log_cycle_time = parameters[0].to_i
@@ -311,7 +311,7 @@ module Cosmos
311
311
  when 'TLM_LOG_RETAIN_TIME'
312
312
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for tlm log files in seconds - nil = Forever>")
313
313
  @tlm_log_retain_time = ConfigParser.handle_nil(parameters[0])
314
- @tlm_log_retain_time = @tlm_log_retain_time.to_i if @tlm_log_retain_time
314
+ @tlm_log_retain_time = @tlm_log_retain_time.to_i if @tlm_log_retain_time
315
315
  when 'TLM_DECOM_LOG_CYCLE_TIME'
316
316
  parser.verify_num_parameters(1, 1, "#{keyword} <Maximum time between files in seconds>")
317
317
  @tlm_decom_log_cycle_time = parameters[0].to_i
@@ -333,22 +333,22 @@ module Cosmos
333
333
  when 'REDUCED_DAY_LOG_RETAIN_TIME'
334
334
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for reduced day log files in seconds - nil = Forever>")
335
335
  @reduced_day_log_retain_time = ConfigParser.handle_nil(parameters[0])
336
- @reduced_day_log_retain_time = @reduced_day_log_retain_time.to_i if @reduced_day_log_retain_time
336
+ @reduced_day_log_retain_time = @reduced_day_log_retain_time.to_i if @reduced_day_log_retain_time
337
337
  when 'LOG_RETAIN_TIME'
338
338
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for all log files in seconds - nil = Forever>")
339
339
  log_retain_time = ConfigParser.handle_nil(parameters[0])
340
- if log_retain_time
340
+ if log_retain_time
341
341
  @cmd_log_retain_time = log_retain_time.to_i
342
- @cmd_decom_log_retain_time = log_retain_time.to_i
342
+ @cmd_decom_log_retain_time = log_retain_time.to_i
343
343
  @tlm_log_retain_time = log_retain_time.to_i
344
344
  @tlm_decom_log_retain_time = log_retain_time.to_i
345
- end
345
+ end
346
346
  when 'REDUCED_LOG_RETAIN_TIME'
347
347
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for all reduced log files in seconds - nil = Forever>")
348
348
  reduced_log_retain_time = ConfigParser.handle_nil(parameters[0])
349
- if reduced_log_retain_time
349
+ if reduced_log_retain_time
350
350
  @reduced_minute_log_retain_time = reduced_log_retain_time.to_i
351
- @reduced_hour_log_retain_time = reduced_log_retain_time.to_i
351
+ @reduced_hour_log_retain_time = reduced_log_retain_time.to_i
352
352
  @reduced_day_log_retain_time = reduced_log_retain_time.to_i
353
353
  end
354
354
  when 'CLEANUP_POLL_TIME'
@@ -380,7 +380,7 @@ module Cosmos
380
380
  data = File.read(filename, mode: "rb")
381
381
  begin
382
382
  Cosmos.set_working_dir(File.dirname(filename)) do
383
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
383
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
384
384
  end
385
385
  rescue => error
386
386
  raise "ERB error parsing: #{filename}: #{error.formatted}"
@@ -413,12 +413,12 @@ module Cosmos
413
413
  Store.hdel("#{@scope}__limits_groups", group)
414
414
  end
415
415
  self.class.packets(@name, type: :CMD, scope: @scope).each do |packet|
416
- Store.del("#{@scope}__COMMAND__{#{@name}}__#{packet['packet_name']}")
417
- Store.del("#{@scope}__DECOMCMD__{#{@name}}__#{packet['packet_name']}")
416
+ Topic.del("#{@scope}__COMMAND__{#{@name}}__#{packet['packet_name']}")
417
+ Topic.del("#{@scope}__DECOMCMD__{#{@name}}__#{packet['packet_name']}")
418
418
  end
419
419
  self.class.packets(@name, scope: @scope).each do |packet|
420
- Store.del("#{@scope}__TELEMETRY__{#{@name}}__#{packet['packet_name']}")
421
- Store.del("#{@scope}__DECOM__{#{@name}}__#{packet['packet_name']}")
420
+ Topic.del("#{@scope}__TELEMETRY__{#{@name}}__#{packet['packet_name']}")
421
+ Topic.del("#{@scope}__DECOM__{#{@name}}__#{packet['packet_name']}")
422
422
  CvtModel.del(target_name: @name, packet_name: packet['packet_name'], scope: @scope)
423
423
  LimitsEventTopic.delete(@name, packet['packet_name'], scope: @scope)
424
424
  end
@@ -455,7 +455,7 @@ module Cosmos
455
455
 
456
456
  begin
457
457
  Cosmos.set_working_dir(File.dirname(path)) do
458
- return ERB.new(File.read(path)).result(b)
458
+ return ERB.new(File.read(path), trim_mode: "-").result(b)
459
459
  end
460
460
  rescue => error
461
461
  raise "ERB error parsing: #{path}: #{error.formatted}"
@@ -585,10 +585,10 @@ module Cosmos
585
585
  # No telemetry packets for this target
586
586
  end
587
587
  # It's ok to call initialize_streams with an empty array
588
- Store.initialize_streams(command_topic_list)
589
- Store.initialize_streams(decom_command_topic_list)
590
- Store.initialize_streams(packet_topic_list)
591
- Store.initialize_streams(decom_topic_list)
588
+ Topic.initialize_streams(command_topic_list)
589
+ Topic.initialize_streams(decom_command_topic_list)
590
+ Topic.initialize_streams(packet_topic_list)
591
+ Topic.initialize_streams(decom_topic_list)
592
592
 
593
593
  unless command_topic_list.empty?
594
594
  # CommandLog Microservice
@@ -0,0 +1,38 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # This program may also be used under the terms of a commercial or
17
+ # enterprise edition license of COSMOS if purchased from the
18
+ # copyright holder
19
+
20
+ module Cosmos
21
+ class ToolConfigModel
22
+ def self.list_configs(tool, scope: $cosmos_scope)
23
+ Store.hkeys("#{scope}__config__#{tool}")
24
+ end
25
+
26
+ def self.load_config(tool, name, scope: $cosmos_scope)
27
+ Store.hget("#{scope}__config__#{tool}", name)
28
+ end
29
+
30
+ def self.save_config(tool, name, data, scope: $cosmos_scope)
31
+ Store.hset("#{scope}__config__#{tool}", name, data)
32
+ end
33
+
34
+ def self.delete_config(tool, name, scope: $cosmos_scope)
35
+ Store.hdel("#{scope}__config__#{tool}", name)
36
+ end
37
+ end
38
+ end
@@ -231,7 +231,7 @@ module Cosmos
231
231
 
232
232
  # Load tool files
233
233
  data = File.read(filename, mode: "rb")
234
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
234
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
235
235
  rubys3_client.put_object(bucket: 'tools', content_type: content_type, cache_control: cache_control, key: key, body: data)
236
236
  end
237
237
  end
@@ -121,7 +121,7 @@ module Cosmos
121
121
  # Load widget file
122
122
  data = File.read(filename, mode: "rb")
123
123
  Cosmos.set_working_dir(File.dirname(filename)) do
124
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
124
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
125
125
  end
126
126
  # TODO: support widgets that aren't just a single js file (and its associated map file)
127
127
  rubys3_client.put_object(bucket: 'tools', content_type: 'application/javascript', cache_control: cache_control, key: @s3_key, body: data)
@@ -39,7 +39,7 @@ module Cosmos
39
39
  end
40
40
 
41
41
  def convert_microservice_to_process_definition(microservice_name, microservice_config)
42
- process_definition = ["ruby", "plugin_microservice.rb", microservice_name]
42
+ process_definition = ["ruby", "plugin_microservice.rb"]
43
43
  work_dir = "/cosmos/lib/cosmos/microservices"
44
44
  env = microservice_config["env"].dup
45
45
  if microservice_config["needs_dependencies"]
@@ -47,6 +47,7 @@ module Cosmos
47
47
  else
48
48
  env['GEM_HOME'] = nil
49
49
  end
50
+ env['COSMOS_MICROSERVICE_NAME'] = microservice_name
50
51
  container = microservice_config["container"]
51
52
  scope = microservice_name.split("__")[0]
52
53
  return process_definition, work_dir, env, scope, container
@@ -18,6 +18,7 @@
18
18
  # copyright holder
19
19
 
20
20
  require 'cosmos/script/extract'
21
+ require 'time'
21
22
 
22
23
  module Cosmos
23
24
  module Script
@@ -25,38 +26,48 @@ module Cosmos
25
26
 
26
27
  private
27
28
 
28
- # Sets the metadata value for a target
29
+ # Gets the current metadata
29
30
  #
30
- # @param target [String] Target to set metadata on
31
31
  # @return The result of the method call.
32
- def get_metadata(target)
33
- endpoint = "/cosmos-api/metadata/_get/#{target}"
34
- response = $api_server.request('get', endpoint)
32
+ def get_metadata()
33
+ response = $api_server.request('get', "/cosmos-api/metadata/latest")
35
34
  return nil if response.nil? || response.code != 200
36
35
  return JSON.parse(response.body)
37
36
  end
38
37
 
39
- # Sets the metadata value for a target
38
+ # Sets the metadata
40
39
  #
41
- # @param target [String] Target to set metadata on
42
40
  # @param metadata [Hash<Symbol, Variable>] A hash of metadata
43
- # @param color [String] Events color to show on Calendar tool
44
- # @param start (optional) [String] Metadata time value if nil will default to current time
41
+ # @param color [String] Events color to show on Calendar tool, if nil will be blue
42
+ # @param start [Time] Metadata time value, if nil will be current time
45
43
  # @return The result of the method call.
46
- def set_metadata(target, metadata, color: nil, start: nil)
44
+ def set_metadata(metadata, color: nil, start: nil)
47
45
  color = color.nil? ? '#003784' : color
48
- data = {:color => color, :metadata => metadata, :target => target}
49
- data[:start] = start unless start.nil?
46
+ data = { color: color, metadata: metadata }
47
+ data[:start] = start.iso8601 unless start.nil?
50
48
  response = $api_server.request('post', '/cosmos-api/metadata', data: data, json: true)
51
49
  return nil if response.nil? || response.code != 201
52
50
  return JSON.parse(response.body)
53
51
  end
54
52
 
55
- # Requests the metadata from the user for a target
53
+ # Updates the metadata
56
54
  #
57
- def input_metadata(*args, **kwargs)
58
- rasie StandardError "can only be used in script-runner"
55
+ # @param start [Integer] Metadata time value as integer seconds from epoch
56
+ # @param metadata [Hash<Symbol, Variable>] A hash of metadata
57
+ # @param color [String] Events color to show on Calendar tool, if nil will be blue
58
+ # @return The result of the method call.
59
+ def update_metadata(start, metadata, color: nil)
60
+ color = color.nil? ? '#003784' : color
61
+ data = { :color => color, :metadata => metadata }
62
+ data[:start] = Time.at(start).iso8601
63
+ response = $api_server.request('put', "/cosmos-api/metadata/#{start}", data: data, json: true)
64
+ return nil if response.nil? || response.code != 201
65
+ return JSON.parse(response.body)
59
66
  end
60
67
 
68
+ # Requests the metadata from the user for a target
69
+ def input_metadata(*args, **kwargs)
70
+ raise StandardError "can only be used in Script Runner"
71
+ end
61
72
  end
62
73
  end
@@ -26,6 +26,7 @@ require 'cosmos/packets/limits'
26
26
  require 'cosmos/system/target'
27
27
  require 'cosmos/utilities/s3'
28
28
  require 'cosmos/utilities/zip'
29
+ require 'cosmos/models/scope_model'
29
30
  require 'thread'
30
31
  require 'fileutils'
31
32
 
@@ -54,7 +55,7 @@ module Cosmos
54
55
 
55
56
  # @return [Symbol] The current limits_set of the system returned from Redis
56
57
  def self.limits_set
57
- Store.hget("#{$cosmos_scope}__cosmos_system", 'limits_set').intern
58
+ ScopeModel.limits_set(scope: $cosmos_scope)
58
59
  end
59
60
 
60
61
  def self.setup_targets(target_names, base_dir, scope:)
@@ -37,9 +37,13 @@ class HazardousError < StandardError
37
37
  attr_accessor :cmd_name
38
38
  attr_accessor :cmd_params
39
39
  attr_accessor :hazardous_description
40
+ attr_accessor :formatted # formatted command for use in resending original
40
41
 
41
42
  def to_s
42
- "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous due to #{hazardous_description}"
43
+ string = "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous"
44
+ string << "due to '#{hazardous_description}'" if hazardous_description
45
+ # Pass along the original formatted command so it can be resent
46
+ string << ".\n#{formatted}"
43
47
  end
44
48
  end
45
49
 
@@ -26,7 +26,7 @@ module Cosmos
26
26
  # Notify to the topic
27
27
  #
28
28
  # ```json
29
- # {
29
+ # {
30
30
  # "kind" => "created",
31
31
  # "type" => "trigger",
32
32
  # "data" => {
@@ -46,7 +46,7 @@ module Cosmos
46
46
  # }
47
47
  # ```
48
48
  def self.write_notification(notification, scope:)
49
- Store.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
49
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
50
50
  end
51
51
  end
52
52
  end
@@ -38,7 +38,7 @@ module Cosmos
38
38
  # }
39
39
  # ```
40
40
  def self.write_entry(entry, scope:)
41
- Store.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
41
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
42
42
  end
43
43
  end
44
44
  end
@@ -40,7 +40,37 @@ module Cosmos
40
40
  json_hash[item.name + "__U"] = packet.read_item(item, :WITH_UNITS) if item.units
41
41
  end
42
42
  msg_hash['json_data'] = JSON.generate(json_hash.as_json)
43
- Store.write_topic(topic, msg_hash)
43
+ Topic.write_topic(topic, msg_hash)
44
+ end
45
+
46
+ def self.get_cmd_item(target_name, packet_name, param_name, type: :WITH_UNITS, scope: $cosmos_scope)
47
+ msg_id, msg_hash = Topic.get_newest_message("#{scope}__DECOMCMD__{#{target_name}}__#{packet_name}")
48
+ if msg_id
49
+ # TODO: We now have these reserved items directly on command packets
50
+ # Do we still calculate from msg_hash['time'] or use the times directly?
51
+ #
52
+ # if param_name == 'RECEIVED_TIMESECONDS' || param_name == 'PACKET_TIMESECONDS'
53
+ # Time.from_nsec_from_epoch(msg_hash['time'].to_i).to_f
54
+ # elsif param_name == 'RECEIVED_TIMEFORMATTED' || param_name == 'PACKET_TIMEFORMATTED'
55
+ # Time.from_nsec_from_epoch(msg_hash['time'].to_i).formatted
56
+ if param_name == 'RECEIVED_COUNT'
57
+ msg_hash['received_count'].to_i
58
+ else
59
+ json = msg_hash['json_data']
60
+ hash = JSON.parse(json)
61
+ # Start from the most complex down to the basic raw value
62
+ value = hash["#{param_name}__U"]
63
+ return value if value && type == :WITH_UNITS
64
+
65
+ value = hash["#{param_name}__F"]
66
+ return value if value && (type == :WITH_UNITS || type == :FORMATTED)
67
+
68
+ value = hash["#{param_name}__C"]
69
+ return value if value && (type == :WITH_UNITS || type == :FORMATTED || type == :CONVERTED)
70
+
71
+ return hash[param_name]
72
+ end
73
+ end
44
74
  end
45
75
  end
46
76
  end
@@ -31,17 +31,17 @@ module Cosmos
31
31
  received_count: packet.received_count,
32
32
  stored: packet.stored,
33
33
  buffer: packet.buffer(false) }
34
- Store.write_topic(topic, msg_hash)
34
+ Topic.write_topic(topic, msg_hash)
35
35
  end
36
36
 
37
37
  # @param command [Hash] Command hash structure read to be written to a topic
38
38
  def self.send_command(command, scope:)
39
39
  ack_topic = "{#{scope}__ACKCMD}TARGET__#{command['target_name']}"
40
- Store.update_topic_offsets([ack_topic])
40
+ Topic.update_topic_offsets([ack_topic])
41
41
  # Save the existing cmd_params Hash and JSON generate before writing to the topic
42
42
  cmd_params = command['cmd_params']
43
43
  command['cmd_params'] = JSON.generate(command['cmd_params'].as_json)
44
- cmd_id = Store.write_topic("{#{scope}__CMD}TARGET__#{command['target_name']}", command, '*', 100)
44
+ cmd_id = Topic.write_topic("{#{scope}__CMD}TARGET__#{command['target_name']}", command, '*', 100)
45
45
  # TODO: This timeout is fine for most but can we get the write_timeout from the interface here?
46
46
  time = Time.now
47
47
  while (Time.now - time) < COMMAND_ACK_TIMEOUT_S
@@ -66,7 +66,7 @@ module Cosmos
66
66
  ###########################################################################
67
67
 
68
68
  def self.raise_hazardous_error(msg_hash, target_name, cmd_name, cmd_params)
69
- _, description, _ = msg_hash["result"].split("\n")
69
+ _, description, formatted = msg_hash["result"].split("\n")
70
70
  # Create and populate a new HazardousError and raise it up
71
71
  # The _cmd method in script/commands.rb rescues this and calls prompt_for_hazardous
72
72
  error = HazardousError.new
@@ -74,6 +74,8 @@ module Cosmos
74
74
  error.cmd_name = cmd_name
75
75
  error.cmd_params = cmd_params
76
76
  error.hazardous_description = description
77
+ error.formatted = formatted
78
+
77
79
  # No Logger.info because the error is already logged by the Logger.info "Ack Received ...
78
80
  raise error
79
81
  end