openc3 5.5.0.pre.beta0 → 5.5.1

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +58 -74
  3. data/data/config/_canvas_values.yaml +41 -0
  4. data/data/config/_graph_params.yaml +25 -0
  5. data/data/config/graph_settings.yaml +52 -0
  6. data/data/config/interface_modifiers.yaml +4 -0
  7. data/data/config/item_modifiers.yaml +1 -1
  8. data/data/config/microservice.yaml +5 -1
  9. data/data/config/plugins.yaml +5 -0
  10. data/data/config/screen.yaml +44 -50
  11. data/data/config/settings.yaml +144 -0
  12. data/data/config/widgets.yaml +1744 -1491
  13. data/lib/openc3/api/cmd_api.rb +43 -17
  14. data/lib/openc3/api/tlm_api.rb +37 -4
  15. data/lib/openc3/bridge/bridge.rb +3 -3
  16. data/lib/openc3/bridge/bridge_config.rb +68 -20
  17. data/lib/openc3/interfaces/interface.rb +8 -0
  18. data/lib/openc3/microservices/decom_microservice.rb +0 -3
  19. data/lib/openc3/microservices/interface_microservice.rb +2 -0
  20. data/lib/openc3/microservices/reaction_microservice.rb +0 -1
  21. data/lib/openc3/models/cvt_model.rb +1 -2
  22. data/lib/openc3/models/interface_model.rb +5 -2
  23. data/lib/openc3/models/metadata_model.rb +1 -1
  24. data/lib/openc3/models/microservice_model.rb +7 -6
  25. data/lib/openc3/models/note_model.rb +1 -1
  26. data/lib/openc3/models/plugin_model.rb +17 -8
  27. data/lib/openc3/models/timeline_model.rb +1 -1
  28. data/lib/openc3/models/trigger_group_model.rb +1 -1
  29. data/lib/openc3/operators/microservice_operator.rb +2 -2
  30. data/lib/openc3/packets/packet_item.rb +1 -1
  31. data/lib/openc3/script/storage.rb +5 -5
  32. data/lib/openc3/streams/web_socket_client_stream.rb +0 -1
  33. data/lib/openc3/tools/table_manager/table_manager_core.rb +1 -2
  34. data/lib/openc3/utilities/aws_bucket.rb +22 -4
  35. data/lib/openc3/utilities/bucket_file_cache.rb +3 -2
  36. data/lib/openc3/utilities/bucket_utilities.rb +1 -1
  37. data/lib/openc3/utilities/cli_generator.rb +210 -0
  38. data/lib/openc3/utilities/local_mode.rb +2 -2
  39. data/lib/openc3/utilities/redis_secrets.rb +4 -4
  40. data/lib/openc3/utilities/secrets.rb +5 -5
  41. data/lib/openc3/utilities/target_file.rb +43 -32
  42. data/lib/openc3/version.rb +6 -6
  43. data/templates/conversion/conversion.rb +43 -0
  44. data/templates/limits_response/response.rb +51 -0
  45. data/templates/microservice/microservices/TEMPLATE/microservice.rb +62 -0
  46. data/templates/plugin/plugin.txt +1 -0
  47. data/templates/{plugin-template → target}/targets/TARGET/screens/status.txt +1 -2
  48. metadata +23 -16
  49. data/lib/openc3/models/traefik_model.rb +0 -47
  50. data/templates/plugin-template/plugin.txt +0 -9
  51. /data/templates/{plugin-template → plugin}/LICENSE.txt +0 -0
  52. /data/templates/{plugin-template → plugin}/README.md +0 -0
  53. /data/templates/{plugin-template → plugin}/Rakefile +0 -0
  54. /data/templates/{plugin-template → plugin}/plugin.gemspec +0 -0
  55. /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/cmd.txt +0 -0
  56. /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/tlm.txt +0 -0
  57. /data/templates/{plugin-template → target}/targets/TARGET/lib/target.rb +0 -0
  58. /data/templates/{plugin-template → target}/targets/TARGET/procedures/procedure.rb +0 -0
  59. /data/templates/{plugin-template → target}/targets/TARGET/target.txt +0 -0
@@ -44,7 +44,7 @@ module OpenC3
44
44
  class PluginModel < Model
45
45
  PRIMARY_KEY = 'openc3_plugins'
46
46
  # Reserved VARIABLE names. See local_mode.rb: update_local_plugin()
47
- RESERVED_VARIABLE_NAMES = ['target_name', 'microservice_name']
47
+ RESERVED_VARIABLE_NAMES = ['target_name', 'microservice_name', 'scope']
48
48
 
49
49
  attr_accessor :variables
50
50
  attr_accessor :plugin_txt_lines
@@ -72,7 +72,7 @@ module OpenC3
72
72
  temp_dir = Dir.mktmpdir
73
73
  tf = nil
74
74
  begin
75
- if File.exists?(gem_file_path)
75
+ if File.exist?(gem_file_path)
76
76
  # Load gem to internal gem server
77
77
  OpenC3::GemModel.put(gem_file_path, gem_install: false, scope: scope) unless validate_only
78
78
  else
@@ -122,7 +122,6 @@ module OpenC3
122
122
  if existing_variables && existing_variables.key?(variable_name)
123
123
  variables[variable_name] = existing_variables[variable_name]
124
124
  end
125
- # Ignore everything else during phase 1
126
125
  end
127
126
  end
128
127
 
@@ -162,14 +161,21 @@ module OpenC3
162
161
  gem_path = File.join(temp_dir, "gem")
163
162
  FileUtils.mkdir_p(gem_path)
164
163
  pkg = Gem::Package.new(gem_file_path)
165
- needs_dependencies = pkg.spec.runtime_dependencies.length > 0
166
164
  pkg.extract_files(gem_path)
167
165
  Dir[File.join(gem_path, '**/screens/*.txt')].each do |filename|
168
166
  if File.basename(filename) != File.basename(filename).downcase
169
- raise "Invalid screen name: #{filename}. Screen names must be lowercase."
167
+ raise "Invalid screen filename: #{filename}. Screen filenames must be lowercase."
170
168
  end
171
169
  end
170
+ needs_dependencies = pkg.spec.runtime_dependencies.length > 0
172
171
  needs_dependencies = true if Dir.exist?(File.join(gem_path, 'lib'))
172
+ # If needs_dependencies hasn't already been set we need to scan the plugin.txt
173
+ # to see if they've explicitly set the NEEDS_DEPENDENCIES keyword
174
+ unless needs_dependencies
175
+ if plugin_hash['plugin_txt_lines'].join("\n").include?('NEEDS_DEPENDENCIES')
176
+ needs_dependencies = true
177
+ end
178
+ end
173
179
  if needs_dependencies
174
180
  plugin_model.needs_dependencies = true
175
181
  plugin_model.update unless validate_only
@@ -192,13 +198,15 @@ module OpenC3
192
198
  tf.close
193
199
  plugin_txt_path = tf.path
194
200
  variables = plugin_hash['variables']
201
+ variables ||= {}
202
+ variables['scope'] = scope
195
203
  if File.exist?(plugin_txt_path)
196
204
  parser = OpenC3::ConfigParser.new("https://openc3.com")
197
205
 
198
206
  current_model = nil
199
207
  parser.parse_file(plugin_txt_path, false, true, true, variables) do |keyword, params|
200
208
  case keyword
201
- when 'VARIABLE'
209
+ when 'VARIABLE', 'NEEDS_DEPENDENCIES'
202
210
  # Ignore during phase 2
203
211
  when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
204
212
  if current_model
@@ -206,12 +214,13 @@ module OpenC3
206
214
  current_model.deploy(gem_path, variables, validate_only: validate_only)
207
215
  current_model = nil
208
216
  end
209
- current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser, keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
217
+ current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
218
+ keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
210
219
  else
211
220
  if current_model
212
221
  current_model.handle_config(parser, keyword, params)
213
222
  else
214
- raise "Invalid keyword #{keyword} in plugin.txt"
223
+ raise "Invalid keyword '#{keyword}' in plugin.txt"
215
224
  end
216
225
  end
217
226
  end
@@ -91,7 +91,7 @@ module OpenC3
91
91
  if color.nil?
92
92
  color = '#%06x' % (rand * 0xffffff)
93
93
  end
94
- valid_color = color =~ /[0-9,a-f,A-F]{6}/
94
+ valid_color = color =~ /[0-9a-fA-F]{6}/
95
95
  if valid_color.nil?
96
96
  raise RuntimeError.new "invalid color but in hex format. #FF0000"
97
97
  end
@@ -92,7 +92,7 @@ module OpenC3
92
92
  if color.nil?
93
93
  color = '#%06x' % (rand * 0xffffff)
94
94
  end
95
- valid_color = color =~ /[0-9,a-f,A-F]{6}/
95
+ valid_color = color =~ /[0-9a-fA-F]{6}/
96
96
  if valid_color.nil?
97
97
  raise TriggerGroupInputError.new "invalid color must be in hex format. #FF0000"
98
98
  end
@@ -60,8 +60,8 @@ module OpenC3
60
60
  # Setup secrets for microservice
61
61
  secrets = microservice_config["secrets"]
62
62
  if secrets
63
- secrets.each do |type, secret_name, env_name_or_path|
64
- secret_value = @secrets.get(secret_name, scope: scope)
63
+ secrets.each do |type, secret_name, env_name_or_path, secret_store|
64
+ secret_value = @secrets.get(secret_name, secret_store: secret_store, scope: scope)
65
65
  if secret_value
66
66
  case type
67
67
  when 'ENV'
@@ -80,7 +80,7 @@ module OpenC3
80
80
 
81
81
  # @return [Hash] Whether or not messages should be printed for this state.
82
82
  # Given as STATE_NAME => true / false.
83
- attr_accessor :messages_disabled
83
+ attr_reader :messages_disabled
84
84
 
85
85
  # Colors associated with states
86
86
  # @return [Hash] State colors given as STATE_NAME => COLOR
@@ -77,7 +77,7 @@ module OpenC3
77
77
  else
78
78
  request.body_stream = io_or_string
79
79
  end
80
- response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
80
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
81
81
  http.request(request) do |response|
82
82
  response.value() # Raises an HTTP error if the response is not 2xx (success)
83
83
  return response
@@ -157,17 +157,17 @@ module OpenC3
157
157
  if $openc3_in_cluster
158
158
  case ENV['OPENC3_CLOUD']
159
159
  when 'local'
160
- uri = URI.parse("http://openc3-minio:9000" + url)
160
+ URI.parse("http://openc3-minio:9000" + url)
161
161
  when 'aws'
162
- uri = URI.parse("https://s3.#{ENV['AWS_REGION']}.amazonaws.com" + url)
162
+ URI.parse("https://s3.#{ENV['AWS_REGION']}.amazonaws.com" + url)
163
163
  when 'gcp'
164
- uri = URI.parse("https://storage.googleapis.com" + url)
164
+ URI.parse("https://storage.googleapis.com" + url)
165
165
  # when 'azure'
166
166
  else
167
167
  raise "Unknown cloud #{ENV['OPENC3_CLOUD']}"
168
168
  end
169
169
  else
170
- uri = URI.parse($api_server.generate_url + url)
170
+ URI.parse($api_server.generate_url + url)
171
171
  end
172
172
  end
173
173
 
@@ -51,7 +51,6 @@ module OpenC3
51
51
  @frame = ::WebSocket::Frame::Incoming::Client.new
52
52
  @handshaked = false
53
53
  @write_socket.write(@handshake.to_s)
54
- start_time = Time.now
55
54
  read() # This should wait for the handshake
56
55
  return true
57
56
  end
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
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 'openc3'
@@ -148,7 +148,6 @@ module OpenC3
148
148
  }
149
149
  col = 0
150
150
  row = 0
151
- num_cols = table.num_columns
152
151
  table.sorted_items.each_with_index do |item, index|
153
152
  next if item.hidden
154
153
  if table.num_columns == 1
@@ -111,7 +111,12 @@ module OpenC3
111
111
  token = nil
112
112
  result = []
113
113
  while true
114
- resp = @client.list_objects_v2(bucket: bucket, prefix: prefix, max_keys: max_request)
114
+ resp = @client.list_objects_v2({
115
+ bucket: bucket,
116
+ max_keys: max_request,
117
+ prefix: prefix,
118
+ continuation_token: token
119
+ })
115
120
  result.concat(resp.contents)
116
121
  break if result.length >= max_total
117
122
  break unless resp.is_truncated
@@ -119,12 +124,12 @@ module OpenC3
119
124
  end
120
125
  # Array of objects with key and size methods
121
126
  result
122
- rescue Aws::S3::Errors::NoSuchBucket => error
127
+ rescue Aws::S3::Errors::NoSuchBucket
123
128
  raise NotFound, "Bucket '#{bucket}' does not exist."
124
129
  end
125
130
 
126
131
  # Lists the files under a specified path
127
- def list_files(bucket:, path:, only_directories: false)
132
+ def list_files(bucket:, path:, only_directories: false, include_head: false)
128
133
  # Trailing slash is important in AWS S3 when listing files
129
134
  # See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Types/ListObjectsV2Output.html#common_prefixes-instance_method
130
135
  if path[-1] != '/'
@@ -158,6 +163,9 @@ module OpenC3
158
163
  item['name'] = aws_item.key.split('/')[-1]
159
164
  item['modified'] = aws_item.last_modified
160
165
  item['size'] = aws_item.size
166
+ if include_head
167
+ item['head'] = head_object(bucket: bucket, key: aws_item.key)
168
+ end
161
169
  files << item
162
170
  end
163
171
  result = [dirs, files]
@@ -166,10 +174,20 @@ module OpenC3
166
174
  token = resp.next_continuation_token
167
175
  end
168
176
  result
169
- rescue Aws::S3::Errors::NoSuchBucket => error
177
+ rescue Aws::S3::Errors::NoSuchBucket
170
178
  raise NotFound, "Bucket '#{bucket}' does not exist."
171
179
  end
172
180
 
181
+ # get metadata for a specific object
182
+ def head_object(bucket:, key:)
183
+ head = @client.head_object({
184
+ bucket: bucket,
185
+ key: key
186
+ })
187
+ rescue Aws::S3::Errors::NotFound => error
188
+ raise NotFound, "Object: #{bucket}/#{key}"
189
+ end
190
+
173
191
  # put_object fires off the request to store but does not confirm
174
192
  def put_object(bucket:, key:, body:, content_type: nil, cache_control: nil, metadata: nil)
175
193
  @client.put_object(bucket: bucket, key: key, body: body,
@@ -57,10 +57,10 @@ class BucketFile
57
57
  cmd_or_tlm = path_split[2].to_s.upcase
58
58
  target_name = path_split[3].to_s.upcase
59
59
  if stream_mode == 'RAW'
60
- type = (@cmd_or_tlm == 'CMD') ? 'COMMAND' : 'TELEMETRY'
60
+ type = (cmd_or_tlm == 'CMD') ? 'COMMAND' : 'TELEMETRY'
61
61
  else
62
62
  if stream_mode == 'DECOM'
63
- type = (@cmd_or_tlm == 'CMD') ? 'DECOMCMD' : 'DECOM'
63
+ type = (cmd_or_tlm == 'CMD') ? 'DECOMCMD' : 'DECOM'
64
64
  else
65
65
  type = stream_mode # REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
66
66
  end
@@ -156,6 +156,7 @@ class BucketFileCache
156
156
  @current_disk_usage = 0
157
157
  @queued_bucket_files = []
158
158
  @bucket_file_hash = {}
159
+ bucket_file = nil
159
160
 
160
161
  @thread = Thread.new do
161
162
  client = OpenC3::Bucket.getClient()
@@ -162,7 +162,7 @@ module OpenC3
162
162
 
163
163
  def self.get_file_times(bucket_path)
164
164
  basename = File.basename(bucket_path)
165
- file_start_timestamp, file_end_timestamp, other = basename.split("__")
165
+ file_start_timestamp, file_end_timestamp, _ = basename.split("__")
166
166
  file_start_time = DateTime.strptime(file_start_timestamp, FILE_TIMESTAMP_FORMAT).to_time
167
167
  file_end_time = DateTime.strptime(file_end_timestamp, FILE_TIMESTAMP_FORMAT).to_time
168
168
  return file_start_time, file_end_time
@@ -0,0 +1,210 @@
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
+ module OpenC3
20
+ class CliGenerator
21
+ GENERATORS = %w(plugin target microservice conversion limits_response)
22
+ TEMPLATES_DIR = "#{File.dirname(__FILE__)}/../../../templates"
23
+
24
+ # Called by openc3cli with ARGV[1..-1]
25
+ def self.generate(args)
26
+ unless GENERATORS.include?(args[0])
27
+ abort("Unknown generator '#{args[0]}'. Valid generators: #{GENERATORS.join(', ')}")
28
+ end
29
+ check_args(args)
30
+ send("generate_#{args[0]}", args)
31
+ end
32
+
33
+ def self.check_args(args)
34
+ args.each do |arg|
35
+ if arg =~ /\s/
36
+ abort("#{args[0].capitalize} arguments can not have spaces!")
37
+ end
38
+ end
39
+ # All generators except 'plugin' must be within an existing plugin
40
+ if args[0] != 'plugin' and Dir.glob("*.gemspec").empty?
41
+ abort("No gemspec file detected. #{args[0].capitalize} generator should be run within an existing plugin.")
42
+ end
43
+ end
44
+
45
+ def self.process_template(template_dir, the_binding)
46
+ Dir.glob("#{template_dir}/**/*").each do |file|
47
+ base_name = file.sub("#{template_dir}/", '')
48
+ yield base_name
49
+ if File.directory?(file)
50
+ FileUtils.mkdir(base_name)
51
+ next
52
+ end
53
+ output = ERB.new(File.read(file), trim_mode: "-").result(the_binding)
54
+ File.open(base_name, 'w') do |file|
55
+ file.write output
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.generate_plugin(args)
61
+ if args.length != 2
62
+ abort("Usage: cli generate #{args[0]} <NAME>")
63
+ end
64
+
65
+ # Create the local variables
66
+ plugin = args[1].downcase.gsub(/_+|-+/, '-')
67
+ plugin_name = "openc3-cosmos-#{plugin}"
68
+ if File.exist?(plugin_name)
69
+ abort("Plugin #{plugin_name} already exists!")
70
+ end
71
+ FileUtils.mkdir(plugin_name)
72
+ Dir.chdir(plugin_name) # Change to the plugin path to make copying easier
73
+
74
+ process_template("#{TEMPLATES_DIR}/plugin", binding) do |filename|
75
+ filename.sub!("plugin.gemspec", "#{plugin_name}.gemspec")
76
+ end
77
+
78
+ puts "Plugin #{plugin_name} successfully generated!"
79
+ return plugin_name
80
+ end
81
+
82
+ def self.generate_target(args)
83
+ if args.length != 2
84
+ abort("Usage: cli generate #{args[0]} <NAME>")
85
+ end
86
+
87
+ # Create the local variables
88
+ target_name = args[1].upcase.gsub(/_+|-+/, '_')
89
+ target_path = "targets/#{target_name}"
90
+ if File.exist?(target_path)
91
+ abort("Target #{target_path} already exists!")
92
+ end
93
+ target_lib_filename = "#{target_name.downcase}.rb"
94
+ target_class = target_lib_filename.filename_to_class_name
95
+ target_object = target_name.downcase
96
+
97
+ process_template("#{TEMPLATES_DIR}/target", binding) do |filename|
98
+ # Rename the template TARGET to our actual target named after the plugin
99
+ filename.sub!("targets/TARGET", "targets/#{target_name}")
100
+ filename.sub!("target.rb", target_lib_filename)
101
+ end
102
+
103
+ # Add this target to plugin.txt
104
+ File.open("plugin.txt", 'a') do |file|
105
+ file.puts <<~DOC
106
+
107
+ VARIABLE #{target_name.downcase}_target_name #{target_name}
108
+
109
+ TARGET #{target_name} <%= #{target_name.downcase}_target_name %>
110
+ INTERFACE <%= #{target_name.downcase}_target_name %>_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil BURST
111
+ MAP_TARGET <%= #{target_name.downcase}_target_name %>
112
+ DOC
113
+ end
114
+
115
+ puts "Target #{target_name} successfully generated!"
116
+ return target_name
117
+ end
118
+
119
+ def self.generate_microservice(args)
120
+ if args.length != 2
121
+ abort("Usage: cli generate #{args[0]} <NAME>")
122
+ end
123
+
124
+ # Create the local variables
125
+ microservice_name = args[1].upcase.gsub(/_+|-+/, '_')
126
+ microservice_path = "microservices/#{microservice_name}"
127
+ if File.exist?(microservice_path)
128
+ abort("Microservice #{microservice_path} already exists!")
129
+ end
130
+ microservice_filename = "#{microservice_name.downcase}.rb"
131
+ microservice_class = microservice_filename.filename_to_class_name
132
+
133
+ process_template("#{TEMPLATES_DIR}/microservice", binding) do |filename|
134
+ # Rename the template MICROSERVICE to our actual microservice name
135
+ filename.sub!("microservices/TEMPLATE", "microservices/#{microservice_name}")
136
+ filename.sub!("microservice.rb", microservice_filename)
137
+ end
138
+
139
+ # Add this microservice to plugin.txt
140
+ File.open("plugin.txt", 'a') do |file|
141
+ file.puts <<~DOC
142
+
143
+ MICROSERVICE #{microservice_name} #{microservice_name.downcase.gsub('_','-')}-microservice
144
+ CMD ruby #{microservice_name.downcase}.rb
145
+ DOC
146
+ end
147
+
148
+ puts "Microservice #{microservice_name} successfully generated!"
149
+ return microservice_name
150
+ end
151
+
152
+ def self.generate_conversion(args)
153
+ if args.length != 3
154
+ abort("Usage: cli generate conversion <TARGET> <NAME>")
155
+ end
156
+
157
+ # Create the local variables
158
+ target_name = args[1].upcase
159
+ unless File.exist?("targets/#{target_name}")
160
+ abort("Target '#{target_name}' does not exist! Conversions must be created for existing targets.")
161
+ end
162
+ conversion_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_CONVERSION"
163
+ conversion_path = "targets/#{target_name}/lib/"
164
+ conversion_basename = "#{conversion_name.downcase}.rb"
165
+ conversion_class = conversion_basename.filename_to_class_name
166
+ conversion_filename = "targets/#{target_name}/lib/#{conversion_basename}"
167
+ if File.exist?(conversion_filename)
168
+ abort("Conversion #{conversion_filename} already exists!")
169
+ end
170
+
171
+ process_template("#{TEMPLATES_DIR}/conversion", binding) do |filename|
172
+ filename.sub!("conversion.rb", conversion_filename)
173
+ end
174
+
175
+ puts "Conversion #{conversion_filename} successfully generated!"
176
+ puts "To use the conversion add the following to a telemetry item:"
177
+ puts " READ_CONVERSION #{conversion_basename}"
178
+ return conversion_name
179
+ end
180
+
181
+ def self.generate_limits_response(args)
182
+ if args.length != 3
183
+ abort("Usage: cli generate limits_response <TARGET> <NAME>")
184
+ end
185
+
186
+ # Create the local variables
187
+ target_name = args[1].upcase
188
+ unless File.exist?("targets/#{target_name}")
189
+ abort("Target '#{target_name}' does not exist! Limits responses must be created for existing targets.")
190
+ end
191
+ response_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_LIMITS_RESPONSE"
192
+ response_path = "targets/#{target_name}/lib/"
193
+ response_basename = "#{response_name.downcase}.rb"
194
+ response_class = response_basename.filename_to_class_name
195
+ response_filename = "targets/#{target_name}/lib/#{response_basename}"
196
+ if File.exist?(response_filename)
197
+ abort("response #{response_filename} already exists!")
198
+ end
199
+
200
+ process_template("#{TEMPLATES_DIR}/limits_response", binding) do |filename|
201
+ filename.sub!("response.rb", response_filename)
202
+ end
203
+
204
+ puts "Limits response #{response_filename} successfully generated!"
205
+ puts "To use the limits response add the following to a telemetry item:"
206
+ puts " LIMITS_RESPONSE #{response_basename}"
207
+ return response_name
208
+ end
209
+ end
210
+ end
@@ -275,13 +275,13 @@ module OpenC3
275
275
  return if DEFAULT_PLUGINS.include?(gem_name)
276
276
  puts "Updating local plugin files: #{full_folder_path}"
277
277
  FileUtils.mkdir_p(full_folder_path)
278
- gems, plugin_instance = scan_plugin_dir(full_folder_path)
278
+ gems, _ = scan_plugin_dir(full_folder_path)
279
279
  gems.each do |gem|
280
280
  File.delete(gem)
281
281
  end
282
282
  temp_dir = Dir.mktmpdir
283
283
  begin
284
- unless File.exists?(plugin_file_path)
284
+ unless File.exist?(plugin_file_path)
285
285
  plugin_file_path = OpenC3::GemModel.get(plugin_file_path)
286
286
  end
287
287
  File.open(File.join(full_folder_path, File.basename(plugin_file_path)), 'wb') do |file|
@@ -21,11 +21,11 @@ require 'openc3/utilities/secrets'
21
21
 
22
22
  module OpenC3
23
23
  class RedisSecrets < Secrets
24
- def keys(scope:)
24
+ def keys(secret_store: nil, scope:)
25
25
  SecretModel.names(scope: scope)
26
26
  end
27
27
 
28
- def get(key, scope:)
28
+ def get(key, secret_store: nil, scope:)
29
29
  data = SecretModel.get(name: key, scope: scope)
30
30
  if data
31
31
  return data['value']
@@ -34,11 +34,11 @@ module OpenC3
34
34
  end
35
35
  end
36
36
 
37
- def set(key, value, scope:)
37
+ def set(key, value, secret_store: nil, scope:)
38
38
  SecretModel.set( {name: key, value: value.to_s }, scope: scope)
39
39
  end
40
40
 
41
- def delete(key, scope:)
41
+ def delete(key, secret_store: nil, scope:)
42
42
  model = SecretModel.get_model(name: key, scope: scope)
43
43
  model.destroy if model
44
44
  end
@@ -31,24 +31,24 @@ module OpenC3
31
31
  klass.new
32
32
  end
33
33
 
34
- def keys(scope:)
34
+ def keys(secret_store: nil, scope:)
35
35
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
36
36
  end
37
37
 
38
- def get(key, scope:)
38
+ def get(key, secret_store: nil, scope:)
39
39
  return @local_secrets[key]
40
40
  end
41
41
 
42
- def set(key, value, scope:)
42
+ def set(key, value, secret_store: nil, scope:)
43
43
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
44
44
  end
45
45
 
46
- def delete(key, scope:)
46
+ def delete(key, secret_store: nil, scope:)
47
47
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
48
48
  end
49
49
 
50
50
  def setup(secrets)
51
- secrets.each do |type, key, data|
51
+ secrets.each do |type, key, data, secret_store|
52
52
  case type
53
53
  when 'ENV'
54
54
  @local_secrets[key] = ENV[data]
@@ -26,41 +26,19 @@ module OpenC3
26
26
  # Matches ScriptRunner.vue const TEMP_FOLDER
27
27
  TEMP_FOLDER = '__TEMP__'
28
28
 
29
- def self.all(scope, path_matchers, include_temp: false)
30
- result = []
31
- modified = []
32
- temp = []
29
+ def self.all(scope, path_matchers, target: nil, include_temp: false)
30
+ target = target.upcase if target
33
31
 
34
32
  bucket = Bucket.getClient()
35
- resp = bucket.list_objects(
36
- bucket: ENV['OPENC3_CONFIG_BUCKET'],
37
- prefix: "#{scope}/targets",
38
- )
39
- resp.each do |object|
40
- split_key = object.key.split('/')
41
- # DEFAULT/targets_modified/__TEMP__/YYYY_MM_DD_HH_MM_SS_mmm_temp.rb
42
- if split_key[2] == TEMP_FOLDER
43
- temp << split_key[2..-1].join('/') if include_temp
44
- next
45
- end
46
-
47
- if path_matchers
48
- found = false
49
- path_matchers.each do |path|
50
- if split_key.include?(path)
51
- found = true
52
- break
53
- end
54
- end
55
- next unless found
56
- end
57
- result_no_scope_or_target_folder = split_key[2..-1].join('/')
58
- if object.key.include?("#{scope}/targets_modified")
59
- modified << result_no_scope_or_target_folder
60
- else
61
- result << result_no_scope_or_target_folder
62
- end
33
+ if target
34
+ prefix = "#{scope}/targets/#{target}/"
35
+ modified_prefix = "#{scope}/targets_modified/#{target}/"
36
+ else
37
+ prefix = "#{scope}/targets/"
38
+ modified_prefix = "#{scope}/targets_modified/"
63
39
  end
40
+ result, _ = remote_target_files(bucket_client: bucket, prefix: prefix, include_temp: false, path_matchers: path_matchers)
41
+ modified, temp = remote_target_files(bucket_client: bucket, prefix: modified_prefix, include_temp: include_temp, path_matchers: path_matchers)
64
42
 
65
43
  # Add in local targets_modified if present
66
44
  if ENV['OPENC3_LOCAL_MODE']
@@ -172,5 +150,38 @@ module OpenC3
172
150
  )
173
151
  true
174
152
  end
153
+
154
+ # protected
155
+
156
+ def self.remote_target_files(bucket_client:, prefix:, include_temp: false, path_matchers: nil)
157
+ result = []
158
+ temp = []
159
+ resp = bucket_client.list_objects(
160
+ bucket: ENV['OPENC3_CONFIG_BUCKET'],
161
+ prefix: prefix,
162
+ )
163
+ resp.each do |object|
164
+ split_key = object.key.split('/')
165
+ # DEFAULT/targets_modified/__TEMP__/YYYY_MM_DD_HH_MM_SS_mmm_temp.rb
166
+ if split_key[2] == TEMP_FOLDER
167
+ temp << split_key[2..-1].join('/') if include_temp
168
+ next
169
+ end
170
+
171
+ if path_matchers
172
+ found = false
173
+ path_matchers.each do |path|
174
+ if split_key.include?(path)
175
+ found = true
176
+ break
177
+ end
178
+ end
179
+ next unless found
180
+ end
181
+ result_no_scope_or_target_folder = split_key[2..-1].join('/')
182
+ result << result_no_scope_or_target_folder
183
+ end
184
+ return result, temp
185
+ end
175
186
  end
176
187
  end
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '5.5.0-beta0'
3
+ OPENC3_VERSION = '5.5.1'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '5'
7
7
  MINOR = '5'
8
- PATCH = '0'
9
- OTHER = 'pre.beta0'
10
- BUILD = 'abab088dbe97761fca351752b3396f1c05dd852b'
8
+ PATCH = '1'
9
+ OTHER = ''
10
+ BUILD = 'a1e84e1850e06f2c01726e03372e2872137b3852'
11
11
  end
12
- VERSION = '5.5.0-beta0'
13
- GEM_VERSION = '5.5.0.pre.beta0'
12
+ VERSION = '5.5.1'
13
+ GEM_VERSION = '5.5.1'
14
14
  end