openc3 5.12.0 → 5.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +3 -3
- data/bin/openc3cli +21 -18
- data/data/config/command_modifiers.yaml +53 -1
- data/data/config/graph_settings.yaml +1 -1
- data/data/config/item_modifiers.yaml +1 -2
- data/data/config/parameter_modifiers.yaml +13 -14
- data/data/config/screen.yaml +1 -2
- data/data/config/target_config.yaml +2 -6
- data/lib/openc3/accessors/accessor.rb +42 -29
- data/lib/openc3/accessors/binary_accessor.rb +11 -1
- data/lib/openc3/accessors/form_accessor.rb +11 -1
- data/lib/openc3/accessors/http_accessor.rb +38 -0
- data/lib/openc3/accessors/json_accessor.rb +15 -3
- data/lib/openc3/accessors/template_accessor.rb +150 -0
- data/lib/openc3/accessors/xml_accessor.rb +11 -1
- data/lib/openc3/accessors.rb +1 -0
- data/lib/openc3/api/cmd_api.rb +99 -35
- data/lib/openc3/api/limits_api.rb +3 -3
- data/lib/openc3/api/tlm_api.rb +70 -31
- data/lib/openc3/interfaces/interface.rb +9 -7
- data/lib/openc3/interfaces/mqtt_interface.rb +11 -9
- data/lib/openc3/interfaces/mqtt_stream_interface.rb +78 -0
- data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +116 -0
- data/lib/openc3/interfaces/tcpip_client_interface.rb +4 -0
- data/lib/openc3/interfaces/tcpip_server_interface.rb +5 -0
- data/lib/openc3/interfaces.rb +1 -1
- data/lib/openc3/logs/packet_log_reader.rb +2 -2
- data/lib/openc3/logs/text_log_writer.rb +3 -2
- data/lib/openc3/microservices/decom_microservice.rb +1 -0
- data/lib/openc3/microservices/interface_microservice.rb +10 -1
- data/lib/openc3/microservices/trigger_group_microservice.rb +2 -1
- data/lib/openc3/models/cvt_model.rb +16 -12
- data/lib/openc3/models/gem_model.rb +20 -3
- data/lib/openc3/models/microservice_model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +43 -5
- data/lib/openc3/models/target_model.rb +69 -8
- data/lib/openc3/packets/json_packet.rb +46 -15
- data/lib/openc3/packets/packet.rb +92 -4
- data/lib/openc3/packets/packet_config.rb +27 -2
- data/lib/openc3/packets/parsers/xtce_parser.rb +5 -1
- data/lib/openc3/script/api_shared.rb +42 -31
- data/lib/openc3/script/commands.rb +18 -12
- data/lib/openc3/script/limits.rb +1 -1
- data/lib/openc3/script/script.rb +6 -12
- data/lib/openc3/script/storage.rb +4 -4
- data/lib/openc3/script/web_socket_api.rb +2 -2
- data/lib/openc3/streams/mqtt_stream.rb +109 -0
- data/lib/openc3/streams/tcpip_socket_stream.rb +19 -0
- data/lib/openc3/system/system.rb +13 -1
- data/lib/openc3/utilities/cli_generator.rb +48 -21
- data/lib/openc3/utilities/local_mode.rb +3 -3
- data/lib/openc3/utilities/logger.rb +17 -16
- data/lib/openc3/utilities/process_manager.rb +1 -1
- data/lib/openc3/utilities/store_queued.rb +126 -0
- data/lib/openc3/version.rb +5 -5
- data/templates/conversion/conversion.py +28 -0
- data/templates/conversion/conversion.rb +1 -18
- data/templates/limits_response/response.py +37 -0
- data/templates/limits_response/response.rb +0 -17
- data/templates/microservice/microservices/TEMPLATE/microservice.py +54 -0
- data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -7
- data/templates/plugin/.gitignore +1 -0
- data/templates/plugin/plugin.gemspec +2 -2
- data/templates/target/targets/TARGET/lib/target.py +9 -0
- data/templates/target/targets/TARGET/procedures/procedure.py +3 -0
- data/templates/target/targets/TARGET/public/README.txt +1 -0
- data/templates/tool_angular/package.json +21 -20
- data/templates/tool_angular/yarn.lock +2287 -3171
- data/templates/tool_react/package.json +15 -15
- data/templates/tool_react/yarn.lock +716 -789
- data/templates/tool_svelte/package.json +16 -15
- data/templates/tool_svelte/src/services/openc3-api.js +17 -22
- data/templates/tool_svelte/yarn.lock +715 -620
- data/templates/tool_vue/package.json +16 -15
- data/templates/tool_vue/yarn.lock +149 -69
- data/templates/widget/package.json +15 -14
- data/templates/widget/yarn.lock +132 -63
- metadata +160 -148
- data/lib/openc3/io/openc3_snmp.rb +0 -61
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2024 OpenC3, Inc.
|
4
4
|
# All Rights Reserved.
|
5
5
|
#
|
6
6
|
# This program is free software; you can modify and/or redistribute it
|
@@ -40,20 +40,33 @@ module OpenC3
|
|
40
40
|
if args[0] != 'plugin' and Dir.glob("*.gemspec").empty?
|
41
41
|
abort("No gemspec file detected. #{args[0].to_s.downcase} generator should be run within an existing plugin.")
|
42
42
|
end
|
43
|
+
|
44
|
+
if args[-1] == '--python'
|
45
|
+
@@language = 'py'
|
46
|
+
else
|
47
|
+
@@language = 'rb'
|
48
|
+
end
|
43
49
|
end
|
44
50
|
|
45
51
|
def self.process_template(template_dir, the_binding)
|
46
52
|
Dir.glob("#{template_dir}/**/*", File::FNM_DOTMATCH).each do |file|
|
47
53
|
next if File.basename(file) == '.'
|
54
|
+
if @@language == 'rb'
|
55
|
+
# Ignore python files if we're ruby
|
56
|
+
next if File.extname(file) == '.py'
|
57
|
+
elsif @@language == 'py'
|
58
|
+
# Ignore ruby files if we're python
|
59
|
+
next if File.extname(file) == '.rb'
|
60
|
+
end
|
48
61
|
base_name = file.sub("#{template_dir}/", '')
|
49
62
|
next if yield base_name
|
50
63
|
if File.directory?(file)
|
51
|
-
FileUtils.
|
64
|
+
FileUtils.mkdir_p(base_name)
|
52
65
|
next
|
53
66
|
end
|
54
|
-
output = ERB.new(File.read(file)
|
55
|
-
File.open(base_name, 'w') do |
|
56
|
-
|
67
|
+
output = ERB.new(File.read(file), trim_mode: "-").result(the_binding)
|
68
|
+
File.open(base_name, 'w') do |base_file|
|
69
|
+
base_file.write output
|
57
70
|
end
|
58
71
|
end
|
59
72
|
end
|
@@ -82,8 +95,8 @@ module OpenC3
|
|
82
95
|
end
|
83
96
|
|
84
97
|
def self.generate_target(args)
|
85
|
-
if args.length
|
86
|
-
abort("Usage: cli generate #{args[0]} <NAME>")
|
98
|
+
if args.length < 2 or args.length > 3
|
99
|
+
abort("Usage: cli generate #{args[0]} <NAME> (--ruby or --python)")
|
87
100
|
end
|
88
101
|
|
89
102
|
# Create the local variables
|
@@ -92,17 +105,31 @@ module OpenC3
|
|
92
105
|
if File.exist?(target_path)
|
93
106
|
abort("Target #{target_path} already exists!")
|
94
107
|
end
|
95
|
-
target_lib_filename = "#{target_name.downcase}
|
108
|
+
target_lib_filename = "#{target_name.downcase}.#{@@language}"
|
96
109
|
target_class = target_lib_filename.filename_to_class_name
|
97
110
|
target_object = target_name.downcase
|
98
111
|
|
99
112
|
process_template("#{TEMPLATES_DIR}/target", binding) do |filename|
|
100
113
|
# Rename the template TARGET to our actual target named after the plugin
|
101
114
|
filename.sub!("targets/TARGET", "targets/#{target_name}")
|
102
|
-
filename.sub!("target
|
115
|
+
filename.sub!("target.#{@@language}", target_lib_filename)
|
103
116
|
false
|
104
117
|
end
|
105
118
|
|
119
|
+
if @@language == 'py'
|
120
|
+
# If we're using Python create a requirements.txt and list it in the gemspec
|
121
|
+
# However, don't write over an existing file they may have already created
|
122
|
+
unless File.exist?("requirements.txt")
|
123
|
+
File.open("requirements.txt", 'w') do |file|
|
124
|
+
file.puts "# Python dependencies"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
gemspec_filename = Dir['*.gemspec'][0]
|
128
|
+
gemspec = File.read(gemspec_filename)
|
129
|
+
gemspec.gsub!('plugin.txt', 'plugin.txt requirements.txt')
|
130
|
+
File.write(gemspec_filename, gemspec)
|
131
|
+
end
|
132
|
+
|
106
133
|
# Add this target to plugin.txt
|
107
134
|
File.open("plugin.txt", 'a') do |file|
|
108
135
|
file.puts <<~DOC
|
@@ -120,8 +147,8 @@ module OpenC3
|
|
120
147
|
end
|
121
148
|
|
122
149
|
def self.generate_microservice(args)
|
123
|
-
if args.length
|
124
|
-
abort("Usage: cli generate #{args[0]} <NAME>")
|
150
|
+
if args.length < 2 or args.length > 3
|
151
|
+
abort("Usage: cli generate #{args[0]} <NAME> (--ruby or --python)")
|
125
152
|
end
|
126
153
|
|
127
154
|
# Create the local variables
|
@@ -130,13 +157,13 @@ module OpenC3
|
|
130
157
|
if File.exist?(microservice_path)
|
131
158
|
abort("Microservice #{microservice_path} already exists!")
|
132
159
|
end
|
133
|
-
microservice_filename = "#{microservice_name.downcase}
|
160
|
+
microservice_filename = "#{microservice_name.downcase}.#{@@language}"
|
134
161
|
microservice_class = microservice_filename.filename_to_class_name
|
135
162
|
|
136
163
|
process_template("#{TEMPLATES_DIR}/microservice", binding) do |filename|
|
137
164
|
# Rename the template MICROSERVICE to our actual microservice name
|
138
165
|
filename.sub!("microservices/TEMPLATE", "microservices/#{microservice_name}")
|
139
|
-
filename.sub!("microservice
|
166
|
+
filename.sub!("microservice.#{@@language}", microservice_filename)
|
140
167
|
false
|
141
168
|
end
|
142
169
|
|
@@ -252,8 +279,8 @@ module OpenC3
|
|
252
279
|
self.singleton_class.send(:alias_method, :generate_tool_svelte, :generate_tool)
|
253
280
|
|
254
281
|
def self.generate_conversion(args)
|
255
|
-
if args.length
|
256
|
-
abort("Usage: cli generate conversion <TARGET> <NAME>")
|
282
|
+
if args.length < 3 or args.length > 4
|
283
|
+
abort("Usage: cli generate conversion <TARGET> <NAME> (--ruby or --python)")
|
257
284
|
end
|
258
285
|
|
259
286
|
# Create the local variables
|
@@ -263,7 +290,7 @@ module OpenC3
|
|
263
290
|
end
|
264
291
|
conversion_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_CONVERSION"
|
265
292
|
conversion_path = "targets/#{target_name}/lib/"
|
266
|
-
conversion_basename = "#{conversion_name.downcase}
|
293
|
+
conversion_basename = "#{conversion_name.downcase}.#{@@language}"
|
267
294
|
conversion_class = conversion_basename.filename_to_class_name
|
268
295
|
conversion_filename = "targets/#{target_name}/lib/#{conversion_basename}"
|
269
296
|
if File.exist?(conversion_filename)
|
@@ -271,7 +298,7 @@ module OpenC3
|
|
271
298
|
end
|
272
299
|
|
273
300
|
process_template("#{TEMPLATES_DIR}/conversion", binding) do |filename|
|
274
|
-
filename.sub!("conversion
|
301
|
+
filename.sub!("conversion.#{@@language}", conversion_filename)
|
275
302
|
false
|
276
303
|
end
|
277
304
|
|
@@ -282,8 +309,8 @@ module OpenC3
|
|
282
309
|
end
|
283
310
|
|
284
311
|
def self.generate_limits_response(args)
|
285
|
-
if args.length
|
286
|
-
abort("Usage: cli generate limits_response <TARGET> <NAME>")
|
312
|
+
if args.length < 3 or args.length > 4
|
313
|
+
abort("Usage: cli generate limits_response <TARGET> <NAME> (--ruby or --python)")
|
287
314
|
end
|
288
315
|
|
289
316
|
# Create the local variables
|
@@ -293,7 +320,7 @@ module OpenC3
|
|
293
320
|
end
|
294
321
|
response_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_LIMITS_RESPONSE"
|
295
322
|
response_path = "targets/#{target_name}/lib/"
|
296
|
-
response_basename = "#{response_name.downcase}
|
323
|
+
response_basename = "#{response_name.downcase}.#{@@language}"
|
297
324
|
response_class = response_basename.filename_to_class_name
|
298
325
|
response_filename = "targets/#{target_name}/lib/#{response_basename}"
|
299
326
|
if File.exist?(response_filename)
|
@@ -301,7 +328,7 @@ module OpenC3
|
|
301
328
|
end
|
302
329
|
|
303
330
|
process_template("#{TEMPLATES_DIR}/limits_response", binding) do |filename|
|
304
|
-
filename.sub!("response
|
331
|
+
filename.sub!("response.#{@@language}", response_filename)
|
305
332
|
false
|
306
333
|
end
|
307
334
|
|
@@ -29,9 +29,7 @@ module OpenC3
|
|
29
29
|
# When updating update local_mode.py, PluginsTab.vue, plugins.spec.ts
|
30
30
|
DEFAULT_PLUGINS = [
|
31
31
|
'openc3-cosmos-tool-admin',
|
32
|
-
'openc3-cosmos-tool-autonomic',
|
33
32
|
'openc3-cosmos-tool-bucketexplorer',
|
34
|
-
'openc3-cosmos-tool-calendar',
|
35
33
|
'openc3-cosmos-tool-cmdsender',
|
36
34
|
'openc3-cosmos-tool-cmdtlmserver',
|
37
35
|
'openc3-cosmos-tool-dataextractor',
|
@@ -47,6 +45,8 @@ module OpenC3
|
|
47
45
|
'openc3-cosmos-tool-tlmviewer',
|
48
46
|
'openc3-cosmos-enterprise-tool-admin',
|
49
47
|
'openc3-enterprise-tool-base',
|
48
|
+
'openc3-cosmos-tool-autonomic',
|
49
|
+
'openc3-cosmos-tool-calendar',
|
50
50
|
'openc3-tool-base',
|
51
51
|
]
|
52
52
|
|
@@ -252,7 +252,7 @@ module OpenC3
|
|
252
252
|
# New install of same plugin - Leave it alone
|
253
253
|
end
|
254
254
|
else
|
255
|
-
# No
|
255
|
+
# No existing instance.json, but we found the same gem
|
256
256
|
# This shouldn't happen without users using this wrong
|
257
257
|
# We will update
|
258
258
|
found = true
|
@@ -59,11 +59,11 @@ module OpenC3
|
|
59
59
|
# FATAL prints FATAL, ERROR, WARN, INFO, DEBUG messages
|
60
60
|
FATAL = ::Logger::FATAL
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
DEBUG_LEVEL = 'DEBUG'
|
63
|
+
INFO_LEVEL = 'INFO'
|
64
|
+
WARN_LEVEL = 'WARN'
|
65
|
+
ERROR_LEVEL = 'ERROR'
|
66
|
+
FATAL_LEVEL = 'FATAL'
|
67
67
|
|
68
68
|
# Types
|
69
69
|
LOG = 'log'
|
@@ -98,27 +98,27 @@ module OpenC3
|
|
98
98
|
# @param block [Proc] Block to call which should return a string to append
|
99
99
|
# to the log message
|
100
100
|
def debug(message = nil, scope: @@scope, user: nil, type: LOG, url: nil, &block)
|
101
|
-
log_message(
|
101
|
+
log_message(DEBUG_LEVEL, message, scope: scope, user: user, type: type, url: url, &block) if @level <= DEBUG
|
102
102
|
end
|
103
103
|
|
104
104
|
# (see #debug)
|
105
105
|
def info(message = nil, scope: @@scope, user: nil, type: LOG, url: nil, &block)
|
106
|
-
log_message(
|
106
|
+
log_message(INFO_LEVEL, message, scope: scope, user: user, type: type, url: url, &block) if @level <= INFO
|
107
107
|
end
|
108
108
|
|
109
109
|
# (see #debug)
|
110
110
|
def warn(message = nil, scope: @@scope, user: nil, type: LOG, url: nil, &block)
|
111
|
-
log_message(
|
111
|
+
log_message(WARN_LEVEL, message, scope: scope, user: user, type: type, url: url, &block) if @level <= WARN
|
112
112
|
end
|
113
113
|
|
114
114
|
# (see #debug)
|
115
115
|
def error(message = nil, scope: @@scope, user: nil, type: LOG, url: nil, &block)
|
116
|
-
log_message(
|
116
|
+
log_message(ERROR_LEVEL, message, scope: scope, user: user, type: type, url: url, &block) if @level <= ERROR
|
117
117
|
end
|
118
118
|
|
119
119
|
# (see #debug)
|
120
120
|
def fatal(message = nil, scope: @@scope, user: nil, type: LOG, url: nil, &block)
|
121
|
-
log_message(
|
121
|
+
log_message(FATAL_LEVEL, message, scope: scope, user: user, type: type, url: url, &block) if @level <= FATAL
|
122
122
|
end
|
123
123
|
|
124
124
|
# (see #debug)
|
@@ -174,10 +174,11 @@ module OpenC3
|
|
174
174
|
|
175
175
|
protected
|
176
176
|
|
177
|
-
def log_message(
|
177
|
+
def log_message(log_level, message, scope:, user:, type:, url:)
|
178
178
|
@@mutex.synchronize do
|
179
|
-
time = Time.now
|
180
|
-
|
179
|
+
time = Time.now.utc
|
180
|
+
# timestamp iso8601 with 6 decimal places to match the python output format
|
181
|
+
data = { time: time.to_nsec_from_epoch, '@timestamp' => time.iso8601(6), level: log_level }
|
181
182
|
data[:microservice_name] = @microservice_name if @microservice_name
|
182
183
|
data[:detail] = @detail_string if @detail_string
|
183
184
|
data[:user] = user if user # EE: If a user is passed, put its name. Don't include user data if no user was passed.
|
@@ -185,12 +186,12 @@ module OpenC3
|
|
185
186
|
message = yield
|
186
187
|
end
|
187
188
|
data[:container_name] = @container_name
|
188
|
-
data[:
|
189
|
+
data[:message] = message
|
189
190
|
data[:type] = type
|
190
191
|
data[:url] = url if url
|
191
192
|
if @stdout
|
192
|
-
case
|
193
|
-
when
|
193
|
+
case log_level
|
194
|
+
when WARN_LEVEL, ERROR_LEVEL, FATAL_LEVEL
|
194
195
|
if ENV['OPENC3_LOG_STDERR']
|
195
196
|
$stderr.puts data.as_json(:allow_nan => true).to_json(:allow_nan => true)
|
196
197
|
$stderr.flush
|
@@ -96,7 +96,7 @@ module OpenC3
|
|
96
96
|
process.status.output = output
|
97
97
|
if process.exit_code != 0
|
98
98
|
process.status.state = "Crashed"
|
99
|
-
elsif output.include?('"
|
99
|
+
elsif output.include?('"level":"ERROR"') || output.include?('"level":"WARN"')
|
100
100
|
process.status.state = "Warning"
|
101
101
|
else
|
102
102
|
process.status.state = "Complete"
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2024 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
|
+
require 'openc3/utilities/store'
|
20
|
+
|
21
|
+
module OpenC3
|
22
|
+
class StoreQueued
|
23
|
+
# Variable that holds the singleton instance
|
24
|
+
@instance = nil
|
25
|
+
|
26
|
+
# Mutex used to ensure that only one instance is created
|
27
|
+
@@instance_mutex = Mutex.new
|
28
|
+
|
29
|
+
# Get the singleton instance
|
30
|
+
# Sets the update interval to 1 second by default
|
31
|
+
def self.instance(update_interval = 1) # seconds
|
32
|
+
return @instance if @instance
|
33
|
+
|
34
|
+
@@instance_mutex.synchronize do
|
35
|
+
@instance ||= self.new(update_interval)
|
36
|
+
return @instance
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Delegate all unknown class methods to delegate to the instance
|
41
|
+
def self.method_missing(message, *args, **kwargs, &)
|
42
|
+
self.instance.public_send(message, *args, **kwargs, &)
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(update_interval)
|
46
|
+
@update_interval = update_interval
|
47
|
+
@store = store_instance()
|
48
|
+
# Queue to hold the store requests
|
49
|
+
@store_queue = Queue.new
|
50
|
+
# Sleeper used to delay update thread
|
51
|
+
@update_sleeper = Sleeper.new
|
52
|
+
|
53
|
+
at_exit() do
|
54
|
+
shutdown()
|
55
|
+
end
|
56
|
+
|
57
|
+
# Thread used to call methods on the store
|
58
|
+
@update_thread = OpenC3.safe_thread(self.class.to_s) do
|
59
|
+
store_thread_body()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def store_thread_body
|
64
|
+
while true
|
65
|
+
start_time = Time.now
|
66
|
+
|
67
|
+
unless @store_queue.empty?
|
68
|
+
# Pipeline the requests to redis to improve performance
|
69
|
+
@store.pipelined do
|
70
|
+
while !@store_queue.empty?
|
71
|
+
action = @store_queue.pop()
|
72
|
+
@store.method_missing(action.message, *action.args, **action.kwargs, &action.block)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Only check whether to update at a set interval
|
78
|
+
run_time = Time.now - start_time
|
79
|
+
sleep_time = @update_interval - run_time
|
80
|
+
sleep_time = 0 if sleep_time < 0
|
81
|
+
break if @update_sleeper.sleep(sleep_time)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def shutdown
|
86
|
+
@update_sleeper.cancel if @update_sleeper
|
87
|
+
OpenC3.kill_thread(self, @update_thread) if @update_thread
|
88
|
+
@update_thread = nil
|
89
|
+
# Drain the queue before shutdown
|
90
|
+
unless @store_queue.empty?
|
91
|
+
# Pipeline the requests to redis to improve performance
|
92
|
+
@store.pipelined do
|
93
|
+
while !@store_queue.empty?
|
94
|
+
action = @store_queue.pop()
|
95
|
+
@store.method_missing(action.message, *action.args, **action.kwargs, &action.block)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Record the message for pipelining by the thread
|
102
|
+
def method_missing(message, *args, **kwargs, &block)
|
103
|
+
o = OpenStruct.new
|
104
|
+
o.message = message
|
105
|
+
o.args = args
|
106
|
+
o.kwargs = kwargs
|
107
|
+
o.block = block
|
108
|
+
@store_queue.push(o)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the store we're working with
|
112
|
+
def store_instance
|
113
|
+
Store.instance
|
114
|
+
end
|
115
|
+
|
116
|
+
def graceful_kill
|
117
|
+
# Do nothing
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class EphemeralStoreQueued < StoreQueued
|
122
|
+
def store_instance
|
123
|
+
EphemeralStore.instance
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/openc3/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
OPENC3_VERSION = '5.
|
3
|
+
OPENC3_VERSION = '5.14.0'
|
4
4
|
module OpenC3
|
5
5
|
module Version
|
6
6
|
MAJOR = '5'
|
7
|
-
MINOR = '
|
7
|
+
MINOR = '14'
|
8
8
|
PATCH = '0'
|
9
9
|
OTHER = ''
|
10
|
-
BUILD = '
|
10
|
+
BUILD = 'dd31e4853fa50ed08ee3100e43e75849617d0a47'
|
11
11
|
end
|
12
|
-
VERSION = '5.
|
13
|
-
GEM_VERSION = '5.
|
12
|
+
VERSION = '5.14.0'
|
13
|
+
GEM_VERSION = '5.14.0'
|
14
14
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from openc3.conversions.conversion import Conversion
|
2
|
+
|
3
|
+
# Custom conversion class
|
4
|
+
# See https://openc3.com/docs/v5/telemetry#read_conversion
|
5
|
+
class <%= conversion_class %>(Conversion):
|
6
|
+
def __init__(self):
|
7
|
+
super().__init__()
|
8
|
+
# Should be one of 'INT', 'UINT', 'FLOAT', 'STRING', 'BLOCK'
|
9
|
+
self.converted_type = 'STRING'
|
10
|
+
# Size of the converted type in bits
|
11
|
+
# Use 0 for 'STRING' or 'BLOCK' where the size can be variable
|
12
|
+
self.converted_bit_size = 0
|
13
|
+
|
14
|
+
# @param value [Object] Value based on the item definition. This could be
|
15
|
+
# a string, integer, float, or array of values.
|
16
|
+
# @param packet [Packet] The packet object where the conversion is defined
|
17
|
+
# @param buffer [String] The raw packet buffer
|
18
|
+
def call(self, value, packet, buffer):
|
19
|
+
# Read values from the packet and do a conversion
|
20
|
+
# Used for DERIVED items that don't have a value
|
21
|
+
# item1 = packet.read("ITEM1") # returns CONVERTED value (default)
|
22
|
+
# item2 = packet.read("ITEM2", 'RAW') # returns RAW value
|
23
|
+
# return (item1 + item2) / 2
|
24
|
+
#
|
25
|
+
# Perform conversion logic directly on value
|
26
|
+
# Used when conversion is applied to a regular (not DERIVED) item
|
27
|
+
# NOTE: You can also use packet.read("ITEM") to get additional values
|
28
|
+
# return value / 2 * packet.read("OTHER_ITEM")
|
@@ -1,21 +1,4 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
|
-
|
3
|
-
# Copyright 2023 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
2
|
require 'openc3/conversions/conversion'
|
20
3
|
|
21
4
|
module OpenC3
|
@@ -37,7 +20,7 @@ module OpenC3
|
|
37
20
|
# @param buffer [String] The raw packet buffer
|
38
21
|
def call(value, packet, buffer)
|
39
22
|
# Read values from the packet and do a conversion
|
40
|
-
# Used for
|
23
|
+
# Used for DERIVED items that don't have a value
|
41
24
|
# item1 = packet.read("ITEM1") # returns CONVERTED value (default)
|
42
25
|
# item2 = packet.read("ITEM2", :RAW) # returns RAW value
|
43
26
|
# return (item1 + item2) / 2
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from openc3.packets.limits_response import LimitsResponse
|
2
|
+
|
3
|
+
|
4
|
+
class <%= response_class %>(LimitsResponse):
|
5
|
+
# @param packet [Packet] Packet the limits response is assigned to
|
6
|
+
# @param item [PacketItem] PacketItem the limits response is assigned to
|
7
|
+
# @param old_limits_state [Symbol] Previous value of the limit. One of nil,
|
8
|
+
# "GREEN_HIGH", "GREEN_LOW", "YELLOW", "YELLOW_HIGH", "YELLOW_LOW",
|
9
|
+
# "RED", "RED_HIGH", "RED_LOW". nil if the previous limit state has not yet
|
10
|
+
# been established.
|
11
|
+
def call(self, packet, item, old_limits_state):
|
12
|
+
# Take action based on the current limits state
|
13
|
+
# Delete any of the case lines that do not apply or you don't care about
|
14
|
+
match item.limits.state:
|
15
|
+
case "RED_HIGH":
|
16
|
+
# Take action like sending a command:
|
17
|
+
# cmd("TARGET SAFE")
|
18
|
+
pass
|
19
|
+
case "RED_LOW":
|
20
|
+
pass
|
21
|
+
case "YELLOW_LOW":
|
22
|
+
pass
|
23
|
+
case "YELLOW_HIGH":
|
24
|
+
pass
|
25
|
+
# GREEN limits are only available if a telemetry item has them defined
|
26
|
+
# COSMOS refers to these as "operational limits"
|
27
|
+
# See https://openc3.com/docs/v5/telemetry#limits
|
28
|
+
case "GREEN_LOW":
|
29
|
+
pass
|
30
|
+
case "GREEN_HIGH":
|
31
|
+
pass
|
32
|
+
# :RED and :YELLOW limits are triggered for STATES with defined RED and YELLOW states
|
33
|
+
# See https://openc3.com/docs/v5/telemetry#state
|
34
|
+
case "RED":
|
35
|
+
pass
|
36
|
+
case "YELLOW":
|
37
|
+
pass
|
@@ -1,21 +1,4 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
|
-
|
3
|
-
# Copyright 2022 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
2
|
require 'openc3/packets/limits_response'
|
20
3
|
|
21
4
|
module OpenC3
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import time
|
2
|
+
from openc3.microservices.microservice import Microservice
|
3
|
+
from openc3.utilities.sleeper import Sleeper
|
4
|
+
from openc3.api import *
|
5
|
+
|
6
|
+
|
7
|
+
class <%= microservice_class %>(Microservice):
|
8
|
+
def __init__(self, name):
|
9
|
+
super().__init__(name)
|
10
|
+
for option in self.config['options']:
|
11
|
+
# Update with your own OPTION handling
|
12
|
+
match option[0].upper():
|
13
|
+
case 'PERIOD':
|
14
|
+
self.period = int(option[1])
|
15
|
+
case _:
|
16
|
+
self.logger.error(
|
17
|
+
"Unknown option passed to microservice #{@name}: #{option}"
|
18
|
+
)
|
19
|
+
|
20
|
+
if not self.period:
|
21
|
+
self.period = 60 # 1 minutes
|
22
|
+
self.sleeper = Sleeper()
|
23
|
+
|
24
|
+
def run(self):
|
25
|
+
while True:
|
26
|
+
start_time = time.time()
|
27
|
+
if self.cancel_thread:
|
28
|
+
break
|
29
|
+
|
30
|
+
# Do your microservice work here
|
31
|
+
self.logger.info("Template Microservice ran")
|
32
|
+
# cmd("INST ABORT")
|
33
|
+
|
34
|
+
# The self.state variable is set to 'RUNNING' by the microservice base class
|
35
|
+
# The self.state is reflected to the user in the MICROSERVICES tab so you can
|
36
|
+
# convey long running actions by changing it, e.g. self.state = 'CALCULATING ...'
|
37
|
+
|
38
|
+
run_time = time.time() - start_time
|
39
|
+
delta = self.period - run_time
|
40
|
+
if delta > 0:
|
41
|
+
# Delay till the next period
|
42
|
+
if self.sleeper.sleep(
|
43
|
+
delta
|
44
|
+
): # returns true and breaks loop on shutdown
|
45
|
+
break
|
46
|
+
self.count += 1
|
47
|
+
|
48
|
+
def shutdown(self):
|
49
|
+
self.sleeper.cancel() # Breaks out of run()
|
50
|
+
super().shutdown()
|
51
|
+
|
52
|
+
|
53
|
+
if __name__ == "__main__":
|
54
|
+
<%= microservice_class %>.run()
|
@@ -1,11 +1,4 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
|
-
|
3
|
-
# Copyright 2023 OpenC3, Inc.
|
4
|
-
# All Rights Reserved.
|
5
|
-
#
|
6
|
-
# This file may also be used under the terms of a commercial license
|
7
|
-
# if purchased from OpenC3, Inc.
|
8
|
-
|
9
2
|
require 'openc3/microservices/microservice'
|
10
3
|
require 'openc3/api/api'
|
11
4
|
|
@@ -0,0 +1 @@
|
|
1
|
+
node_modules
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
3
|
# Create the overall gemspec
|
4
|
-
|
4
|
+
Gem::Specification.new do |s|
|
5
5
|
s.name = '<%= plugin_name %>'
|
6
6
|
s.summary = 'OpenC3 <%= plugin_name %> plugin'
|
7
7
|
s.description = <<-EOF
|
@@ -10,7 +10,7 @@ spec = Gem::Specification.new do |s|
|
|
10
10
|
s.license = 'MIT'
|
11
11
|
s.authors = ['Anonymous']
|
12
12
|
s.email = ['name@domain.com']
|
13
|
-
s.homepage = 'https://github.com/OpenC3/
|
13
|
+
s.homepage = 'https://github.com/OpenC3/cosmos'
|
14
14
|
s.platform = Gem::Platform::RUBY
|
15
15
|
|
16
16
|
if ENV['VERSION']
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This class can be used in your scripts like so:
|
2
|
+
# require_utility '<%= target_class.upcase %>/lib/<%= target_lib_filename %>'
|
3
|
+
# <%= target_object %> = <%= target_class %>()
|
4
|
+
# <%= target_object %>.utility()
|
5
|
+
# For more information see the OpenC3 scripting guide
|
6
|
+
|
7
|
+
class <%= target_class %>:
|
8
|
+
def utility():
|
9
|
+
pass
|
@@ -0,0 +1 @@
|
|
1
|
+
Put image files in this directory for use in Telemetry Viewer Canvas Image widgets such as CANVASIMAGE and CANVASIMAGEVALUE.
|