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.

Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/bin/openc3cli +21 -18
  4. data/data/config/command_modifiers.yaml +53 -1
  5. data/data/config/graph_settings.yaml +1 -1
  6. data/data/config/item_modifiers.yaml +1 -2
  7. data/data/config/parameter_modifiers.yaml +13 -14
  8. data/data/config/screen.yaml +1 -2
  9. data/data/config/target_config.yaml +2 -6
  10. data/lib/openc3/accessors/accessor.rb +42 -29
  11. data/lib/openc3/accessors/binary_accessor.rb +11 -1
  12. data/lib/openc3/accessors/form_accessor.rb +11 -1
  13. data/lib/openc3/accessors/http_accessor.rb +38 -0
  14. data/lib/openc3/accessors/json_accessor.rb +15 -3
  15. data/lib/openc3/accessors/template_accessor.rb +150 -0
  16. data/lib/openc3/accessors/xml_accessor.rb +11 -1
  17. data/lib/openc3/accessors.rb +1 -0
  18. data/lib/openc3/api/cmd_api.rb +99 -35
  19. data/lib/openc3/api/limits_api.rb +3 -3
  20. data/lib/openc3/api/tlm_api.rb +70 -31
  21. data/lib/openc3/interfaces/interface.rb +9 -7
  22. data/lib/openc3/interfaces/mqtt_interface.rb +11 -9
  23. data/lib/openc3/interfaces/mqtt_stream_interface.rb +78 -0
  24. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +116 -0
  25. data/lib/openc3/interfaces/tcpip_client_interface.rb +4 -0
  26. data/lib/openc3/interfaces/tcpip_server_interface.rb +5 -0
  27. data/lib/openc3/interfaces.rb +1 -1
  28. data/lib/openc3/logs/packet_log_reader.rb +2 -2
  29. data/lib/openc3/logs/text_log_writer.rb +3 -2
  30. data/lib/openc3/microservices/decom_microservice.rb +1 -0
  31. data/lib/openc3/microservices/interface_microservice.rb +10 -1
  32. data/lib/openc3/microservices/trigger_group_microservice.rb +2 -1
  33. data/lib/openc3/models/cvt_model.rb +16 -12
  34. data/lib/openc3/models/gem_model.rb +20 -3
  35. data/lib/openc3/models/microservice_model.rb +1 -1
  36. data/lib/openc3/models/plugin_model.rb +43 -5
  37. data/lib/openc3/models/target_model.rb +69 -8
  38. data/lib/openc3/packets/json_packet.rb +46 -15
  39. data/lib/openc3/packets/packet.rb +92 -4
  40. data/lib/openc3/packets/packet_config.rb +27 -2
  41. data/lib/openc3/packets/parsers/xtce_parser.rb +5 -1
  42. data/lib/openc3/script/api_shared.rb +42 -31
  43. data/lib/openc3/script/commands.rb +18 -12
  44. data/lib/openc3/script/limits.rb +1 -1
  45. data/lib/openc3/script/script.rb +6 -12
  46. data/lib/openc3/script/storage.rb +4 -4
  47. data/lib/openc3/script/web_socket_api.rb +2 -2
  48. data/lib/openc3/streams/mqtt_stream.rb +109 -0
  49. data/lib/openc3/streams/tcpip_socket_stream.rb +19 -0
  50. data/lib/openc3/system/system.rb +13 -1
  51. data/lib/openc3/utilities/cli_generator.rb +48 -21
  52. data/lib/openc3/utilities/local_mode.rb +3 -3
  53. data/lib/openc3/utilities/logger.rb +17 -16
  54. data/lib/openc3/utilities/process_manager.rb +1 -1
  55. data/lib/openc3/utilities/store_queued.rb +126 -0
  56. data/lib/openc3/version.rb +5 -5
  57. data/templates/conversion/conversion.py +28 -0
  58. data/templates/conversion/conversion.rb +1 -18
  59. data/templates/limits_response/response.py +37 -0
  60. data/templates/limits_response/response.rb +0 -17
  61. data/templates/microservice/microservices/TEMPLATE/microservice.py +54 -0
  62. data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -7
  63. data/templates/plugin/.gitignore +1 -0
  64. data/templates/plugin/plugin.gemspec +2 -2
  65. data/templates/target/targets/TARGET/lib/target.py +9 -0
  66. data/templates/target/targets/TARGET/procedures/procedure.py +3 -0
  67. data/templates/target/targets/TARGET/public/README.txt +1 -0
  68. data/templates/tool_angular/package.json +21 -20
  69. data/templates/tool_angular/yarn.lock +2287 -3171
  70. data/templates/tool_react/package.json +15 -15
  71. data/templates/tool_react/yarn.lock +716 -789
  72. data/templates/tool_svelte/package.json +16 -15
  73. data/templates/tool_svelte/src/services/openc3-api.js +17 -22
  74. data/templates/tool_svelte/yarn.lock +715 -620
  75. data/templates/tool_vue/package.json +16 -15
  76. data/templates/tool_vue/yarn.lock +149 -69
  77. data/templates/widget/package.json +15 -14
  78. data/templates/widget/yarn.lock +132 -63
  79. metadata +160 -148
  80. data/lib/openc3/io/openc3_snmp.rb +0 -61
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2023 OpenC3, Inc.
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.mkdir(base_name) unless File.exist?(base_name)
64
+ FileUtils.mkdir_p(base_name)
52
65
  next
53
66
  end
54
- output = ERB.new(File.read(file).comment_erb(), trim_mode: "-").result(the_binding)
55
- File.open(base_name, 'w') do |file|
56
- file.write output
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 != 2
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}.rb"
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.rb", target_lib_filename)
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 != 2
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}.rb"
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.rb", microservice_filename)
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 != 3
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}.rb"
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.rb", conversion_filename)
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 != 3
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}.rb"
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.rb", response_filename)
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 exiting instance.json, but we found the same gem
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
- DEBUG_SEVERITY_STRING = 'DEBUG'
63
- INFO_SEVERITY_STRING = 'INFO'
64
- WARN_SEVERITY_STRING = 'WARN'
65
- ERROR_SEVERITY_STRING = 'ERROR'
66
- FATAL_SEVERITY_STRING = 'FATAL'
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(DEBUG_SEVERITY_STRING, message, scope: scope, user: user, type: type, url: url, &block) if @level <= DEBUG
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(INFO_SEVERITY_STRING, message, scope: scope, user: user, type: type, url: url, &block) if @level <= INFO
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(WARN_SEVERITY_STRING, message, scope: scope, user: user, type: type, url: url, &block) if @level <= WARN
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(ERROR_SEVERITY_STRING, message, scope: scope, user: user, type: type, url: url, &block) if @level <= ERROR
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(FATAL_SEVERITY_STRING, message, scope: scope, user: user, type: type, url: url, &block) if @level <= FATAL
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(severity_string, message, scope:, user:, type:, url:)
177
+ def log_message(log_level, message, scope:, user:, type:, url:)
178
178
  @@mutex.synchronize do
179
- time = Time.now
180
- data = { time: time.to_nsec_from_epoch, '@timestamp' => time.xmlschema(3), severity: severity_string }
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[:log] = message
189
+ data[:message] = message
189
190
  data[:type] = type
190
191
  data[:url] = url if url
191
192
  if @stdout
192
- case severity_string
193
- when WARN_SEVERITY_STRING, ERROR_SEVERITY_STRING, FATAL_SEVERITY_STRING
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?('"severity":"ERROR"') || output.include?('"severity":"WARN"')
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
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '5.12.0'
3
+ OPENC3_VERSION = '5.14.0'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '5'
7
- MINOR = '12'
7
+ MINOR = '14'
8
8
  PATCH = '0'
9
9
  OTHER = ''
10
- BUILD = '8f36977eacfebdcdae21f02223be66e69feaa2bf'
10
+ BUILD = 'dd31e4853fa50ed08ee3100e43e75849617d0a47'
11
11
  end
12
- VERSION = '5.12.0'
13
- GEM_VERSION = '5.12.0'
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 DERVIVED items that don't have a value
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
- spec = Gem::Specification.new do |s|
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/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,3 @@
1
+ # Script Runner test script
2
+ cmd("<%= target_name %> EXAMPLE")
3
+ wait_check("<%= target_name %> STATUS BOOL == 'FALSE'", 5)
@@ -0,0 +1 @@
1
+ Put image files in this directory for use in Telemetry Viewer Canvas Image widgets such as CANVASIMAGE and CANVASIMAGEVALUE.