openc3 6.1.0 → 6.2.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/data/config/microservice.yaml +5 -0
- data/data/config/target.yaml +1 -1
- data/data/config/widgets.yaml +8 -4
- data/lib/openc3/api/cmd_api.rb +2 -2
- data/lib/openc3/api/settings_api.rb +3 -1
- data/lib/openc3/conversions/generic_conversion.rb +2 -2
- data/lib/openc3/interfaces/file_interface.rb +3 -4
- data/lib/openc3/interfaces/interface.rb +18 -3
- data/lib/openc3/interfaces/stream_interface.rb +2 -2
- data/lib/openc3/microservices/cleanup_microservice.rb +42 -35
- data/lib/openc3/microservices/decom_microservice.rb +75 -13
- data/lib/openc3/microservices/interface_microservice.rb +14 -3
- data/lib/openc3/microservices/log_microservice.rb +5 -1
- data/lib/openc3/microservices/microservice.rb +22 -15
- data/lib/openc3/microservices/multi_microservice.rb +25 -29
- data/lib/openc3/microservices/periodic_microservice.rb +8 -2
- data/lib/openc3/microservices/reducer_microservice.rb +5 -1
- data/lib/openc3/microservices/router_microservice.rb +5 -1
- data/lib/openc3/microservices/scope_cleanup_microservice.rb +12 -9
- data/lib/openc3/microservices/text_log_microservice.rb +5 -1
- data/lib/openc3/models/microservice_model.rb +8 -0
- data/lib/openc3/models/news_model.rb +6 -2
- data/lib/openc3/models/plugin_model.rb +2 -2
- data/lib/openc3/models/scope_model.rb +7 -1
- data/lib/openc3/models/target_model.rb +2 -2
- data/lib/openc3/operators/microservice_operator.rb +93 -47
- data/lib/openc3/operators/operator.rb +1 -1
- data/lib/openc3/packets/parsers/packet_item_parser.rb +28 -21
- data/lib/openc3/packets/structure.rb +19 -2
- data/lib/openc3/packets/structure_item.rb +1 -0
- data/lib/openc3/topics/decom_interface_topic.rb +1 -2
- data/lib/openc3/topics/interface_topic.rb +0 -2
- data/lib/openc3/topics/router_topic.rb +0 -2
- data/lib/openc3/utilities/local_mode.rb +1 -1
- data/lib/openc3/utilities/thread_manager.rb +83 -0
- data/lib/openc3/version.rb +5 -5
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_vue/package.json +3 -3
- data/templates/widget/package.json +2 -2
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7afa541be45293ddc3e884af4bc3a3bdff2fb910a8b3fb1fba16f4ba5c825741
|
4
|
+
data.tar.gz: dd6e9ad3e292dfcaaa8c6b1db9807e398f46d162f8b382cca176589be39bbdf3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb2b9b43fd05e466b804648ff255a17687f7620728d4bc646817ab04c36ecb87a2b58a8e5094d8e0488186c8142fc877f4c80ae2335b3dad0173b01bb74a02a7
|
7
|
+
data.tar.gz: 5cd84c02078c957ea4dc1df6c305f60f0f240fce674d4328114b855f70393b4c27fa6cb710aa4375449421b6fe4a0100ee925fefecf8691fbe71af37a1d85245
|
data/data/config/target.yaml
CHANGED
@@ -154,7 +154,7 @@ TARGET:
|
|
154
154
|
parameters:
|
155
155
|
- name: Time
|
156
156
|
required: true
|
157
|
-
description: Number of seconds between runs of the cleanup process (default =
|
157
|
+
description: Number of seconds between runs of the cleanup process (default = 600 = 10 minutes)
|
158
158
|
values: \d+
|
159
159
|
REDUCER_DISABLE:
|
160
160
|
summary: Disables the data reduction microservice for the target
|
data/data/config/widgets.yaml
CHANGED
@@ -850,13 +850,17 @@ Telemetry Widgets:
|
|
850
850
|
required: false
|
851
851
|
description: Radius of the circle (default is 10)
|
852
852
|
values: .*
|
853
|
-
- name:
|
853
|
+
- name: Item Name Display
|
854
854
|
required: false
|
855
|
-
description: Show the full item name (
|
855
|
+
description: Show the full item name, e.g. TGT PKT ITEM (true), no item name (nil or none) or just the item name (false). Default is false.
|
856
856
|
values: .*
|
857
857
|
example: |
|
858
|
-
|
859
|
-
|
858
|
+
HORIZONTAL
|
859
|
+
LIMITSCOLOR INST HEALTH_STATUS TEMP1 CONVERTED 10 NIL # No label
|
860
|
+
LABEL '1st Temp'
|
861
|
+
END
|
862
|
+
LIMITSCOLOR INST HEALTH_STATUS TEMP2 # Default is label with just item name
|
863
|
+
LIMITSCOLOR INST HEALTH_STATUS TEMP3 CONVERTED 20 TRUE # Full TGT/PKT/ITEM label
|
860
864
|
VALUELIMITSBAR:
|
861
865
|
summary: Displays an item VALUE followed by LIMITSBAR
|
862
866
|
parameters:
|
data/lib/openc3/api/cmd_api.rb
CHANGED
@@ -108,7 +108,7 @@ module OpenC3
|
|
108
108
|
# Build a command binary
|
109
109
|
#
|
110
110
|
# @since 5.8.0
|
111
|
-
def build_cmd(*args, range_check: true, raw: false, manual: false, scope: $openc3_scope, token: $openc3_token, **kwargs)
|
111
|
+
def build_cmd(*args, range_check: true, raw: false, manual: false, timeout: 5, scope: $openc3_scope, token: $openc3_token, **kwargs)
|
112
112
|
extract_string_kwargs_to_args(args, kwargs)
|
113
113
|
case args.length
|
114
114
|
when 1
|
@@ -129,7 +129,7 @@ module OpenC3
|
|
129
129
|
cmd_name = cmd_name.upcase
|
130
130
|
cmd_params = cmd_params.transform_keys(&:upcase)
|
131
131
|
authorize(permission: 'cmd_info', target_name: target_name, manual: manual, scope: scope, token: token)
|
132
|
-
DecomInterfaceTopic.build_cmd(target_name, cmd_name, cmd_params, range_check, raw, scope: scope)
|
132
|
+
DecomInterfaceTopic.build_cmd(target_name, cmd_name, cmd_params, range_check, raw, timeout: timeout, scope: scope)
|
133
133
|
end
|
134
134
|
# build_command is DEPRECATED
|
135
135
|
alias build_command build_cmd
|
@@ -91,7 +91,7 @@ module OpenC3
|
|
91
91
|
if response.success?
|
92
92
|
NewsModel.set(response.body)
|
93
93
|
else
|
94
|
-
NewsModel.news_error(response)
|
94
|
+
NewsModel.news_error("Error contacting OpenC3 news feed (status: #{response.status})")
|
95
95
|
end
|
96
96
|
|
97
97
|
# Test code to update the news feed with a dummy message
|
@@ -100,6 +100,8 @@ module OpenC3
|
|
100
100
|
# json.unshift( { date: Time.now.utc.iso8601, title: "News at #{Time.now}", body: "The news feed has been updated at #{Time.now}." })
|
101
101
|
# json.pop if json.length > 5
|
102
102
|
# NewsModel.set(json.to_json)
|
103
|
+
rescue Exception => e
|
104
|
+
NewsModel.news_error("Error contacting OpenC3 news feed. #{e.message})")
|
103
105
|
end
|
104
106
|
end
|
105
107
|
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -55,7 +55,7 @@ module OpenC3
|
|
55
55
|
# (see OpenC3::Conversion#call)
|
56
56
|
def call(value, packet, buffer)
|
57
57
|
myself = packet # For backwards compatibility
|
58
|
-
if
|
58
|
+
if myself # Remove unused variable warning for myself
|
59
59
|
return eval(@code_to_eval)
|
60
60
|
end
|
61
61
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
4
4
|
# All Rights Reserved.
|
5
5
|
#
|
6
6
|
# This program is free software; you can modify and/or redistribute it
|
@@ -72,15 +72,14 @@ module OpenC3
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def connect
|
75
|
-
super()
|
75
|
+
super() # Reset the protocols
|
76
76
|
|
77
77
|
if @telemetry_read_folder
|
78
78
|
@listener = Listen.to(@telemetry_read_folder, force_polling: @polling) do |modified, added, removed|
|
79
79
|
@queue << added if added
|
80
80
|
end
|
81
|
-
@listener.start # starts a listener thread
|
81
|
+
@listener.start # starts a listener thread - does not block
|
82
82
|
end
|
83
|
-
|
84
83
|
@connected = true
|
85
84
|
end
|
86
85
|
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -223,6 +223,21 @@ module OpenC3
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
+
# Called immediately after the interface is connected.
|
227
|
+
# By default this method will run any commands specified by the CONNECT_CMD option
|
228
|
+
def post_connect
|
229
|
+
connect_cmds = @options['CONNECT_CMD']
|
230
|
+
if connect_cmds
|
231
|
+
connect_cmds.each do |log_dont_log, cmd_string|
|
232
|
+
if log_dont_log.upcase == 'DONT_LOG'
|
233
|
+
cmd(cmd_string, log_message: false)
|
234
|
+
else
|
235
|
+
cmd(cmd_string)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
226
241
|
# Indicates if the interface is connected to its target(s) or not. Must be
|
227
242
|
# implemented by a subclass.
|
228
243
|
def connected?
|
@@ -499,9 +514,9 @@ module OpenC3
|
|
499
514
|
def set_option(option_name, option_values)
|
500
515
|
option_name_upcase = option_name.upcase
|
501
516
|
|
502
|
-
# PERIODIC_CMD
|
517
|
+
# CONNECT_CMD and PERIODIC_CMD are special because there could be more than 1
|
503
518
|
# so we store them in an array for processing during connect()
|
504
|
-
if option_name_upcase == 'PERIODIC_CMD'
|
519
|
+
if option_name_upcase == 'PERIODIC_CMD' or option_name_upcase == 'CONNECT_CMD'
|
505
520
|
# OPTION PERIODIC_CMD LOG/DONT_LOG 1.0 "INST COLLECT with TYPE NORMAL"
|
506
521
|
@options[option_name_upcase] ||= []
|
507
522
|
@options[option_name_upcase] << option_values.clone
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -40,7 +40,7 @@ module OpenC3
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def connect
|
43
|
-
super()
|
43
|
+
super() # Reset the protocols
|
44
44
|
@stream.connect if @stream
|
45
45
|
end
|
46
46
|
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -35,44 +35,33 @@ module OpenC3
|
|
35
35
|
@sleeper = Sleeper.new
|
36
36
|
end
|
37
37
|
|
38
|
-
def cleanup(areas,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
# probably less performant, but we can instead delete each item one at a time.
|
56
|
-
# bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
|
57
|
-
slice.each do |item|
|
58
|
-
bucket.delete_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: item)
|
59
|
-
end
|
60
|
-
@logger.debug("Cleanup deleted #{slice.length} log files")
|
61
|
-
@delete_count += slice.length
|
62
|
-
@metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
|
38
|
+
def cleanup(areas, bucket)
|
39
|
+
@state = 'GETTING_OBJECTS'
|
40
|
+
start_time = Time.now
|
41
|
+
areas.each do |prefix, retain_time|
|
42
|
+
next unless retain_time
|
43
|
+
time = start_time - retain_time
|
44
|
+
oldest_list = BucketUtilities.files_between_time(ENV['OPENC3_LOGS_BUCKET'], prefix, nil, time)
|
45
|
+
if oldest_list.length > 0
|
46
|
+
@state = 'DELETING_OBJECTS'
|
47
|
+
oldest_list.each_slice(1000) do |slice|
|
48
|
+
# The delete_objects function utilizes an MD5 hash when verifying the checksums, which is not
|
49
|
+
# FIPS compliant (https://github.com/aws/aws-sdk-ruby/issues/2645).
|
50
|
+
# delete_object does NOT require an MD5 hash and will work on FIPS compliant systems. It is
|
51
|
+
# probably less performant, but we can instead delete each item one at a time.
|
52
|
+
# bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
|
53
|
+
slice.each do |item|
|
54
|
+
bucket.delete_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: item)
|
63
55
|
end
|
56
|
+
@logger.debug("Cleanup deleted #{slice.length} log files")
|
57
|
+
@delete_count += slice.length
|
58
|
+
@metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
|
64
59
|
end
|
65
60
|
end
|
66
|
-
|
67
|
-
@count += 1
|
68
|
-
@metric.set(name: 'cleanup_total', value: @count, type: 'counter')
|
69
|
-
|
70
|
-
@state = 'SLEEPING'
|
71
|
-
break if @sleeper.sleep(cleanup_poll_time)
|
72
61
|
end
|
73
62
|
end
|
74
63
|
|
75
|
-
def
|
64
|
+
def get_areas_and_poll_time
|
76
65
|
split_name = @name.split("__")
|
77
66
|
target_name = split_name[-1]
|
78
67
|
target = TargetModel.get_model(name: target_name, scope: @scope)
|
@@ -86,8 +75,22 @@ module OpenC3
|
|
86
75
|
["#{@scope}/reduced_hour_logs/tlm/#{target_name}", target.reduced_hour_log_retain_time],
|
87
76
|
["#{@scope}/reduced_day_logs/tlm/#{target_name}", target.reduced_day_log_retain_time],
|
88
77
|
]
|
78
|
+
return areas, target.cleanup_poll_time
|
79
|
+
end
|
89
80
|
|
90
|
-
|
81
|
+
def run
|
82
|
+
bucket = Bucket.getClient()
|
83
|
+
while true
|
84
|
+
break if @cancel_thread
|
85
|
+
areas, poll_time = get_areas_and_poll_time()
|
86
|
+
cleanup(areas, bucket)
|
87
|
+
|
88
|
+
@count += 1
|
89
|
+
@metric.set(name: 'cleanup_total', value: @count, type: 'counter')
|
90
|
+
|
91
|
+
@state = 'SLEEPING'
|
92
|
+
break if @sleeper.sleep(poll_time)
|
93
|
+
end
|
91
94
|
end
|
92
95
|
|
93
96
|
def shutdown
|
@@ -97,4 +100,8 @@ module OpenC3
|
|
97
100
|
end
|
98
101
|
end
|
99
102
|
|
100
|
-
|
103
|
+
if __FILE__ == $0
|
104
|
+
OpenC3::CleanupMicroservice.run
|
105
|
+
ThreadManager.instance.shutdown
|
106
|
+
ThreadManager.instance.join
|
107
|
+
end
|
@@ -14,19 +14,75 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'time'
|
24
|
+
require 'thread'
|
24
25
|
require 'openc3/microservices/microservice'
|
25
26
|
require 'openc3/microservices/interface_decom_common'
|
26
27
|
require 'openc3/topics/telemetry_decom_topic'
|
27
28
|
require 'openc3/topics/limits_event_topic'
|
28
29
|
|
29
30
|
module OpenC3
|
31
|
+
class LimitsResponseThread
|
32
|
+
def initialize(microservice_name:, queue:, logger:, metric:, scope:)
|
33
|
+
@microservice_name = microservice_name
|
34
|
+
@queue = queue
|
35
|
+
@logger = logger
|
36
|
+
@metric = metric
|
37
|
+
@scope = scope
|
38
|
+
@count = 0
|
39
|
+
@error_count = 0
|
40
|
+
@metric.set(name: 'limits_response_total', value: @count, type: 'counter')
|
41
|
+
@metric.set(name: 'limits_response_error_total', value: @error_count, type: 'counter')
|
42
|
+
end
|
43
|
+
|
44
|
+
def start
|
45
|
+
@thread = Thread.new do
|
46
|
+
run()
|
47
|
+
rescue Exception => e
|
48
|
+
@logger.error "#{@microservice_name}: Limits Response thread died: #{e.formatted}"
|
49
|
+
raise e
|
50
|
+
end
|
51
|
+
ThreadManager.instance.register(@thread, stop_object: self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
if @thread
|
56
|
+
OpenC3.kill_thread(self, @thread)
|
57
|
+
@thread = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def graceful_kill
|
62
|
+
@queue << [nil, nil, nil]
|
63
|
+
end
|
64
|
+
|
65
|
+
def run
|
66
|
+
while true
|
67
|
+
packet, item, old_limits_state = @queue.pop()
|
68
|
+
break if packet.nil?
|
69
|
+
|
70
|
+
begin
|
71
|
+
item.limits.response.call(packet, item, old_limits_state)
|
72
|
+
rescue Exception => e
|
73
|
+
@error_count += 1
|
74
|
+
@metric.set(name: 'limits_response_error_total', value: @error_count, type: 'counter')
|
75
|
+
@logger.error "#{packet.target_name} #{packet.packet_name} #{item.name} Limits Response Exception!"
|
76
|
+
@logger.error "Called with old_state = #{old_limits_state}, new_state = #{item.limits.state}"
|
77
|
+
@logger.error e.filtered
|
78
|
+
end
|
79
|
+
|
80
|
+
@count += 1
|
81
|
+
@metric.set(name: 'limits_response_total', value: @count, type: 'counter')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
30
86
|
class DecomMicroservice < Microservice
|
31
87
|
include InterfaceDecomCommon
|
32
88
|
LIMITS_STATE_INDEX = { RED_LOW: 0, YELLOW_LOW: 1, YELLOW_HIGH: 2, RED_HIGH: 3, GREEN_LOW: 4, GREEN_HIGH: 5 }
|
@@ -44,9 +100,14 @@ module OpenC3
|
|
44
100
|
@error_count = 0
|
45
101
|
@metric.set(name: 'decom_total', value: @count, type: 'counter')
|
46
102
|
@metric.set(name: 'decom_error_total', value: @error_count, type: 'counter')
|
103
|
+
@limits_response_queue = Queue.new
|
104
|
+
@limits_response_thread = nil
|
47
105
|
end
|
48
106
|
|
49
107
|
def run
|
108
|
+
@limits_response_thread = LimitsResponseThread.new(microservice_name: @name, queue: @limits_response_queue, logger: @logger, metric: @metric, scope: @scope)
|
109
|
+
@limits_response_thread.start()
|
110
|
+
|
50
111
|
setup_microservice_topic()
|
51
112
|
while true
|
52
113
|
break if @cancel_thread
|
@@ -81,6 +142,9 @@ module OpenC3
|
|
81
142
|
@logger.error("Decom error: #{e.formatted}")
|
82
143
|
end
|
83
144
|
end
|
145
|
+
|
146
|
+
@limits_response_thread.stop()
|
147
|
+
@limits_response_thread = nil
|
84
148
|
end
|
85
149
|
|
86
150
|
def decom_packet(_topic, msg_id, msg_hash, _redis)
|
@@ -119,8 +183,9 @@ module OpenC3
|
|
119
183
|
# but that is rescued separately in the limits_change_callback
|
120
184
|
packet.check_limits(System.limits_set)
|
121
185
|
|
122
|
-
# This is what updates the CVT
|
186
|
+
# This is what actually decommutates the packet and updates the CVT
|
123
187
|
TelemetryDecomTopic.write_packet(packet, scope: @scope)
|
188
|
+
|
124
189
|
diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
|
125
190
|
@metric.set(name: 'decom_duration_seconds', value: diff, type: 'gauge', unit: 'seconds')
|
126
191
|
end
|
@@ -179,19 +244,16 @@ module OpenC3
|
|
179
244
|
LimitsEventTopic.write(event, scope: @scope)
|
180
245
|
|
181
246
|
if item.limits.response
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
item.limits.response.call(packet, item, old_limits_state)
|
186
|
-
rescue Exception => e
|
187
|
-
@error = e
|
188
|
-
@logger.error "#{packet.target_name} #{packet.packet_name} #{item.name} Limits Response Exception!"
|
189
|
-
@logger.error "Called with old_state = #{old_limits_state}, new_state = #{item.limits.state}"
|
190
|
-
@logger.error e.filtered
|
191
|
-
end
|
247
|
+
copied_packet = packet.deep_copy
|
248
|
+
copied_item = packet.items[item.name]
|
249
|
+
@limits_response_queue << [copied_packet, copied_item, old_limits_state]
|
192
250
|
end
|
193
251
|
end
|
194
252
|
end
|
195
253
|
end
|
196
254
|
|
197
|
-
|
255
|
+
if __FILE__ == $0
|
256
|
+
OpenC3::DecomMicroservice.run
|
257
|
+
ThreadManager.instance.shutdown
|
258
|
+
ThreadManager.instance.join
|
259
|
+
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -70,14 +70,16 @@ module OpenC3
|
|
70
70
|
@logger.error "#{@interface.name}: Command handler thread died: #{e.formatted}"
|
71
71
|
raise e
|
72
72
|
end
|
73
|
+
ThreadManager.instance.register(@thread, stop_object: self)
|
73
74
|
end
|
74
75
|
|
75
|
-
def stop
|
76
|
+
def stop()
|
76
77
|
OpenC3.kill_thread(self, @thread)
|
77
78
|
end
|
78
79
|
|
79
80
|
def graceful_kill
|
80
81
|
InterfaceTopic.shutdown(@interface, scope: @scope)
|
82
|
+
sleep(0.001) # Allow other threads to run
|
81
83
|
end
|
82
84
|
|
83
85
|
def run
|
@@ -104,6 +106,7 @@ module OpenC3
|
|
104
106
|
@metric.set(name: 'interface_directive_total', value: @directive_count, type: 'counter') if @metric
|
105
107
|
if msg_hash['shutdown']
|
106
108
|
@logger.info "#{@interface.name}: Shutdown requested"
|
109
|
+
InterfaceTopic.clear_topics(InterfaceTopic.topics(@interface, scope: @scope))
|
107
110
|
return
|
108
111
|
end
|
109
112
|
if msg_hash['connect']
|
@@ -344,6 +347,7 @@ module OpenC3
|
|
344
347
|
@logger.error "#{@router.name}: Telemetry handler thread died: #{e.formatted}"
|
345
348
|
raise e
|
346
349
|
end
|
350
|
+
ThreadManager.instance.register(@thread, stop_object: self)
|
347
351
|
end
|
348
352
|
|
349
353
|
def stop
|
@@ -352,6 +356,7 @@ module OpenC3
|
|
352
356
|
|
353
357
|
def graceful_kill
|
354
358
|
RouterTopic.shutdown(@router, scope: @scope)
|
359
|
+
sleep(0.001) # Allow other threads to run
|
355
360
|
end
|
356
361
|
|
357
362
|
def run
|
@@ -367,6 +372,7 @@ module OpenC3
|
|
367
372
|
|
368
373
|
if msg_hash['shutdown']
|
369
374
|
@logger.info "#{@router.name}: Shutdown requested"
|
375
|
+
RouterTopic.clear_topics(RouterTopic.topics(@router, scope: @scope))
|
370
376
|
return
|
371
377
|
end
|
372
378
|
if msg_hash['connect']
|
@@ -726,6 +732,7 @@ module OpenC3
|
|
726
732
|
@logger.info "#{@interface.name}: Connect #{@interface.connection_string}"
|
727
733
|
begin
|
728
734
|
@interface.connect
|
735
|
+
@interface.post_connect
|
729
736
|
rescue Exception => e
|
730
737
|
begin
|
731
738
|
@interface.disconnect # Ensure disconnect is called at least once on a partial connect
|
@@ -808,4 +815,8 @@ module OpenC3
|
|
808
815
|
end
|
809
816
|
end
|
810
817
|
|
811
|
-
|
818
|
+
if __FILE__ == $0
|
819
|
+
OpenC3::InterfaceMicroservice.run
|
820
|
+
ThreadManager.instance.shutdown
|
821
|
+
ThreadManager.instance.join
|
822
|
+
end
|
@@ -30,6 +30,7 @@ OpenC3.require_file 'openc3/utilities/bucket'
|
|
30
30
|
OpenC3.require_file 'openc3/utilities/secrets'
|
31
31
|
OpenC3.require_file 'openc3/utilities/sleeper'
|
32
32
|
OpenC3.require_file 'openc3/utilities/open_telemetry'
|
33
|
+
OpenC3.require_file 'openc3/utilities/thread_manager'
|
33
34
|
OpenC3.require_file 'openc3/models/microservice_model'
|
34
35
|
OpenC3.require_file 'openc3/models/microservice_status_model'
|
35
36
|
OpenC3.require_file 'tmpdir'
|
@@ -49,22 +50,27 @@ module OpenC3
|
|
49
50
|
def self.run(name = nil)
|
50
51
|
name = ENV['OPENC3_MICROSERVICE_NAME'] unless name
|
51
52
|
microservice = self.new(name)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
thread = Thread.new do
|
54
|
+
begin
|
55
|
+
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
|
56
|
+
microservice.state = 'RUNNING'
|
57
|
+
microservice.run
|
58
|
+
microservice.state = 'FINISHED'
|
59
|
+
rescue Exception => e
|
60
|
+
if SystemExit === e or SignalException === e
|
61
|
+
microservice.state = 'KILLED'
|
62
|
+
else
|
63
|
+
microservice.error = e
|
64
|
+
microservice.state = 'DIED_ERROR'
|
65
|
+
Logger.fatal("Microservice #{name} dying from exception\n#{e.formatted}")
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
|
64
69
|
end
|
65
|
-
ensure
|
66
|
-
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
|
67
70
|
end
|
71
|
+
ThreadManager.instance.register(thread, shutdown_object: microservice)
|
72
|
+
ThreadManager.instance.monitor
|
73
|
+
ThreadManager.instance.shutdown
|
68
74
|
end
|
69
75
|
|
70
76
|
def as_json(*a)
|
@@ -192,6 +198,7 @@ module OpenC3
|
|
192
198
|
@logger.error "#{@name} status thread died: #{e.formatted}"
|
193
199
|
raise e
|
194
200
|
end
|
201
|
+
ThreadManager.instance.register(@microservice_status_thread)
|
195
202
|
end
|
196
203
|
end
|
197
204
|
|
@@ -206,7 +213,7 @@ module OpenC3
|
|
206
213
|
@cancel_thread = true
|
207
214
|
@microservice_status_sleeper.cancel if @microservice_status_sleeper
|
208
215
|
MicroserviceStatusModel.set(as_json(:allow_nan => true), scope: @scope)
|
209
|
-
FileUtils.
|
216
|
+
FileUtils.remove_entry_secure(@temp_dir, true)
|
210
217
|
@metric.shutdown
|
211
218
|
@logger.debug("Shutting down microservice complete: #{@name}")
|
212
219
|
@shutdown_complete = true
|
@@ -18,45 +18,41 @@
|
|
18
18
|
|
19
19
|
require 'openc3/microservices/microservice'
|
20
20
|
require 'openc3/topics/topic'
|
21
|
+
require 'openc3/utilities/thread_manager'
|
21
22
|
|
22
23
|
module OpenC3
|
23
24
|
class MultiMicroservice < Microservice
|
24
25
|
def run
|
25
|
-
@threads = []
|
26
26
|
ARGV.each do |microservice_name|
|
27
27
|
microservice_model = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
if microservice_model.enabled
|
29
|
+
thread = Thread.new do
|
30
|
+
cmd_line = microservice_model.cmd.join(' ')
|
31
|
+
split_cmd_line = cmd_line.split(' ')
|
32
|
+
filename = nil
|
33
|
+
split_cmd_line.each do |item|
|
34
|
+
if File.extname(item) == '.rb'
|
35
|
+
filename = item
|
36
|
+
break
|
37
|
+
end
|
36
38
|
end
|
39
|
+
raise "Could not determine class filename from '#{cmd_line}'" unless filename
|
40
|
+
OpenC3.set_working_dir(microservice_model.work_dir) do
|
41
|
+
require File.join(microservice_model.work_dir, filename)
|
42
|
+
end
|
43
|
+
klass = filename.filename_to_class_name.to_class
|
44
|
+
klass.run(microservice_model.name)
|
37
45
|
end
|
38
|
-
|
39
|
-
OpenC3.set_working_dir(microservice_model.work_dir) do
|
40
|
-
require File.join(microservice_model.work_dir, filename)
|
41
|
-
end
|
42
|
-
klass = filename.filename_to_class_name.to_class
|
43
|
-
klass.run(microservice_model.name)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
@threads.each do |thread|
|
47
|
-
thread.join
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def shutdown
|
52
|
-
super()
|
53
|
-
if @threads
|
54
|
-
@threads.each do |thread|
|
55
|
-
thread.join
|
46
|
+
ThreadManager.instance.register(thread)
|
56
47
|
end
|
57
48
|
end
|
49
|
+
ThreadManager.instance.monitor
|
50
|
+
ThreadManager.instance.shutdown
|
58
51
|
end
|
59
52
|
end
|
60
53
|
end
|
61
|
-
|
62
|
-
OpenC3::MultiMicroservice.run
|
54
|
+
if __FILE__ == $0
|
55
|
+
OpenC3::MultiMicroservice.run
|
56
|
+
ThreadManager.instance.shutdown
|
57
|
+
ThreadManager.instance.join
|
58
|
+
end
|