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
@@ -48,9 +48,11 @@ module OpenC3
|
|
48
48
|
if response.success?
|
49
49
|
NewsModel.set(response.body)
|
50
50
|
else
|
51
|
-
NewsModel.news_error(response)
|
51
|
+
NewsModel.news_error("Error contacting OpenC3 news feed (status: #{response.status})")
|
52
52
|
end
|
53
53
|
end
|
54
|
+
rescue Exception => e
|
55
|
+
NewsModel.news_error("Error contacting OpenC3 news feed. #{e.message})")
|
54
56
|
end
|
55
57
|
|
56
58
|
def run
|
@@ -87,4 +89,8 @@ module OpenC3
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
|
92
|
+
if __FILE__ == $0
|
93
|
+
OpenC3::PeriodicMicroservice.run
|
94
|
+
ThreadManager.instance.shutdown
|
95
|
+
ThreadManager.instance.join
|
96
|
+
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
|
@@ -21,22 +21,25 @@ require 'openc3/microservices/cleanup_microservice'
|
|
21
21
|
|
22
22
|
module OpenC3
|
23
23
|
class ScopeCleanupMicroservice < CleanupMicroservice
|
24
|
-
def
|
24
|
+
def get_areas_and_poll_time
|
25
25
|
scope = ScopeModel.get_model(name: @scope)
|
26
|
-
|
27
26
|
areas = [
|
28
|
-
["#{@scope}/text_logs", scope.text_log_retain_time],
|
29
|
-
["#{@scope}/tool_logs", scope.tool_log_retain_time],
|
27
|
+
["#{@scope}/text_logs/openc3_log_messages", scope.text_log_retain_time],
|
28
|
+
["#{@scope}/tool_logs/sr", scope.tool_log_retain_time],
|
30
29
|
]
|
31
30
|
|
32
31
|
if @scope == 'DEFAULT'
|
33
|
-
areas << ["NOSCOPE/text_logs", scope.text_log_retain_time]
|
34
|
-
areas << ["NOSCOPE/tool_logs", scope.tool_log_retain_time]
|
32
|
+
areas << ["NOSCOPE/text_logs/openc3_log_messages", scope.text_log_retain_time]
|
33
|
+
areas << ["NOSCOPE/tool_logs/sr", scope.tool_log_retain_time]
|
35
34
|
end
|
36
35
|
|
37
|
-
|
36
|
+
return areas, scope.cleanup_poll_time
|
38
37
|
end
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
42
|
-
|
41
|
+
if __FILE__ == $0
|
42
|
+
OpenC3::ScopeCleanupMicroservice.run
|
43
|
+
ThreadManager.instance.shutdown
|
44
|
+
ThreadManager.instance.join
|
45
|
+
end
|
@@ -45,6 +45,7 @@ module OpenC3
|
|
45
45
|
attr_accessor :disable_erb
|
46
46
|
attr_accessor :ignore_changes
|
47
47
|
attr_accessor :shard
|
48
|
+
attr_accessor :enabled
|
48
49
|
|
49
50
|
# NOTE: The following three class methods are used by the ModelController
|
50
51
|
# and are reimplemented to enable various Model class methods to work
|
@@ -107,6 +108,7 @@ module OpenC3
|
|
107
108
|
disable_erb: nil,
|
108
109
|
ignore_changes: nil,
|
109
110
|
shard: 0,
|
111
|
+
enabled: true,
|
110
112
|
scope:
|
111
113
|
)
|
112
114
|
parts = name.split("__")
|
@@ -134,6 +136,8 @@ module OpenC3
|
|
134
136
|
@disable_erb = disable_erb
|
135
137
|
@ignore_changes = ignore_changes
|
136
138
|
@shard = shard.to_i # to_i to handle nil
|
139
|
+
@enabled = enabled
|
140
|
+
@enabled = true if @enabled.nil?
|
137
141
|
@bucket = Bucket.getClient()
|
138
142
|
end
|
139
143
|
|
@@ -158,6 +162,7 @@ module OpenC3
|
|
158
162
|
'disable_erb' => @disable_erb,
|
159
163
|
'ignore_changes' => @ignore_changes,
|
160
164
|
'shard' => @shard,
|
165
|
+
'enabled' => @enabled,
|
161
166
|
}
|
162
167
|
end
|
163
168
|
|
@@ -222,6 +227,9 @@ module OpenC3
|
|
222
227
|
when 'SHARD'
|
223
228
|
parser.verify_num_parameters(1, 1, "#{keyword} <Shard Number Starting from 0>")
|
224
229
|
@shard = Integer(parameters[0])
|
230
|
+
when 'STOPPED'
|
231
|
+
parser.verify_num_parameters(0, 0, "#{keyword}")
|
232
|
+
@enabled = false
|
225
233
|
else
|
226
234
|
raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Microservice: #{keyword} #{parameters.join(" ")}")
|
227
235
|
end
|
@@ -31,8 +31,12 @@ module OpenC3
|
|
31
31
|
Store.get(PRIMARY_KEY)
|
32
32
|
end
|
33
33
|
|
34
|
-
def self.news_error(
|
35
|
-
Store.set(PRIMARY_KEY, [{
|
34
|
+
def self.news_error(message)
|
35
|
+
Store.set(PRIMARY_KEY, [{
|
36
|
+
date: Time.now.utc.iso8601,
|
37
|
+
title: 'News Error',
|
38
|
+
body: message
|
39
|
+
}].to_json)
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
@@ -134,7 +134,7 @@ module OpenC3
|
|
134
134
|
result['existing_plugin_txt_lines'] = existing_plugin_txt_lines if existing_plugin_txt_lines and not process_existing and existing_plugin_txt_lines != result['plugin_txt_lines']
|
135
135
|
return result
|
136
136
|
ensure
|
137
|
-
FileUtils.
|
137
|
+
FileUtils.remove_entry_secure(temp_dir, true)
|
138
138
|
tf.unlink if tf
|
139
139
|
end
|
140
140
|
end
|
@@ -287,7 +287,7 @@ module OpenC3
|
|
287
287
|
plugin_model.destroy unless validate_only
|
288
288
|
raise e
|
289
289
|
ensure
|
290
|
-
FileUtils.
|
290
|
+
FileUtils.remove_entry_secure(temp_dir, true)
|
291
291
|
tf.unlink if tf
|
292
292
|
end
|
293
293
|
return plugin_model.as_json(:allow_nan => true)
|
@@ -95,7 +95,7 @@ module OpenC3
|
|
95
95
|
text_log_cycle_size: 50_000_000,
|
96
96
|
text_log_retain_time: nil,
|
97
97
|
tool_log_retain_time: nil,
|
98
|
-
cleanup_poll_time:
|
98
|
+
cleanup_poll_time: 600,
|
99
99
|
command_authority: false,
|
100
100
|
critical_commanding: "OFF",
|
101
101
|
shard: 0,
|
@@ -129,6 +129,12 @@ module OpenC3
|
|
129
129
|
raise "Invalid scope name: #{@name}" if @name !~ /^[a-zA-Z0-9_-]+$/
|
130
130
|
@name = @name.upcase
|
131
131
|
@scope = @name # Ensure @scope matches @name
|
132
|
+
# Ensure the various cycle and retain times are integers
|
133
|
+
@text_log_cycle_time = @text_log_cycle_time.to_i
|
134
|
+
@text_log_cycle_size = @text_log_cycle_size.to_i
|
135
|
+
@text_log_retain_time = @text_log_retain_time.to_i if @text_log_retain_time
|
136
|
+
@tool_log_retain_time = @tool_log_retain_time.to_i if @tool_log_retain_time
|
137
|
+
@cleanup_poll_time = @cleanup_poll_time.to_i
|
132
138
|
super(update: update, force: force, queued: queued)
|
133
139
|
|
134
140
|
if ENTERPRISE
|
@@ -340,7 +340,7 @@ module OpenC3
|
|
340
340
|
reduced_minute_log_retain_time: nil,
|
341
341
|
reduced_hour_log_retain_time: nil,
|
342
342
|
reduced_day_log_retain_time: nil,
|
343
|
-
cleanup_poll_time:
|
343
|
+
cleanup_poll_time: 600,
|
344
344
|
needs_dependencies: false,
|
345
345
|
target_microservices: {'REDUCER' => [[]]},
|
346
346
|
reducer_disable: false,
|
@@ -619,7 +619,7 @@ module OpenC3
|
|
619
619
|
ConfigTopic.write({ kind: 'created', type: 'target', name: @name, plugin: @plugin }, scope: @scope)
|
620
620
|
end
|
621
621
|
ensure
|
622
|
-
FileUtils.
|
622
|
+
FileUtils.remove_entry_secure(temp_dir, true)
|
623
623
|
end
|
624
624
|
end
|
625
625
|
|
@@ -84,6 +84,96 @@ module OpenC3
|
|
84
84
|
return process_definition, work_dir, env, scope, container
|
85
85
|
end
|
86
86
|
|
87
|
+
# Handle the detection of a new microservice model
|
88
|
+
def handle_new_microservice(microservice_name, microservice_config)
|
89
|
+
parent = microservice_config['parent']
|
90
|
+
enabled = microservice_config['enabled']
|
91
|
+
enabled = true if enabled.nil?
|
92
|
+
scope = microservice_name.split("__")[0]
|
93
|
+
|
94
|
+
if enabled
|
95
|
+
Logger.info("New microservice detected: #{microservice_name}", scope: scope)
|
96
|
+
if parent
|
97
|
+
# Respawn parent if it exists and isn't new
|
98
|
+
if @microservices[parent] and @previous_microservices[parent]
|
99
|
+
@changed_microservices[parent] = @microservices[parent]
|
100
|
+
end
|
101
|
+
else
|
102
|
+
# New process be spawned
|
103
|
+
@new_microservices[microservice_name] = microservice_config
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Handle a change detected in a microservice model
|
109
|
+
def handle_changed_microservice(microservice_name, microservice_config)
|
110
|
+
parent = microservice_config['parent']
|
111
|
+
enabled = microservice_config['enabled']
|
112
|
+
enabled = true if enabled.nil?
|
113
|
+
scope = microservice_name.split("__")[0]
|
114
|
+
previous_parent = @previous_microservices[microservice_name]['parent']
|
115
|
+
previous_enabled = @previous_microservices[microservice_name]["enabled"]
|
116
|
+
previous_enabled = true if previous_enabled.nil?
|
117
|
+
|
118
|
+
Logger.info("Changed microservice detected: #{microservice_name}\nWas: #{@previous_microservices[microservice_name]}\nIs: #{microservice_config}", scope: scope)
|
119
|
+
|
120
|
+
if parent or previous_parent
|
121
|
+
if parent == previous_parent
|
122
|
+
# Same Parent - Respawn parent
|
123
|
+
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
124
|
+
elsif parent and previous_parent
|
125
|
+
# Parent changed - Respawn both parents
|
126
|
+
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
127
|
+
@changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
|
128
|
+
elsif parent
|
129
|
+
# Moved under a parent - Respawn parent and kill standalone (if previously enabled)
|
130
|
+
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
131
|
+
if previous_enabled
|
132
|
+
@removed_microservices[microservice_name] = microservice_config
|
133
|
+
end
|
134
|
+
else # previous_parent
|
135
|
+
# Moved to standalone - Respawn previous parent and make new (if enabled)
|
136
|
+
@changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
|
137
|
+
if enabled
|
138
|
+
@new_microservices[microservice_name] = microservice_config
|
139
|
+
end
|
140
|
+
end
|
141
|
+
else
|
142
|
+
if previous_enabled
|
143
|
+
if enabled
|
144
|
+
# Respawn regular microservice
|
145
|
+
@changed_microservices[microservice_name] = microservice_config
|
146
|
+
else
|
147
|
+
# Remove regular microservice
|
148
|
+
@removed_microservices[microservice_name] = microservice_config
|
149
|
+
end
|
150
|
+
else
|
151
|
+
# Newly enabled microservice
|
152
|
+
@new_microservices[microservice_name] = microservice_config
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Handle the detection of a removed microservice model
|
158
|
+
def handle_removed_microservice(microservice_name, microservice_config)
|
159
|
+
previous_parent = @previous_microservices[microservice_name]['parent']
|
160
|
+
scope = microservice_name.split("__")[0]
|
161
|
+
|
162
|
+
Logger.info("Removed microservice detected: #{microservice_name}", scope: scope)
|
163
|
+
|
164
|
+
if previous_parent
|
165
|
+
# Respawn previous parent
|
166
|
+
@changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
|
167
|
+
else
|
168
|
+
previous_enabled = @previous_microservices[microservice_name]["enabled"]
|
169
|
+
previous_enabled = true if previous_enabled.nil?
|
170
|
+
if previous_enabled
|
171
|
+
# Regular process to be removed
|
172
|
+
@removed_microservices[microservice_name] = microservice_config
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
87
177
|
def update
|
88
178
|
@previous_microservices = @microservices.dup
|
89
179
|
# Get all the microservice configuration
|
@@ -100,65 +190,21 @@ module OpenC3
|
|
100
190
|
@changed_microservices = {}
|
101
191
|
@removed_microservices = {}
|
102
192
|
@microservices.each do |microservice_name, microservice_config|
|
103
|
-
parent = microservice_config['parent']
|
104
193
|
if @previous_microservices[microservice_name]
|
105
|
-
previous_parent = @previous_microservices[microservice_name]['parent']
|
106
194
|
if @previous_microservices[microservice_name] != microservice_config
|
107
|
-
# CHANGED
|
108
195
|
if not microservice_config['ignore_changes']
|
109
|
-
|
110
|
-
Logger.info("Changed microservice detected: #{microservice_name}\nWas: #{@previous_microservices[microservice_name]}\nIs: #{microservice_config}", scope: scope)
|
111
|
-
if parent or previous_parent
|
112
|
-
if parent == previous_parent
|
113
|
-
# Same Parent - Respawn parent
|
114
|
-
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
115
|
-
elsif parent and previous_parent
|
116
|
-
# Parent changed - Respawn both parents
|
117
|
-
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
118
|
-
@changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
|
119
|
-
elsif parent
|
120
|
-
# Moved under a parent - Respawn parent and kill standalone
|
121
|
-
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
122
|
-
@removed_microservices[microservice_name] = microservice_config
|
123
|
-
else # previous_parent
|
124
|
-
# Moved to standalone - Respawn previous parent and make new
|
125
|
-
@changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
|
126
|
-
@new_microservices[microservice_name] = microservice_config
|
127
|
-
end
|
128
|
-
else
|
129
|
-
# Respawn regular microservice
|
130
|
-
@changed_microservices[microservice_name] = microservice_config
|
131
|
-
end
|
196
|
+
handle_changed_microservice(microservice_name, microservice_config)
|
132
197
|
end
|
133
198
|
end
|
134
199
|
else
|
135
|
-
|
136
|
-
scope = microservice_name.split("__")[0]
|
137
|
-
Logger.info("New microservice detected: #{microservice_name}", scope: scope)
|
138
|
-
if parent
|
139
|
-
# Respawn parent
|
140
|
-
@changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
|
141
|
-
else
|
142
|
-
# New process be spawned
|
143
|
-
@new_microservices[microservice_name] = microservice_config
|
144
|
-
end
|
200
|
+
handle_new_microservice(microservice_name, microservice_config)
|
145
201
|
end
|
146
202
|
end
|
147
203
|
|
148
204
|
# Detect removed microservices
|
149
205
|
@previous_microservices.each do |microservice_name, microservice_config|
|
150
|
-
previous_parent = microservice_config['parent']
|
151
206
|
unless @microservices[microservice_name]
|
152
|
-
|
153
|
-
scope = microservice_name.split("__")[0]
|
154
|
-
Logger.info("Removed microservice detected: #{microservice_name}", scope: scope)
|
155
|
-
if previous_parent
|
156
|
-
# Respawn previous parent
|
157
|
-
@changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
|
158
|
-
else
|
159
|
-
# Regular process to be removed
|
160
|
-
@removed_microservices[microservice_name] = microservice_config
|
161
|
-
end
|
207
|
+
handle_removed_microservice(microservice_name, microservice_config)
|
162
208
|
end
|
163
209
|
end
|
164
210
|
|
@@ -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
|
@@ -80,7 +80,7 @@ module OpenC3
|
|
80
80
|
item.range = get_range()
|
81
81
|
item.default = get_default()
|
82
82
|
end
|
83
|
-
item.id_value = get_id_value()
|
83
|
+
item.id_value = get_id_value(item)
|
84
84
|
item.description = get_description()
|
85
85
|
if append?
|
86
86
|
item = packet.append(item)
|
@@ -165,6 +165,18 @@ module OpenC3
|
|
165
165
|
min..max
|
166
166
|
end
|
167
167
|
|
168
|
+
def convert_string_value(index)
|
169
|
+
# If the default value is 0x<data> (no quotes), it is treated as
|
170
|
+
# binary data. Otherwise, the default value is considered to be a string.
|
171
|
+
if @parser.parameters[index].upcase.start_with?("0X") and
|
172
|
+
!@parser.line.include?("\"#{@parser.parameters[index]}\"") and
|
173
|
+
!@parser.line.include?("\'#{@parser.parameters[index]}\'")
|
174
|
+
return @parser.parameters[index].hex_to_byte_string
|
175
|
+
else
|
176
|
+
return @parser.parameters[index]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
168
180
|
def get_default
|
169
181
|
return [] if @parser.keyword.include?('ARRAY')
|
170
182
|
|
@@ -173,15 +185,7 @@ module OpenC3
|
|
173
185
|
return [] if data_type == :ARRAY
|
174
186
|
return {} if data_type == :OBJECT
|
175
187
|
if data_type == :STRING or data_type == :BLOCK
|
176
|
-
|
177
|
-
# binary data. Otherwise, the default value is considered to be a string.
|
178
|
-
if @parser.parameters[index].upcase.start_with?("0X") and
|
179
|
-
!@parser.line.include?("\"#{@parser.parameters[index]}\"") and
|
180
|
-
!@parser.line.include?("\'#{@parser.parameters[index]}\'")
|
181
|
-
return @parser.parameters[index].hex_to_byte_string
|
182
|
-
else
|
183
|
-
return @parser.parameters[index]
|
184
|
-
end
|
188
|
+
return convert_string_value(index)
|
185
189
|
else
|
186
190
|
if data_type != :DERIVED
|
187
191
|
return ConfigParser.handle_defined_constants(
|
@@ -193,22 +197,25 @@ module OpenC3
|
|
193
197
|
end
|
194
198
|
end
|
195
199
|
|
196
|
-
def get_id_value
|
200
|
+
def get_id_value(item)
|
197
201
|
return nil unless @parser.keyword.include?('ID_')
|
198
|
-
|
199
202
|
data_type = get_data_type
|
200
|
-
if @parser.keyword.include?('ITEM')
|
201
|
-
index = append? ? 3 : 4
|
202
|
-
else # PARAMETER
|
203
|
-
index = append? ? 5 : 6
|
204
|
-
# STRING and BLOCK PARAMETERS don't have min and max values
|
205
|
-
index -= 2 if data_type == :STRING || data_type == :BLOCK
|
206
|
-
end
|
207
203
|
if data_type == :DERIVED
|
208
204
|
raise @parser.error("DERIVED data type not allowed for Identifier")
|
209
205
|
end
|
206
|
+
# For PARAMETERS the default value is the ID value
|
207
|
+
if @parser.keyword.include?("PARAMETER")
|
208
|
+
return item.default
|
209
|
+
end
|
210
210
|
|
211
|
-
|
211
|
+
index = append? ? 3 : 4
|
212
|
+
if data_type == :STRING or data_type == :BLOCK
|
213
|
+
return convert_string_value(index)
|
214
|
+
else
|
215
|
+
return ConfigParser.handle_defined_constants(
|
216
|
+
@parser.parameters[index].convert_to_value, data_type, get_bit_size()
|
217
|
+
)
|
218
|
+
end
|
212
219
|
end
|
213
220
|
|
214
221
|
def get_description
|
@@ -35,10 +35,10 @@ module OpenC3
|
|
35
35
|
|
36
36
|
# @return [Hash] Items that make up the structure.
|
37
37
|
# Hash key is the item's name in uppercase
|
38
|
-
|
38
|
+
attr_accessor :items
|
39
39
|
|
40
40
|
# @return [Array] Items sorted by bit_offset.
|
41
|
-
|
41
|
+
attr_accessor :sorted_items
|
42
42
|
|
43
43
|
# @return [Integer] Defined length in bytes (not bits) of the structure
|
44
44
|
attr_reader :defined_length
|
@@ -511,11 +511,28 @@ module OpenC3
|
|
511
511
|
# additional work that isn't necessary here
|
512
512
|
structure.instance_variable_set("@buffer".freeze, @buffer.clone) if @buffer
|
513
513
|
# Need to update reference packet in the Accessor
|
514
|
+
structure.accessor = @accessor.clone
|
514
515
|
structure.accessor.packet = structure
|
515
516
|
return structure
|
516
517
|
end
|
517
518
|
alias dup clone
|
518
519
|
|
520
|
+
# Clone that also deep copies items
|
521
|
+
# @return [Structure] A deep copy of the structure
|
522
|
+
def deep_copy
|
523
|
+
cloned = clone()
|
524
|
+
cloned_items = []
|
525
|
+
cloned.sorted_items.each do |item|
|
526
|
+
cloned_items << item.clone()
|
527
|
+
end
|
528
|
+
cloned.sorted_items = cloned_items
|
529
|
+
cloned.items = {}
|
530
|
+
cloned_items.each do |item|
|
531
|
+
cloned.items[item.name] = item
|
532
|
+
end
|
533
|
+
return cloned
|
534
|
+
end
|
535
|
+
|
519
536
|
# Enable the ability to read and write item values as if they were methods
|
520
537
|
# to the class
|
521
538
|
def enable_method_missing
|
@@ -20,7 +20,7 @@ require 'openc3/topics/topic'
|
|
20
20
|
|
21
21
|
module OpenC3
|
22
22
|
class DecomInterfaceTopic < Topic
|
23
|
-
def self.build_cmd(target_name, cmd_name, cmd_params, range_check, raw, scope:)
|
23
|
+
def self.build_cmd(target_name, cmd_name, cmd_params, range_check, raw, timeout: 5, scope:)
|
24
24
|
data = {}
|
25
25
|
data['target_name'] = target_name.to_s.upcase
|
26
26
|
data['cmd_name'] = cmd_name.to_s.upcase
|
@@ -34,7 +34,6 @@ module OpenC3
|
|
34
34
|
Topic.update_topic_offsets([ack_topic])
|
35
35
|
decom_id = Topic.write_topic("#{scope}__DECOMINTERFACE__{#{target_name}}",
|
36
36
|
{ 'build_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
37
|
-
timeout = 5 # Arbitrary 5s timeout
|
38
37
|
time = Time.now
|
39
38
|
while (Time.now - time) < timeout
|
40
39
|
Topic.read_topics([ack_topic]) do |_topic, _msg_id, msg_hash, _redis|
|
@@ -75,8 +75,6 @@ module OpenC3
|
|
75
75
|
|
76
76
|
def self.shutdown(interface, scope:)
|
77
77
|
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface.name}", { 'shutdown' => 'true' }, '*', 100)
|
78
|
-
sleep 1 # Give some time for the interface to shutdown
|
79
|
-
InterfaceTopic.clear_topics(InterfaceTopic.topics(interface, scope: scope))
|
80
78
|
end
|
81
79
|
|
82
80
|
def self.interface_cmd(interface_name, cmd_name, *cmd_params, scope:)
|
@@ -85,8 +85,6 @@ module OpenC3
|
|
85
85
|
|
86
86
|
def self.shutdown(router, scope:)
|
87
87
|
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router.name}", { 'shutdown' => 'true' }, '*', 100)
|
88
|
-
sleep 1 # Give some time for the interface to shutdown
|
89
|
-
RouterTopic.clear_topics(RouterTopic.topics(router, scope: scope))
|
90
88
|
end
|
91
89
|
|
92
90
|
def self.router_cmd(router_name, cmd_name, *cmd_params, scope:)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
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 file may also be used under the terms of a commercial license
|
17
|
+
# if purchased from OpenC3, Inc.
|
18
|
+
|
19
|
+
module OpenC3
|
20
|
+
class ThreadManager
|
21
|
+
MONITOR_SLEEP_SECONDS = 0.25
|
22
|
+
|
23
|
+
# Variable that holds the singleton instance
|
24
|
+
@@instance = nil
|
25
|
+
|
26
|
+
# Mutex used to ensure that only one instance of is created
|
27
|
+
@@instance_mutex = Mutex.new
|
28
|
+
|
29
|
+
# Get the singleton instance of ThreadManager
|
30
|
+
def self.instance
|
31
|
+
return @@instance if @@instance
|
32
|
+
|
33
|
+
@@instance_mutex.synchronize do
|
34
|
+
return @@instance if @@instance
|
35
|
+
@@instance ||= self.new
|
36
|
+
return @@instance
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
@threads = []
|
42
|
+
@shutdown_started = false
|
43
|
+
end
|
44
|
+
|
45
|
+
def register(thread, stop_object: nil, shutdown_object: nil)
|
46
|
+
@threads << [thread, stop_object, shutdown_object]
|
47
|
+
end
|
48
|
+
|
49
|
+
def monitor
|
50
|
+
while true
|
51
|
+
@threads.each do |thread, _, _|
|
52
|
+
if !thread.alive?
|
53
|
+
return
|
54
|
+
end
|
55
|
+
end
|
56
|
+
sleep(MONITOR_SLEEP_SECONDS)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def shutdown
|
61
|
+
@@instance_mutex.synchronize do
|
62
|
+
return if @shutdown_started
|
63
|
+
@shutdown_started = true
|
64
|
+
end
|
65
|
+
@threads.each do |thread, stop_object, shutdown_object|
|
66
|
+
if thread.alive?
|
67
|
+
if stop_object
|
68
|
+
stop_object.stop
|
69
|
+
end
|
70
|
+
if shutdown_object
|
71
|
+
shutdown_object.shutdown
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def join
|
78
|
+
@threads.each do |thread, _, _|
|
79
|
+
thread.join
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|