openc3 5.10.1 → 5.11.0
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.
- checksums.yaml +4 -4
- data/Rakefile +3 -2
- data/data/config/target.yaml +9 -0
- data/ext/openc3/ext/packet/packet.c +3 -0
- data/ext/openc3/ext/reducer_microservice/extconf.rb +13 -0
- data/ext/openc3/ext/reducer_microservice/reducer_microservice.c +165 -0
- data/ext/openc3/ext/structure/structure.c +7 -9
- data/lib/openc3/accessors/accessor.rb +53 -3
- data/lib/openc3/accessors/binary_accessor.rb +16 -0
- data/lib/openc3/accessors/cbor_accessor.rb +3 -3
- data/lib/openc3/accessors/form_accessor.rb +78 -0
- data/lib/openc3/accessors/http_accessor.rb +145 -0
- data/lib/openc3/accessors/json_accessor.rb +19 -3
- data/lib/openc3/accessors/xml_accessor.rb +18 -1
- data/lib/openc3/accessors.rb +3 -1
- data/lib/openc3/config/config_parser.rb +7 -5
- data/lib/openc3/config/meta_config_parser.rb +1 -1
- data/lib/openc3/core_ext/string.rb +16 -1
- data/lib/openc3/interfaces/http_client_interface.rb +202 -0
- data/lib/openc3/interfaces/http_server_interface.rb +183 -0
- data/lib/openc3/interfaces/interface.rb +86 -16
- data/lib/openc3/interfaces/mqtt_interface.rb +6 -5
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +11 -11
- data/lib/openc3/interfaces/protocols/cobs_protocol.rb +7 -7
- data/lib/openc3/interfaces/protocols/crc_protocol.rb +7 -7
- data/lib/openc3/interfaces/protocols/length_protocol.rb +6 -6
- data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +9 -5
- data/lib/openc3/interfaces/protocols/protocol.rb +8 -6
- data/lib/openc3/interfaces/protocols/slip_protocol.rb +8 -8
- data/lib/openc3/interfaces/protocols/template_protocol.rb +6 -7
- data/lib/openc3/interfaces/protocols/terminated_protocol.rb +4 -4
- data/lib/openc3/interfaces/simulated_target_interface.rb +2 -0
- data/lib/openc3/interfaces/stream_interface.rb +6 -4
- data/lib/openc3/interfaces/tcpip_server_interface.rb +2 -0
- data/lib/openc3/interfaces/udp_interface.rb +8 -5
- data/lib/openc3/interfaces.rb +2 -0
- data/lib/openc3/logs/buffered_packet_log_writer.rb +6 -7
- data/lib/openc3/logs/log_writer.rb +2 -10
- data/lib/openc3/logs/packet_log_constants.rb +13 -3
- data/lib/openc3/logs/packet_log_reader.rb +35 -98
- data/lib/openc3/logs/packet_log_writer.rb +24 -62
- data/lib/openc3/logs/text_log_writer.rb +32 -6
- data/lib/openc3/microservices/cleanup_microservice.rb +23 -16
- data/lib/openc3/microservices/decom_microservice.rb +8 -20
- data/lib/openc3/microservices/log_microservice.rb +3 -1
- data/lib/openc3/microservices/reaction_microservice.rb +22 -11
- data/lib/openc3/microservices/reducer_microservice.rb +174 -130
- data/lib/openc3/{models/notification_model.rb → microservices/scope_cleanup_microservice.rb} +20 -21
- data/lib/openc3/microservices/text_log_microservice.rb +2 -5
- data/lib/openc3/microservices/timeline_microservice.rb +0 -1
- data/lib/openc3/microservices/trigger_group_microservice.rb +0 -1
- data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +44 -0
- data/lib/openc3/models/microservice_model.rb +1 -1
- data/lib/openc3/models/scope_model.rb +65 -34
- data/lib/openc3/models/target_model.rb +25 -5
- data/lib/openc3/models/tool_model.rb +14 -2
- data/lib/openc3/models/widget_model.rb +1 -1
- data/lib/openc3/packets/json_packet.rb +10 -2
- data/lib/openc3/packets/packet.rb +30 -9
- data/lib/openc3/packets/packet_config.rb +6 -2
- data/lib/openc3/packets/parsers/packet_item_parser.rb +11 -6
- data/lib/openc3/packets/structure.rb +19 -12
- data/lib/openc3/script/storage.rb +1 -1
- data/lib/openc3/script/web_socket_api.rb +17 -14
- data/lib/openc3/topics/telemetry_topic.rb +2 -1
- data/lib/openc3/utilities/bucket_utilities.rb +2 -0
- data/lib/openc3/utilities/cli_generator.rb +1 -1
- data/lib/openc3/utilities/logger.rb +62 -47
- data/lib/openc3/utilities/metric.rb +19 -1
- data/lib/openc3/utilities/sleeper.rb +3 -1
- data/lib/openc3/utilities/throttle.rb +76 -0
- data/lib/openc3/version.rb +6 -6
- data/templates/tool_angular/package.json +20 -20
- data/templates/tool_angular/yarn.lock +112 -106
- data/templates/tool_react/package.json +16 -18
- data/templates/tool_react/yarn.lock +977 -664
- data/templates/tool_svelte/.prettierrc.js +5 -0
- data/templates/tool_svelte/package.json +18 -18
- data/templates/tool_svelte/src/services/cable.js +1 -1
- data/templates/tool_svelte/src/services/openc3-api.js +173 -173
- data/templates/tool_svelte/yarn.lock +767 -665
- data/templates/tool_vue/package.json +10 -10
- data/templates/tool_vue/yarn.lock +225 -43
- data/templates/widget/package.json +10 -10
- data/templates/widget/yarn.lock +223 -46
- metadata +41 -4
- data/lib/openc3/topics/notifications_topic.rb +0 -31
|
@@ -35,26 +35,14 @@ module OpenC3
|
|
|
35
35
|
@sleeper = Sleeper.new
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def
|
|
39
|
-
split_name = @name.split("__")
|
|
40
|
-
target_name = split_name[-1]
|
|
41
|
-
target = TargetModel.get_model(name: target_name, scope: @scope)
|
|
42
|
-
|
|
38
|
+
def cleanup(areas, cleanup_poll_time)
|
|
43
39
|
bucket = Bucket.getClient()
|
|
44
40
|
while true
|
|
45
41
|
break if @cancel_thread
|
|
46
42
|
|
|
47
43
|
@state = 'GETTING_OBJECTS'
|
|
48
44
|
start_time = Time.now
|
|
49
|
-
|
|
50
|
-
["#{@scope}/raw_logs/cmd/#{target_name}", target.cmd_log_retain_time],
|
|
51
|
-
["#{@scope}/decom_logs/cmd/#{target_name}", target.cmd_decom_log_retain_time],
|
|
52
|
-
["#{@scope}/raw_logs/tlm/#{target_name}", target.tlm_log_retain_time],
|
|
53
|
-
["#{@scope}/decom_logs/tlm/#{target_name}", target.tlm_decom_log_retain_time],
|
|
54
|
-
["#{@scope}/reduced_minute_logs/tlm/#{target_name}", target.reduced_minute_log_retain_time],
|
|
55
|
-
["#{@scope}/reduced_hour_logs/tlm/#{target_name}", target.reduced_hour_log_retain_time],
|
|
56
|
-
["#{@scope}/reduced_day_logs/tlm/#{target_name}", target.reduced_day_log_retain_time],
|
|
57
|
-
].each do |prefix, retain_time|
|
|
45
|
+
areas.each do |prefix, retain_time|
|
|
58
46
|
next unless retain_time
|
|
59
47
|
time = start_time - retain_time
|
|
60
48
|
oldest_list = BucketUtilities.files_between_time(ENV['OPENC3_LOGS_BUCKET'], prefix, nil, time)
|
|
@@ -62,7 +50,7 @@ module OpenC3
|
|
|
62
50
|
@state = 'DELETING_OBJECTS'
|
|
63
51
|
oldest_list.each_slice(1000) do |slice|
|
|
64
52
|
bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
|
|
65
|
-
@logger.debug("
|
|
53
|
+
@logger.debug("Cleanup deleted #{slice.length} log files")
|
|
66
54
|
@delete_count += slice.length
|
|
67
55
|
@metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
|
|
68
56
|
end
|
|
@@ -71,11 +59,30 @@ module OpenC3
|
|
|
71
59
|
|
|
72
60
|
@count += 1
|
|
73
61
|
@metric.set(name: 'cleanup_total', value: @count, type: 'counter')
|
|
62
|
+
|
|
74
63
|
@state = 'SLEEPING'
|
|
75
|
-
break if @sleeper.sleep(
|
|
64
|
+
break if @sleeper.sleep(cleanup_poll_time)
|
|
76
65
|
end
|
|
77
66
|
end
|
|
78
67
|
|
|
68
|
+
def run
|
|
69
|
+
split_name = @name.split("__")
|
|
70
|
+
target_name = split_name[-1]
|
|
71
|
+
target = TargetModel.get_model(name: target_name, scope: @scope)
|
|
72
|
+
|
|
73
|
+
areas = [
|
|
74
|
+
["#{@scope}/raw_logs/cmd/#{target_name}", target.cmd_log_retain_time],
|
|
75
|
+
["#{@scope}/decom_logs/cmd/#{target_name}", target.cmd_decom_log_retain_time],
|
|
76
|
+
["#{@scope}/raw_logs/tlm/#{target_name}", target.tlm_log_retain_time],
|
|
77
|
+
["#{@scope}/decom_logs/tlm/#{target_name}", target.tlm_decom_log_retain_time],
|
|
78
|
+
["#{@scope}/reduced_minute_logs/tlm/#{target_name}", target.reduced_minute_log_retain_time],
|
|
79
|
+
["#{@scope}/reduced_hour_logs/tlm/#{target_name}", target.reduced_hour_log_retain_time],
|
|
80
|
+
["#{@scope}/reduced_day_logs/tlm/#{target_name}", target.reduced_day_log_retain_time],
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
cleanup(areas, target.cleanup_poll_time)
|
|
84
|
+
end
|
|
85
|
+
|
|
79
86
|
def shutdown
|
|
80
87
|
@sleeper.cancel
|
|
81
88
|
super()
|
|
@@ -24,8 +24,6 @@ require 'openc3/microservices/microservice'
|
|
|
24
24
|
require 'openc3/microservices/interface_decom_common'
|
|
25
25
|
require 'openc3/topics/telemetry_decom_topic'
|
|
26
26
|
require 'openc3/topics/limits_event_topic'
|
|
27
|
-
require 'openc3/topics/notifications_topic'
|
|
28
|
-
require 'openc3/models/notification_model'
|
|
29
27
|
|
|
30
28
|
module OpenC3
|
|
31
29
|
class DecomMicroservice < Microservice
|
|
@@ -93,8 +91,14 @@ module OpenC3
|
|
|
93
91
|
|
|
94
92
|
packet = System.telemetry.packet(target_name, packet_name)
|
|
95
93
|
packet.stored = ConfigParser.handle_true_false(msg_hash["stored"])
|
|
94
|
+
# Note: Packet time will be recalculated as part of decom so not setting
|
|
96
95
|
packet.received_time = Time.from_nsec_from_epoch(msg_hash["received_time"].to_i)
|
|
97
96
|
packet.received_count = msg_hash["received_count"].to_i
|
|
97
|
+
extra = msg_hash["extra"]
|
|
98
|
+
if extra and extra.length > 0
|
|
99
|
+
extra = JSON.parse(extra, allow_nan: true, create_additions: true)
|
|
100
|
+
packet.extra = extra
|
|
101
|
+
end
|
|
98
102
|
packet.buffer = msg_hash["buffer"]
|
|
99
103
|
packet.check_limits(System.limits_set) # Process all the limits and call the limits_change_callback (as necessary)
|
|
100
104
|
|
|
@@ -128,25 +132,9 @@ module OpenC3
|
|
|
128
132
|
when :BLUE, :GREEN, :GREEN_LOW, :GREEN_HIGH
|
|
129
133
|
@logger.info message
|
|
130
134
|
when :YELLOW, :YELLOW_LOW, :YELLOW_HIGH
|
|
131
|
-
|
|
132
|
-
time: time_nsec,
|
|
133
|
-
severity: "caution",
|
|
134
|
-
url: "/tools/limitsmonitor",
|
|
135
|
-
title: "#{packet.target_name} #{packet.packet_name} #{item.name} #{item.limits.state}",
|
|
136
|
-
body: "#{item.name} is #{item.limits.state}"
|
|
137
|
-
)
|
|
138
|
-
NotificationsTopic.write_notification(notification.as_json(:allow_nan => true), scope: @scope)
|
|
139
|
-
@logger.warn message
|
|
135
|
+
@logger.warn(message, type: Logger::NOTIFICATION)
|
|
140
136
|
when :RED, :RED_LOW, :RED_HIGH
|
|
141
|
-
|
|
142
|
-
time: time_nsec,
|
|
143
|
-
severity: "critical",
|
|
144
|
-
url: "/tools/limitsmonitor",
|
|
145
|
-
title: "#{packet.target_name} #{packet.packet_name} #{item.name} #{item.limits.state}",
|
|
146
|
-
body: "#{item.name} is #{item.limits.state}"
|
|
147
|
-
)
|
|
148
|
-
NotificationsTopic.write_notification(notification.as_json(:allow_nan => true), scope: @scope)
|
|
149
|
-
@logger.error message
|
|
137
|
+
@logger.error(message, type: Logger::ALERT)
|
|
150
138
|
end
|
|
151
139
|
end
|
|
152
140
|
|
|
@@ -110,7 +110,9 @@ module OpenC3
|
|
|
110
110
|
packet_type = :JSON_PACKET
|
|
111
111
|
data_key = "json_data"
|
|
112
112
|
end
|
|
113
|
-
|
|
113
|
+
received_time_nsec_since_epoch = msg_hash["received_time"]
|
|
114
|
+
received_time_nsec_since_epoch = received_time_nsec_since_epoch.to_i if received_time_nsec_since_epoch
|
|
115
|
+
@plws[target_name][rt_or_stored].buffered_write(packet_type, @cmd_or_tlm, target_name, packet_name, msg_hash["time"].to_i, rt_or_stored == :STORED, msg_hash[data_key], nil, topic, msg_id, received_time_nsec_since_epoch: received_time_nsec_since_epoch, extra: msg_hash['extra'])
|
|
114
116
|
rescue => err
|
|
115
117
|
@error = err
|
|
116
118
|
@logger.error("#{@name} error: #{err.formatted}")
|
|
@@ -22,8 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
require 'openc3/microservices/microservice'
|
|
24
24
|
require 'openc3/models/reaction_model'
|
|
25
|
-
require 'openc3/models/notification_model'
|
|
26
|
-
require 'openc3/topics/notifications_topic'
|
|
27
25
|
require 'openc3/models/trigger_model'
|
|
28
26
|
require 'openc3/topics/autonomic_topic'
|
|
29
27
|
require 'openc3/utilities/authentication'
|
|
@@ -305,15 +303,28 @@ module OpenC3
|
|
|
305
303
|
end
|
|
306
304
|
|
|
307
305
|
def run_notify(reaction:, action:)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
306
|
+
message = "ReactionWorker-#{@ident} #{reaction.name} notify action complete, body: #{action['value']}"
|
|
307
|
+
url = "/tools/autonomic/reactions"
|
|
308
|
+
case action['severity'].to_s.upcase
|
|
309
|
+
when 'FATAL'
|
|
310
|
+
@logger.fatal(message, url: url, type: Logger::ALERT)
|
|
311
|
+
when 'ERROR', 'CRITICAL'
|
|
312
|
+
@logger.error(message, url: url, type: Logger::ALERT)
|
|
313
|
+
when 'WARN', 'CAUTION', 'SERIOUS'
|
|
314
|
+
@logger.warn(message, url: url, type: Logger::NOTIFICATION)
|
|
315
|
+
when 'INFO', 'NORMAL', 'STANDBY', 'OFF'
|
|
316
|
+
@logger.info(message, url: url, type: Logger::NOTIFICATION)
|
|
317
|
+
when 'DEBUG'
|
|
318
|
+
level = @logger.level
|
|
319
|
+
begin
|
|
320
|
+
@logger.level = Logger::DEBUG
|
|
321
|
+
@logger.debug(message, url: url, type: Logger::NOTIFICATION)
|
|
322
|
+
ensure
|
|
323
|
+
@logger.level = level
|
|
324
|
+
end
|
|
325
|
+
else
|
|
326
|
+
raise "Unknown severity: #{action['severity']}"
|
|
327
|
+
end
|
|
317
328
|
end
|
|
318
329
|
|
|
319
330
|
def run_command(reaction:, action:)
|
|
@@ -25,8 +25,10 @@ require 'openc3/topics/topic'
|
|
|
25
25
|
require 'openc3/topics/telemetry_reduced_topics'
|
|
26
26
|
require 'openc3/packets/json_packet'
|
|
27
27
|
require 'openc3/utilities/bucket_file_cache'
|
|
28
|
+
require 'openc3/utilities/throttle'
|
|
28
29
|
require 'openc3/models/reducer_model'
|
|
29
30
|
require 'openc3/logs/buffered_packet_log_writer'
|
|
31
|
+
require 'openc3/ext/reducer_microservice' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
|
|
30
32
|
require 'rufus-scheduler'
|
|
31
33
|
|
|
32
34
|
module OpenC3
|
|
@@ -96,6 +98,8 @@ module OpenC3
|
|
|
96
98
|
case option[0].upcase
|
|
97
99
|
when 'BUFFER_DEPTH' # Buffer depth to write in time order
|
|
98
100
|
@buffer_depth = option[1].to_i
|
|
101
|
+
when 'MAX_CPU_UTILIZATION'
|
|
102
|
+
@max_cpu_utilization = Float(option[1])
|
|
99
103
|
else
|
|
100
104
|
@logger.error("Unknown option passed to microservice #{@name}: #{option}")
|
|
101
105
|
end
|
|
@@ -103,6 +107,7 @@ module OpenC3
|
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
@buffer_depth = 10 unless @buffer_depth
|
|
110
|
+
@max_cpu_utilization = 30.0 unless @max_cpu_utilization
|
|
106
111
|
@target_name = name.split('__')[-1]
|
|
107
112
|
@packet_logs = {}
|
|
108
113
|
|
|
@@ -189,12 +194,14 @@ module OpenC3
|
|
|
189
194
|
end
|
|
190
195
|
|
|
191
196
|
def process_file(filename, type, entry_nanoseconds, file_nanoseconds)
|
|
197
|
+
throttle = OpenC3::Throttle.new(@max_cpu_utilization)
|
|
192
198
|
file = BucketFile.new(filename)
|
|
193
199
|
file.retrieve
|
|
194
200
|
unless file.local_path
|
|
195
201
|
@logger.warn("Reducer Warning: #{filename}: Could not be retrieved")
|
|
196
202
|
return
|
|
197
203
|
end
|
|
204
|
+
throttle.throttle_sleep
|
|
198
205
|
|
|
199
206
|
# Determine if we already have a PacketLogWriter created
|
|
200
207
|
_, _, scope, target_name, _, rt_or_stored, _ = File.basename(filename).split('__')
|
|
@@ -217,6 +224,7 @@ module OpenC3
|
|
|
217
224
|
# The lifetime of all these variables is a single file - single target / multiple packets
|
|
218
225
|
reducer_state = {}
|
|
219
226
|
plr = OpenC3::PacketLogReader.new
|
|
227
|
+
throttle.throttle_sleep
|
|
220
228
|
plr.each(file.local_path) do |packet|
|
|
221
229
|
# Check to see if we should start a new log file before processing this packet
|
|
222
230
|
current_time = packet.packet_time.to_nsec_from_epoch
|
|
@@ -239,131 +247,27 @@ module OpenC3
|
|
|
239
247
|
end
|
|
240
248
|
|
|
241
249
|
if type == 'minute'
|
|
242
|
-
|
|
243
|
-
if state.first
|
|
244
|
-
state.raw_values = packet.read_all(:RAW, nil, packet.read_all_names(:RAW)).select { |key, value| value.is_a?(Numeric) }
|
|
245
|
-
state.raw_keys ||= state.raw_values.keys
|
|
246
|
-
state.converted_values = packet.read_all(:CONVERTED, nil, packet.read_all_names(:CONVERTED)).select { |key, value| value.is_a?(Numeric) }
|
|
247
|
-
state.converted_keys ||= state.converted_values.keys
|
|
248
|
-
else
|
|
249
|
-
state.raw_values = packet.read_all(:RAW, nil, state.raw_keys).select { |key, value| value.is_a?(Numeric) }
|
|
250
|
-
state.converted_values = packet.read_all(:CONVERTED, nil, state.converted_keys).select { |key, value| value.is_a?(Numeric) }
|
|
251
|
-
end
|
|
250
|
+
get_min_samples(packet, state)
|
|
252
251
|
else
|
|
253
|
-
|
|
254
|
-
state.entry_samples ||= extract_entry_samples(packet)
|
|
255
|
-
if state.first
|
|
256
|
-
state.raw_max_values = packet.read_all(:RAW, :MAX, packet.read_all_names(:RAW, :MAX))
|
|
257
|
-
state.raw_keys = state.raw_max_values.keys
|
|
258
|
-
state.converted_max_values = packet.read_all(:CONVERTED, :MAX, packet.read_all_names(:CONVERTED, :MAX))
|
|
259
|
-
state.converted_keys = state.converted_max_values.keys
|
|
260
|
-
else
|
|
261
|
-
state.raw_max_values = packet.read_all(:RAW, :MAX, state.raw_keys)
|
|
262
|
-
state.converted_max_values = packet.read_all(:CONVERTED, :MAX, state.converted_keys)
|
|
263
|
-
end
|
|
264
|
-
state.raw_min_values = packet.read_all(:RAW, :MIN, state.raw_keys)
|
|
265
|
-
state.raw_avg_values = packet.read_all(:RAW, :AVG, state.raw_keys)
|
|
266
|
-
state.raw_stddev_values = packet.read_all(:RAW, :STDDEV, state.raw_keys)
|
|
267
|
-
state.converted_min_values = packet.read_all(:CONVERTED, :MIN, state.converted_keys)
|
|
268
|
-
state.converted_avg_values = packet.read_all(:CONVERTED, :AVG, state.converted_keys)
|
|
269
|
-
state.converted_stddev_values = packet.read_all(:CONVERTED, :STDDEV, state.converted_keys)
|
|
252
|
+
get_hour_day_samples(packet, state)
|
|
270
253
|
end
|
|
271
254
|
|
|
272
255
|
reduced = state.reduced
|
|
273
256
|
if type == 'minute'
|
|
274
|
-
|
|
275
|
-
state.raw_values.each do |key, value|
|
|
276
|
-
if value
|
|
277
|
-
reduced["#{key}__VALS"] ||= []
|
|
278
|
-
reduced["#{key}__VALS"] << value
|
|
279
|
-
reduced["#{key}__N"] ||= value
|
|
280
|
-
reduced["#{key}__N"] = value if value < reduced["#{key}__N"]
|
|
281
|
-
reduced["#{key}__X"] ||= value
|
|
282
|
-
reduced["#{key}__X"] = value if value > reduced["#{key}__X"]
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
# Update statistics for this packet's converted values
|
|
287
|
-
state.converted_values.each do |key, value|
|
|
288
|
-
if value
|
|
289
|
-
reduced["#{key}__CVALS"] ||= []
|
|
290
|
-
reduced["#{key}__CVALS"] << value
|
|
291
|
-
reduced["#{key}__CN"] ||= value
|
|
292
|
-
reduced["#{key}__CN"] = value if value < reduced["#{key}__CN"]
|
|
293
|
-
reduced["#{key}__CX"] ||= value
|
|
294
|
-
reduced["#{key}__CX"] = value if value > reduced["#{key}__CX"]
|
|
295
|
-
end
|
|
296
|
-
end
|
|
257
|
+
update_min_stats(reduced, state)
|
|
297
258
|
else
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if value
|
|
301
|
-
max_key = "#{key}__X"
|
|
302
|
-
reduced[max_key] ||= value
|
|
303
|
-
reduced[max_key] = value if value > reduced[max_key]
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
state.raw_min_values.each do |key, value|
|
|
307
|
-
if value
|
|
308
|
-
min_key = "#{key}__N"
|
|
309
|
-
reduced[min_key] ||= value
|
|
310
|
-
reduced[min_key] = value if value < reduced[min_key]
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
state.raw_avg_values.each do |key, value|
|
|
314
|
-
if value
|
|
315
|
-
avg_values_key = "#{key}__AVGVALS"
|
|
316
|
-
reduced[avg_values_key] ||= []
|
|
317
|
-
reduced[avg_values_key] << value
|
|
318
|
-
end
|
|
319
|
-
end
|
|
320
|
-
state.raw_stddev_values.each do |key, value|
|
|
321
|
-
if value
|
|
322
|
-
stddev_values_key = "#{key}__STDDEVVALS"
|
|
323
|
-
reduced[stddev_values_key] ||= []
|
|
324
|
-
reduced[stddev_values_key] << value
|
|
325
|
-
end
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
# Update statistics for this packet's converted values
|
|
329
|
-
state.converted_max_values.each do |key, value|
|
|
330
|
-
if value
|
|
331
|
-
max_key = "#{key}__CX"
|
|
332
|
-
reduced[max_key] ||= value
|
|
333
|
-
reduced[max_key] = value if value > reduced[max_key]
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
state.converted_min_values.each do |key, value|
|
|
337
|
-
if value
|
|
338
|
-
min_key = "#{key}__CN"
|
|
339
|
-
reduced[min_key] ||= value
|
|
340
|
-
reduced[min_key] = value if value < reduced[min_key]
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
state.converted_avg_values.each do |key, value|
|
|
344
|
-
if value
|
|
345
|
-
avg_values_key = "#{key}__CAVGVALS"
|
|
346
|
-
reduced[avg_values_key] ||= []
|
|
347
|
-
reduced[avg_values_key] << value
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
state.converted_stddev_values.each do |key, value|
|
|
351
|
-
if value
|
|
352
|
-
stddev_values_key = "#{key}__CSTDDEVVALS"
|
|
353
|
-
reduced[stddev_values_key] ||= []
|
|
354
|
-
reduced[stddev_values_key] << value
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
reduced["_NUM_SAMPLES__VALS"] ||= []
|
|
359
|
-
reduced["_NUM_SAMPLES__VALS"] << packet.read('_NUM_SAMPLES')
|
|
259
|
+
update_raw_hour_day_stats(reduced, state)
|
|
260
|
+
update_converted_hour_day_stats(packet, reduced, state)
|
|
360
261
|
end
|
|
361
|
-
|
|
362
262
|
state.first = false
|
|
263
|
+
|
|
264
|
+
throttle.throttle_sleep
|
|
363
265
|
end
|
|
364
266
|
file.delete # Remove the local copy
|
|
365
267
|
|
|
366
|
-
write_all_entries(reducer_state, plw, type, target_name, stored)
|
|
268
|
+
write_all_entries(reducer_state, plw, type, target_name, stored, throttle)
|
|
269
|
+
|
|
270
|
+
@logger.debug("Reducer Throttle: #{filename}: total_time: #{Time.now - throttle.reset_time}, sleep_time: #{throttle.total_sleep_time}")
|
|
367
271
|
|
|
368
272
|
@count += 1
|
|
369
273
|
@metric.set(name: 'reducer_total', value: @count, type: 'counter')
|
|
@@ -380,6 +284,140 @@ module OpenC3
|
|
|
380
284
|
false
|
|
381
285
|
end
|
|
382
286
|
|
|
287
|
+
def get_min_samples(packet, state)
|
|
288
|
+
state.entry_samples ||= packet.json_hash.dup # Grab all the samples from the first packet
|
|
289
|
+
if state.first
|
|
290
|
+
state.raw_values = packet.read_all(:RAW, nil, packet.read_all_names(:RAW)).select { |key, value| value.is_a?(Numeric) }
|
|
291
|
+
state.raw_keys ||= state.raw_values.keys
|
|
292
|
+
state.converted_values = packet.read_all(:CONVERTED, nil, packet.read_all_names(:CONVERTED)).select { |key, value| value.is_a?(Numeric) }
|
|
293
|
+
state.converted_keys ||= state.converted_values.keys
|
|
294
|
+
else
|
|
295
|
+
state.raw_values = packet.read_all(:RAW, nil, state.raw_keys).select { |key, value| value.is_a?(Numeric) }
|
|
296
|
+
state.converted_values = packet.read_all(:CONVERTED, nil, state.converted_keys).select { |key, value| value.is_a?(Numeric) }
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def get_hour_day_samples(packet, state)
|
|
301
|
+
# Hour or Day
|
|
302
|
+
state.entry_samples ||= extract_entry_samples(packet)
|
|
303
|
+
if state.first
|
|
304
|
+
state.raw_max_values = packet.read_all(:RAW, :MAX, packet.read_all_names(:RAW, :MAX))
|
|
305
|
+
state.raw_keys = state.raw_max_values.keys
|
|
306
|
+
state.converted_max_values = packet.read_all(:CONVERTED, :MAX, packet.read_all_names(:CONVERTED, :MAX))
|
|
307
|
+
state.converted_keys = state.converted_max_values.keys
|
|
308
|
+
else
|
|
309
|
+
state.raw_max_values = packet.read_all(:RAW, :MAX, state.raw_keys)
|
|
310
|
+
state.converted_max_values = packet.read_all(:CONVERTED, :MAX, state.converted_keys)
|
|
311
|
+
end
|
|
312
|
+
state.raw_min_values = packet.read_all(:RAW, :MIN, state.raw_keys)
|
|
313
|
+
state.raw_avg_values = packet.read_all(:RAW, :AVG, state.raw_keys)
|
|
314
|
+
state.raw_stddev_values = packet.read_all(:RAW, :STDDEV, state.raw_keys)
|
|
315
|
+
state.converted_min_values = packet.read_all(:CONVERTED, :MIN, state.converted_keys)
|
|
316
|
+
state.converted_avg_values = packet.read_all(:CONVERTED, :AVG, state.converted_keys)
|
|
317
|
+
state.converted_stddev_values = packet.read_all(:CONVERTED, :STDDEV, state.converted_keys)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
|
321
|
+
def update_min_stats(reduced, state)
|
|
322
|
+
# Update statistics for this packet's raw values
|
|
323
|
+
state.raw_values.each do |key, value|
|
|
324
|
+
if value
|
|
325
|
+
vals_key = "#{key}__VALS"
|
|
326
|
+
reduced[vals_key] ||= []
|
|
327
|
+
reduced[vals_key] << value
|
|
328
|
+
n_key = "#{key}__N"
|
|
329
|
+
reduced[n_key] ||= value
|
|
330
|
+
reduced[n_key] = value if value < reduced[n_key]
|
|
331
|
+
x_key = "#{key}__X"
|
|
332
|
+
reduced[x_key] ||= value
|
|
333
|
+
reduced[x_key] = value if value > reduced[x_key]
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Update statistics for this packet's converted values
|
|
338
|
+
state.converted_values.each do |key, value|
|
|
339
|
+
if value
|
|
340
|
+
cvals_key = "#{key}__CVALS"
|
|
341
|
+
reduced[cvals_key] ||= []
|
|
342
|
+
reduced[cvals_key] << value
|
|
343
|
+
cn_key = "#{key}__CN"
|
|
344
|
+
reduced[cn_key] ||= value
|
|
345
|
+
reduced[cn_key] = value if value < reduced[cn_key]
|
|
346
|
+
cx_key = "#{key}__CX"
|
|
347
|
+
reduced[cx_key] ||= value
|
|
348
|
+
reduced[cx_key] = value if value > reduced[cx_key]
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def update_raw_hour_day_stats(reduced, state)
|
|
355
|
+
# Update statistics for this packet's raw values
|
|
356
|
+
state.raw_max_values.each do |key, value|
|
|
357
|
+
if value
|
|
358
|
+
max_key = "#{key}__X"
|
|
359
|
+
reduced[max_key] ||= value
|
|
360
|
+
reduced[max_key] = value if value > reduced[max_key]
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
state.raw_min_values.each do |key, value|
|
|
364
|
+
if value
|
|
365
|
+
min_key = "#{key}__N"
|
|
366
|
+
reduced[min_key] ||= value
|
|
367
|
+
reduced[min_key] = value if value < reduced[min_key]
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
state.raw_avg_values.each do |key, value|
|
|
371
|
+
if value
|
|
372
|
+
avg_values_key = "#{key}__AVGVALS"
|
|
373
|
+
reduced[avg_values_key] ||= []
|
|
374
|
+
reduced[avg_values_key] << value
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
state.raw_stddev_values.each do |key, value|
|
|
378
|
+
if value
|
|
379
|
+
stddev_values_key = "#{key}__STDDEVVALS"
|
|
380
|
+
reduced[stddev_values_key] ||= []
|
|
381
|
+
reduced[stddev_values_key] << value
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def update_converted_hour_day_stats(packet, reduced, state)
|
|
387
|
+
# Update statistics for this packet's converted values
|
|
388
|
+
state.converted_max_values.each do |key, value|
|
|
389
|
+
if value
|
|
390
|
+
max_key = "#{key}__CX"
|
|
391
|
+
reduced[max_key] ||= value
|
|
392
|
+
reduced[max_key] = value if value > reduced[max_key]
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
state.converted_min_values.each do |key, value|
|
|
396
|
+
if value
|
|
397
|
+
min_key = "#{key}__CN"
|
|
398
|
+
reduced[min_key] ||= value
|
|
399
|
+
reduced[min_key] = value if value < reduced[min_key]
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
state.converted_avg_values.each do |key, value|
|
|
403
|
+
if value
|
|
404
|
+
avg_values_key = "#{key}__CAVGVALS"
|
|
405
|
+
reduced[avg_values_key] ||= []
|
|
406
|
+
reduced[avg_values_key] << value
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
state.converted_stddev_values.each do |key, value|
|
|
410
|
+
if value
|
|
411
|
+
stddev_values_key = "#{key}__CSTDDEVVALS"
|
|
412
|
+
reduced[stddev_values_key] ||= []
|
|
413
|
+
reduced[stddev_values_key] << value
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
reduced["_NUM_SAMPLES__VALS"] ||= []
|
|
418
|
+
reduced["_NUM_SAMPLES__VALS"] << packet.read('_NUM_SAMPLES')
|
|
419
|
+
end
|
|
420
|
+
|
|
383
421
|
def check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
|
|
384
422
|
plw_first_time_nsec = plw.buffered_first_time_nsec
|
|
385
423
|
if plw_first_time_nsec && ((current_time - plw_first_time_nsec) >= file_nanoseconds)
|
|
@@ -408,9 +446,10 @@ module OpenC3
|
|
|
408
446
|
return state
|
|
409
447
|
end
|
|
410
448
|
|
|
411
|
-
def write_all_entries(reducer_state, plw, type, target_name, stored)
|
|
449
|
+
def write_all_entries(reducer_state, plw, type, target_name, stored, throttle = nil)
|
|
412
450
|
reducer_state.each do |packet_name, state|
|
|
413
451
|
write_entry(state, plw, type, target_name, packet_name, stored)
|
|
452
|
+
throttle.throttle_sleep if throttle
|
|
414
453
|
end
|
|
415
454
|
end
|
|
416
455
|
|
|
@@ -484,26 +523,31 @@ module OpenC3
|
|
|
484
523
|
# Calculate Average
|
|
485
524
|
weighted_sum = 0
|
|
486
525
|
avg = reduced["#{key}#{avgvals_key}"]
|
|
487
|
-
avg
|
|
488
|
-
|
|
526
|
+
if avg
|
|
527
|
+
avg.each_with_index do |val, i|
|
|
528
|
+
weighted_sum += (val * samples[i])
|
|
529
|
+
end
|
|
530
|
+
reduced["#{key}#{avg_key}"] = weighted_sum / samples_sum
|
|
489
531
|
end
|
|
490
|
-
reduced["#{key}#{avg_key}"] = weighted_sum / samples_sum
|
|
491
532
|
|
|
492
533
|
# Do the STDDEV calc last so we can use the previously calculated AVG
|
|
493
534
|
# See https://math.stackexchange.com/questions/1547141/aggregating-standard-deviation-to-a-summary-point
|
|
494
535
|
s2 = 0
|
|
495
|
-
reduced["#{key}#{stddevvals_key}"]
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
536
|
+
stddev = reduced["#{key}#{stddevvals_key}"]
|
|
537
|
+
if stddev
|
|
538
|
+
stddev.each_with_index do |val, i|
|
|
539
|
+
# puts "i:#{i} val:#{val} samples[i]:#{samples[i]} avg[i]:#{avg[i]}"
|
|
540
|
+
s2 += (samples[i] * avg[i]**2 + val**2)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# Note: For very large numbers with very small deviations this sqrt can fail.
|
|
544
|
+
# If so then just set the stddev to 0.
|
|
545
|
+
begin
|
|
546
|
+
reduced["#{key}#{stddev_key}"] =
|
|
547
|
+
Math.sqrt(s2 / samples_sum - reduced["#{key}#{avg_key}"])
|
|
548
|
+
rescue Exception
|
|
549
|
+
reduced["#{key}#{stddev_key}"] = 0.0
|
|
550
|
+
end
|
|
507
551
|
end
|
|
508
552
|
|
|
509
553
|
reduced.delete("#{key}#{avgvals_key}")
|
data/lib/openc3/{models/notification_model.rb → microservices/scope_cleanup_microservice.rb}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
2
|
|
|
3
|
-
# Copyright
|
|
3
|
+
# Copyright 2023 OpenC3, Inc.
|
|
4
4
|
# All Rights Reserved.
|
|
5
5
|
#
|
|
6
6
|
# This program is free software; you can modify and/or redistribute it
|
|
@@ -12,32 +12,31 @@
|
|
|
12
12
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
13
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
14
|
# GNU Affero General Public License for more details.
|
|
15
|
-
|
|
16
|
-
# Modified by OpenC3, Inc.
|
|
17
|
-
# All changes Copyright 2022, OpenC3, Inc.
|
|
18
|
-
# All Rights Reserved
|
|
19
15
|
#
|
|
20
|
-
# This file may also be used under the terms of a commercial license
|
|
16
|
+
# This file may also be used under the terms of a commercial license
|
|
21
17
|
# if purchased from OpenC3, Inc.
|
|
22
18
|
|
|
19
|
+
require 'openc3/models/scope_model'
|
|
20
|
+
require 'openc3/microservices/cleanup_microservice'
|
|
21
|
+
|
|
23
22
|
module OpenC3
|
|
24
|
-
class
|
|
25
|
-
|
|
23
|
+
class ScopeCleanupMicroservice < CleanupMicroservice
|
|
24
|
+
def run
|
|
25
|
+
scope = ScopeModel.get_model(name: @scope, scope: @scope)
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@title = title
|
|
32
|
-
@body = body
|
|
33
|
-
end
|
|
27
|
+
areas = [
|
|
28
|
+
["#{@scope}/text_logs", scope.text_log_retain_time],
|
|
29
|
+
["#{@scope}/tool_logs", scope.tool_log_retain_time],
|
|
30
|
+
]
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
if @scope == 'DEFAULT'
|
|
33
|
+
areas << ["NOSCOPE/text_logs", scope.text_log_retain_time]
|
|
34
|
+
areas << ["NOSCOPE/tool_logs", scope.tool_log_retain_time]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
cleanup(areas, scope.cleanup_poll_time)
|
|
41
38
|
end
|
|
42
39
|
end
|
|
43
40
|
end
|
|
41
|
+
|
|
42
|
+
OpenC3::CleanupMicroservice.run if __FILE__ == $0
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
require 'openc3/microservices/microservice'
|
|
24
24
|
require 'openc3/topics/topic'
|
|
25
|
+
require 'openc3/io/json_rpc'
|
|
25
26
|
|
|
26
27
|
module OpenC3
|
|
27
28
|
class TextLogMicroservice < Microservice
|
|
@@ -77,11 +78,7 @@ module OpenC3
|
|
|
77
78
|
msgid_seconds_from_epoch = msg_id.split('-')[0].to_i / 1000.0
|
|
78
79
|
delta = Time.now.to_f - msgid_seconds_from_epoch
|
|
79
80
|
@metric.set(name: 'text_log_topic_delta_seconds', value: delta, type: 'gauge', unit: 'seconds', help: 'Delta time between data written to stream and text log start')
|
|
80
|
-
|
|
81
|
-
keys = msg_hash.keys
|
|
82
|
-
keys.delete("time")
|
|
83
|
-
entry = keys.reduce("") { |data, key| data + "#{key}: #{msg_hash[key]}\t" }
|
|
84
|
-
@tlws[topic].write(msg_hash["time"].to_i, entry, topic, msg_id)
|
|
81
|
+
@tlws[topic].write(msg_hash["time"].to_i, msg_hash.as_json(allow_nan: true).to_json(allow_nan: true), topic, msg_id)
|
|
85
82
|
@count += 1
|
|
86
83
|
rescue => err
|
|
87
84
|
@error = err
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
require 'openc3/utilities/authentication'
|
|
24
24
|
require 'openc3/microservices/microservice'
|
|
25
25
|
require 'openc3/models/activity_model'
|
|
26
|
-
require 'openc3/models/notification_model'
|
|
27
26
|
require 'openc3/models/timeline_model'
|
|
28
27
|
require 'openc3/topics/timeline_topic'
|
|
29
28
|
|