openc3 5.13.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/bin/openc3cli +18 -15
  4. data/data/config/command_modifiers.yaml +53 -1
  5. data/lib/openc3/accessors/accessor.rb +42 -29
  6. data/lib/openc3/accessors/binary_accessor.rb +11 -1
  7. data/lib/openc3/accessors/form_accessor.rb +11 -1
  8. data/lib/openc3/accessors/http_accessor.rb +38 -0
  9. data/lib/openc3/accessors/json_accessor.rb +15 -3
  10. data/lib/openc3/accessors/template_accessor.rb +150 -0
  11. data/lib/openc3/accessors/xml_accessor.rb +11 -1
  12. data/lib/openc3/accessors.rb +1 -0
  13. data/lib/openc3/api/limits_api.rb +3 -3
  14. data/lib/openc3/api/tlm_api.rb +8 -8
  15. data/lib/openc3/interfaces/interface.rb +9 -7
  16. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +116 -0
  17. data/lib/openc3/interfaces/tcpip_client_interface.rb +4 -0
  18. data/lib/openc3/interfaces/tcpip_server_interface.rb +5 -0
  19. data/lib/openc3/interfaces.rb +1 -1
  20. data/lib/openc3/microservices/decom_microservice.rb +1 -0
  21. data/lib/openc3/microservices/interface_microservice.rb +10 -1
  22. data/lib/openc3/models/cvt_model.rb +16 -12
  23. data/lib/openc3/models/gem_model.rb +20 -3
  24. data/lib/openc3/models/microservice_model.rb +1 -1
  25. data/lib/openc3/models/plugin_model.rb +5 -1
  26. data/lib/openc3/models/target_model.rb +69 -8
  27. data/lib/openc3/packets/packet.rb +92 -4
  28. data/lib/openc3/packets/packet_config.rb +25 -1
  29. data/lib/openc3/script/api_shared.rb +11 -0
  30. data/lib/openc3/script/script.rb +6 -12
  31. data/lib/openc3/streams/tcpip_socket_stream.rb +19 -0
  32. data/lib/openc3/system/system.rb +13 -1
  33. data/lib/openc3/utilities/cli_generator.rb +15 -1
  34. data/lib/openc3/utilities/local_mode.rb +1 -1
  35. data/lib/openc3/utilities/store_queued.rb +126 -0
  36. data/lib/openc3/version.rb +5 -5
  37. data/templates/plugin/plugin.gemspec +2 -2
  38. data/templates/target/targets/TARGET/public/README.txt +1 -0
  39. data/templates/tool_angular/package.json +15 -15
  40. data/templates/tool_angular/yarn.lock +184 -78
  41. data/templates/tool_react/package.json +10 -10
  42. data/templates/tool_react/yarn.lock +236 -374
  43. data/templates/tool_svelte/package.json +13 -13
  44. data/templates/tool_svelte/yarn.lock +246 -235
  45. data/templates/tool_vue/package.json +12 -12
  46. data/templates/tool_vue/yarn.lock +63 -55
  47. data/templates/widget/package.json +11 -11
  48. data/templates/widget/yarn.lock +54 -46
  49. metadata +144 -154
  50. data/lib/openc3/io/openc3_snmp.rb +0 -61
@@ -190,10 +190,10 @@ module OpenC3
190
190
  # 'TVAC' => [-25, -10, 50, 55] }
191
191
  #
192
192
  # @return [Hash{String => Array<Number, Number, Number, Number, Number, Number>}]
193
- def get_limits(target_name, packet_name, item_name, scope: $openc3_scope, token: $openc3_token)
193
+ def get_limits(target_name, packet_name, item_name, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
194
194
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
195
195
  limits = {}
196
- item = _get_item(target_name, packet_name, item_name, scope: scope)
196
+ item = _get_item(target_name, packet_name, item_name, cache_timeout: cache_timeout, scope: scope)
197
197
  item['limits'].each do |key, vals|
198
198
  next unless vals.is_a?(Hash)
199
199
 
@@ -374,7 +374,7 @@ module OpenC3
374
374
  # @param item_name [String] item name
375
375
  # @param scope [String] scope
376
376
  # @return Hash The requested item based on the packet name
377
- def _get_item(target_name, packet_name, item_name, cache_timeout: 0.1, scope:)
377
+ def _get_item(target_name, packet_name, item_name, cache_timeout: nil, scope:)
378
378
  # Determine if this item exists, it will raise appropriate errors if not
379
379
  packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: $openc3_scope) if packet_name == 'LATEST'
380
380
  return TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
@@ -69,26 +69,26 @@ module OpenC3
69
69
  # @param args [String|Array<String>] See the description for calling style
70
70
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
71
71
  # @return [Object] The telemetry value formatted as requested
72
- def tlm(*args, type: :CONVERTED, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
72
+ def tlm(*args, type: :CONVERTED, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
73
73
  target_name, packet_name, item_name = _tlm_process_args(args, 'tlm', cache_timeout: cache_timeout, scope: scope)
74
74
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
75
75
  CvtModel.get_item(target_name, packet_name, item_name, type: type.intern, cache_timeout: cache_timeout, scope: scope)
76
76
  end
77
77
 
78
- def tlm_raw(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
78
+ def tlm_raw(*args, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
79
79
  tlm(*args, type: :RAW, cache_timeout: cache_timeout, scope: scope, token: token)
80
80
  end
81
81
 
82
- def tlm_formatted(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
82
+ def tlm_formatted(*args, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
83
83
  tlm(*args, type: :FORMATTED, cache_timeout: cache_timeout, scope: scope, token: token)
84
84
  end
85
85
 
86
- def tlm_with_units(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
86
+ def tlm_with_units(*args, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
87
87
  tlm(*args, type: :WITH_UNITS, cache_timeout: cache_timeout, scope: scope, token: token)
88
88
  end
89
89
 
90
90
  # @deprecated Use tlm with type:
91
- def tlm_variable(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
91
+ def tlm_variable(*args, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
92
92
  tlm(*args[0..-2], type: args[-1].intern, cache_timeout: cache_timeout, scope: scope, token: token)
93
93
  end
94
94
 
@@ -226,7 +226,7 @@ module OpenC3
226
226
  # @return [Array<String, Object, Symbol|nil>] Returns an Array consisting
227
227
  # of [item name, item value, item limits state] where the item limits
228
228
  # state can be one of {OpenC3::Limits::LIMITS_STATES}
229
- def get_tlm_packet(*args, stale_time: 30, type: :CONVERTED, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
229
+ def get_tlm_packet(*args, stale_time: 30, type: :CONVERTED, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
230
230
  target_name, packet_name = _extract_target_packet_names('get_tlm_packet', *args)
231
231
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
232
232
  packet = TargetModel.packet(target_name, packet_name, scope: scope)
@@ -248,7 +248,7 @@ module OpenC3
248
248
  # @return [Array<Object, Symbol>]
249
249
  # Array consisting of the item value and limits state
250
250
  # given as symbols such as :RED, :YELLOW, :STALE
251
- def get_tlm_values(items, stale_time: 30, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
251
+ def get_tlm_values(items, stale_time: 30, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
252
252
  if !items.is_a?(Array) || !items[0].is_a?(String)
253
253
  raise ArgumentError, "items must be array of strings: ['TGT__PKT__ITEM__TYPE', ...]"
254
254
  end
@@ -466,7 +466,7 @@ module OpenC3
466
466
  return nil
467
467
  end
468
468
 
469
- def _tlm_process_args(args, method_name, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
469
+ def _tlm_process_args(args, method_name, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
470
470
  case args.length
471
471
  when 1
472
472
  target_name, packet_name, item_name = extract_fields_from_tlm_text(args[0])
@@ -198,14 +198,16 @@ module OpenC3
198
198
  log_dont_log.upcase!
199
199
  period = "#{period.to_f}s"
200
200
  @scheduler.every period do
201
- begin
202
- if log_dont_log == 'DONT_LOG'
203
- cmd(cmd_string, log_message: false)
204
- else
205
- cmd(cmd_string)
201
+ if connected?()
202
+ begin
203
+ if log_dont_log == 'DONT_LOG'
204
+ cmd(cmd_string, log_message: false)
205
+ else
206
+ cmd(cmd_string)
207
+ end
208
+ rescue Exception => err
209
+ Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{err.formatted}")
206
210
  end
207
- rescue Exception => err
208
- Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{err.formatted}")
209
211
  end
210
212
  end
211
213
  end
@@ -0,0 +1,116 @@
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/config/config_parser'
20
+ require 'openc3/interfaces/protocols/protocol'
21
+ require 'thread' # For Queue
22
+ require 'timeout' # For Timeout::Error
23
+
24
+ module OpenC3
25
+ # Protocol that waits for a response for any commands with a defined response packet.
26
+ # The response packet is identified but not defined by the protocol.
27
+ class CmdResponseProtocol < Protocol
28
+ # @param response_timeout [Float] Number of seconds to wait before timing out
29
+ # when waiting for a response
30
+ # @param response_polling_period [Float] Number of seconds to wait between polling
31
+ # for a response
32
+ # @param raise_exceptions [String] Whether to raise exceptions when errors
33
+ # occur in the protocol like unexpected responses or response timeouts.
34
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
35
+ def initialize(
36
+ response_timeout = 5.0,
37
+ response_polling_period = 0.02,
38
+ raise_exceptions = false,
39
+ allow_empty_data = nil
40
+ )
41
+ super(allow_empty_data)
42
+ @response_timeout = ConfigParser.handle_nil(response_timeout)
43
+ @response_timeout = @response_timeout.to_f if @response_timeout
44
+ @response_polling_period = response_polling_period.to_f
45
+ @raise_exceptions = ConfigParser.handle_true_false(raise_exceptions)
46
+ @write_block_queue = Queue.new
47
+ @response_packet = nil
48
+ end
49
+
50
+ def connect_reset
51
+ super()
52
+ @write_block_queue.clear
53
+ end
54
+
55
+ def disconnect_reset
56
+ super()
57
+ @write_block_queue << nil # Unblock the write block queue
58
+ end
59
+
60
+ def read_packet(packet)
61
+ if @response_packet
62
+ # Grab the response packet specified in the command
63
+ result_packet = System.telemetry.packet(@response_packet[0], @response_packet[1]).clone
64
+ result_packet.buffer = packet.buffer
65
+ result_packet.received_time = nil
66
+ result_packet.stored = packet.stored
67
+ result_packet.extra = packet.extra
68
+
69
+ # Release the write
70
+ @write_block_queue << nil
71
+
72
+ # This returns the fully identified and defined packet
73
+ # Received time is handled by the interface microservice
74
+ return result_packet
75
+ else
76
+ return packet
77
+ end
78
+ end
79
+
80
+ def write_packet(packet)
81
+ # Setup the response packet (if there is one)
82
+ # This primes waiting for the response in post_write_interface
83
+ @response_packet = packet.response
84
+
85
+ return packet
86
+ end
87
+
88
+ def post_write_interface(packet, data, extra = nil)
89
+ if @response_packet
90
+ if @response_timeout
91
+ response_timeout_time = Time.now + @response_timeout
92
+ else
93
+ response_timeout_time = nil
94
+ end
95
+
96
+ # Block the write until the response is received
97
+ begin
98
+ @write_block_queue.pop(true)
99
+ rescue
100
+ sleep(@response_polling_period)
101
+ retry if !response_timeout_time
102
+ retry if response_timeout_time and Time.now < response_timeout_time
103
+ handle_error("#{@interface ? @interface.name : ""}: Timeout waiting for response")
104
+ end
105
+
106
+ @response_packet = nil
107
+ end
108
+ return super(packet, data, extra)
109
+ end
110
+
111
+ def handle_error(msg)
112
+ Logger.error(msg)
113
+ raise msg if @raise_exceptions
114
+ end
115
+ end
116
+ end
@@ -67,6 +67,10 @@ module OpenC3
67
67
  @write_timeout,
68
68
  @read_timeout
69
69
  )
70
+ # Pass down options to the stream
71
+ @options.each do |option_name, option_values|
72
+ @stream.set_option(option_name, option_values)
73
+ end
70
74
  super()
71
75
  end
72
76
  end
@@ -391,6 +391,11 @@ module OpenC3
391
391
  read_socket = socket if listen_read
392
392
  stream = TcpipSocketStream.new(write_socket, read_socket, @write_timeout, @read_timeout)
393
393
 
394
+ # Pass down options to the stream
395
+ @options.each do |option_name, option_values|
396
+ stream.set_option(option_name, option_values)
397
+ end
398
+
394
399
  interface = StreamInterface.new
395
400
  interface.target_names = @target_names
396
401
  interface.cmd_target_names = @cmd_target_names
@@ -42,7 +42,7 @@ module OpenC3
42
42
  autoload(:PreidentifiedProtocol, 'openc3/interfaces/protocols/preidentified_protocol.rb')
43
43
  autoload(:TemplateProtocol, 'openc3/interfaces/protocols/template_protocol.rb')
44
44
  autoload(:TerminatedProtocol, 'openc3/interfaces/protocols/terminated_protocol.rb')
45
-
45
+ autoload(:CmdResponseProtocol, 'openc3/interfaces/protocols/cmd_response_protocol.rb')
46
46
  autoload(:CrcProtocol, 'openc3/interfaces/protocols/crc_protocol.rb')
47
47
  autoload(:IgnorePacketProtocol, 'openc3/interfaces/protocols/ignore_packet_protocol.rb')
48
48
  end
@@ -100,6 +100,7 @@ module OpenC3
100
100
  packet.extra = extra
101
101
  end
102
102
  packet.buffer = msg_hash["buffer"]
103
+ packet.process # Run processors
103
104
  packet.check_limits(System.limits_set) # Process all the limits and call the limits_change_callback (as necessary)
104
105
 
105
106
  TelemetryDecomTopic.write_packet(packet, scope: @scope)
@@ -613,7 +613,16 @@ module OpenC3
613
613
 
614
614
  def connect
615
615
  @logger.info "#{@interface.name}: Connecting ..."
616
- @interface.connect
616
+ begin
617
+ @interface.connect
618
+ rescue Exception => error
619
+ begin
620
+ @interface.disconnect # Ensure disconnect is called at least once on a partial connect
621
+ rescue Exception
622
+ # We want to report any connect errors, not disconnect in this case
623
+ end
624
+ raise error
625
+ end
617
626
  @interface.state = 'CONNECTED'
618
627
  if @interface_or_router == 'INTERFACE'
619
628
  InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
@@ -52,12 +52,14 @@ module OpenC3
52
52
 
53
53
  # Get the hash for packet in the CVT
54
54
  # Note: Does not apply overrides
55
- def self.get(target_name:, packet_name:, cache_timeout: 0.1, scope: $openc3_scope)
55
+ def self.get(target_name:, packet_name:, cache_timeout: nil, scope: $openc3_scope)
56
56
  key = "#{scope}__tlm__#{target_name}"
57
57
  tgt_pkt_key = key + "__#{packet_name}"
58
- cache_time, hash = @@packet_cache[tgt_pkt_key]
59
58
  now = Time.now
60
- return hash if hash and (now - cache_time) < cache_timeout
59
+ if cache_timeout
60
+ cache_time, hash = @@packet_cache[tgt_pkt_key]
61
+ return hash if hash and (now - cache_time) < cache_timeout
62
+ end
61
63
  packet = Store.hget(key, packet_name)
62
64
  raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
63
65
  hash = JSON.parse(packet, :allow_nan => true, :create_additions => true)
@@ -67,7 +69,7 @@ module OpenC3
67
69
 
68
70
  # Set an item in the current value table
69
71
  def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
70
- hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: 0.0, scope: scope)
72
+ hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: nil, scope: scope)
71
73
  case type
72
74
  when :WITH_UNITS
73
75
  hash["#{item_name}__U"] = value.to_s # WITH_UNITS should always be a string
@@ -89,10 +91,10 @@ module OpenC3
89
91
  end
90
92
 
91
93
  # Get an item from the current value table
92
- def self.get_item(target_name, packet_name, item_name, type:, cache_timeout: 0.1, scope: $openc3_scope)
94
+ def self.get_item(target_name, packet_name, item_name, type:, cache_timeout: nil, scope: $openc3_scope)
93
95
  result, types = self._handle_item_override(target_name, packet_name, item_name, type: type, cache_timeout: cache_timeout, scope: scope)
94
96
  return result if result
95
- hash = get(target_name: target_name, packet_name: packet_name, scope: scope)
97
+ hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: cache_timeout, scope: scope)
96
98
  hash.values_at(*types).each do |result|
97
99
  if result
98
100
  if type == :FORMATTED or type == :WITH_UNITS
@@ -109,7 +111,7 @@ module OpenC3
109
111
  # @param items [Array<String>] Items to return. Must be formatted as TGT__PKT__ITEM__TYPE
110
112
  # @param stale_time [Integer] Time in seconds from Time.now that value will be marked stale
111
113
  # @return [Array] Array of values
112
- def self.get_tlm_values(items, stale_time: 30, cache_timeout: 0.1, scope: $openc3_scope)
114
+ def self.get_tlm_values(items, stale_time: 30, cache_timeout: nil, scope: $openc3_scope)
113
115
  now = Time.now
114
116
  results = []
115
117
  lookups = []
@@ -246,7 +248,7 @@ module OpenC3
246
248
  end
247
249
  end
248
250
 
249
- def self.determine_latest_packet_for_item(target_name, item_name, cache_timeout: 0.1, scope: $openc3_scope)
251
+ def self.determine_latest_packet_for_item(target_name, item_name, cache_timeout: nil, scope: $openc3_scope)
250
252
  item_map = TargetModel.get_item_to_packet_map(target_name, scope: scope)
251
253
  packet_names = item_map[item_name]
252
254
  raise "Item '#{target_name} LATEST #{item_name}' does not exist for scope: #{scope}" unless packet_names
@@ -293,10 +295,12 @@ module OpenC3
293
295
  end
294
296
 
295
297
  def self._get_overrides(now, tgt_pkt_key, overrides, target_name, packet_name, cache_timeout:, scope:)
296
- cache_time, hash = @@override_cache[tgt_pkt_key]
297
- if hash and (now - cache_time) < cache_timeout
298
- overrides[tgt_pkt_key] = hash
299
- return hash
298
+ if cache_timeout
299
+ cache_time, hash = @@override_cache[tgt_pkt_key]
300
+ if hash and (now - cache_time) < cache_timeout
301
+ overrides[tgt_pkt_key] = hash
302
+ return hash
303
+ end
300
304
  end
301
305
  override_data = Store.hget("#{scope}__override__#{target_name}", packet_name)
302
306
  if override_data
@@ -39,11 +39,17 @@ module OpenC3
39
39
  include Api
40
40
 
41
41
  def self.names
42
- result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
42
+ if Dir.exist?("#{ENV['GEM_HOME']}/gems")
43
+ result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
44
+ else
45
+ result = []
46
+ end
43
47
  return result.sort
44
48
  end
45
49
 
46
50
  def self.get(name)
51
+ path = "#{ENV['GEM_HOME']}/cosmoscache/#{name}"
52
+ return path if File.exist?(path)
47
53
  path = "#{ENV['GEM_HOME']}/cache/#{name}"
48
54
  return path if File.exist?(path)
49
55
  raise "Gem #{name} not found"
@@ -52,8 +58,9 @@ module OpenC3
52
58
  def self.put(gem_file_path, gem_install: true, scope:)
53
59
  if File.file?(gem_file_path)
54
60
  gem_filename = File.basename(gem_file_path)
55
- FileUtils.mkdir_p("#{ENV['GEM_HOME']}/cache") unless Dir.exist?("#{ENV['GEM_HOME']}/cache")
56
- FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cache/#{File.basename(gem_file_path)}")
61
+ # Put into cosmoscache folder that we control
62
+ FileUtils.mkdir_p("#{ENV['GEM_HOME']}/cosmoscache") unless Dir.exist?("#{ENV['GEM_HOME']}/cosmoscache")
63
+ FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cosmoscache/#{File.basename(gem_file_path)}")
57
64
  if gem_install
58
65
  Logger.info "Installing gem: #{gem_filename}"
59
66
  result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "package_install", gem_filename, Time.now + 3600.0, scope: scope)
@@ -118,5 +125,15 @@ module OpenC3
118
125
  version = File.basename(split_name[-1], '.gem')
119
126
  return gem_name, version
120
127
  end
128
+
129
+ def self.destroy_all_other_versions(name)
130
+ keep_gem_name, keep_gem_version = GemModel.extract_name_and_version(name)
131
+ GemModel.names.each do |gem_full_name|
132
+ gem_name, gem_version = GemModel.extract_name_and_version(gem_full_name)
133
+ if gem_name == keep_gem_name and gem_version != keep_gem_version
134
+ GemModel.destroy(gem_full_name)
135
+ end
136
+ end
137
+ end
121
138
  end
122
139
  end
@@ -185,7 +185,7 @@ module OpenC3
185
185
  @topics << parameters[0]
186
186
  when 'TARGET_NAME'
187
187
  parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
188
- @target_names << parameters[0]
188
+ @target_names << parameters[0].upcase
189
189
  when 'CMD'
190
190
  parser.verify_num_parameters(1, nil, "#{keyword} <Args>")
191
191
  @cmd = parameters.dup
@@ -158,6 +158,11 @@ module OpenC3
158
158
  gem_file_path = OpenC3::GemModel.get(gem_name)
159
159
  end
160
160
 
161
+ # Attempt to remove all older versions of this same plugin before install to prevent version conflicts
162
+ # Especially on downgrades
163
+ # Leave the same version if it already exists
164
+ OpenC3::GemModel.destroy_all_other_versions(File.basename(gem_file_path))
165
+
161
166
  # Actually install the gem now (slow)
162
167
  OpenC3::GemModel.install(gem_file_path, scope: scope) unless validate_only
163
168
 
@@ -356,7 +361,6 @@ module OpenC3
356
361
  # Reinstall
357
362
  def restore
358
363
  plugin_hash = self.as_json(:allow_nan => true)
359
- plugin_hash['name'] = plugin_hash['name'].split("__")[0]
360
364
  OpenC3::PluginModel.install_phase2(plugin_hash, scope: @scope)
361
365
  @destroyed = false
362
366
  end
@@ -742,7 +742,7 @@ module OpenC3
742
742
  end
743
743
  end
744
744
 
745
- def update_store(system)
745
+ def update_target_model(system)
746
746
  target = system.targets[@name]
747
747
 
748
748
  # Add in the information from the target and update
@@ -754,10 +754,11 @@ module OpenC3
754
754
  @tlm_unique_id_mode = target.tlm_unique_id_mode
755
755
  @limits_groups = system.limits.groups.keys
756
756
  update()
757
+ end
757
758
 
758
- # Store Packet Definitions
759
- system.telemetry.all.each do |target_name, packets|
760
- Store.del("#{@scope}__openc3tlm__#{target_name}")
759
+ def update_store_telemetry(packet_hash, clear_old: true)
760
+ packet_hash.each do |target_name, packets|
761
+ Store.del("#{@scope}__openc3tlm__#{target_name}") if clear_old
761
762
  packets.each do |packet_name, packet|
762
763
  Logger.info "Configuring tlm packet: #{target_name} #{packet_name}"
763
764
  begin
@@ -773,8 +774,11 @@ module OpenC3
773
774
  CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: @scope)
774
775
  end
775
776
  end
776
- system.commands.all.each do |target_name, packets|
777
- Store.del("#{@scope}__openc3cmd__#{target_name}")
777
+ end
778
+
779
+ def update_store_commands(packet_hash, clear_old: true)
780
+ packet_hash.each do |target_name, packets|
781
+ Store.del("#{@scope}__openc3cmd__#{target_name}") if clear_old
778
782
  packets.each do |packet_name, packet|
779
783
  Logger.info "Configuring cmd packet: #{target_name} #{packet_name}"
780
784
  begin
@@ -785,7 +789,9 @@ module OpenC3
785
789
  end
786
790
  end
787
791
  end
788
- # Store Limits Groups
792
+ end
793
+
794
+ def update_store_limits_groups(system)
789
795
  system.limits.groups.each do |group, items|
790
796
  begin
791
797
  Store.hset("#{@scope}__limits_groups", group, JSON.generate(items))
@@ -794,23 +800,77 @@ module OpenC3
794
800
  raise err
795
801
  end
796
802
  end
797
- # Merge in Limits Sets
803
+ end
804
+
805
+ def update_store_limits_sets(system)
798
806
  sets = Store.hgetall("#{@scope}__limits_sets")
799
807
  sets ||= {}
800
808
  system.limits.sets.each do |set|
801
809
  sets[set.to_s] = "false" unless sets.key?(set.to_s)
802
810
  end
803
811
  Store.hmset("#{@scope}__limits_sets", *sets)
812
+ end
804
813
 
814
+ def update_store_item_map
805
815
  # Create item_map
806
816
  item_map_key = "#{@scope}__#{@name}__item_to_packet_map"
807
817
  item_map = self.class.build_item_to_packet_map(@name, scope: @scope)
808
818
  Store.set(item_map_key, JSON.generate(item_map, :allow_nan => true))
809
819
  @@item_map_cache[@name] = [Time.now, item_map]
820
+ end
810
821
 
822
+ def update_store(system, clear_old: true)
823
+ update_target_model(system)
824
+ update_store_telemetry(system.telemetry.all, clear_old: clear_old)
825
+ update_store_commands(system.commands.all, clear_old: clear_old)
826
+ update_store_limits_groups(system)
827
+ update_store_limits_sets(system)
828
+ update_store_item_map()
811
829
  return system
812
830
  end
813
831
 
832
+ def dynamic_update(packets, cmd_or_tlm = :TELEMETRY, filename = "dynamic_tlm.txt")
833
+ # Build hash of targets/packets
834
+ packet_hash = {}
835
+ packets.each do |packet|
836
+ target_name = packet.target_name.upcase
837
+ packet_hash[target_name] ||= {}
838
+ packet_name = packet.packet_name.upcase
839
+ packet_hash[target_name][packet_name] = packet
840
+ end
841
+
842
+ # Update Redis
843
+ if cmd_or_tlm == :TELEMETRY
844
+ update_store_telemetry(packet_hash, clear_old: false)
845
+ update_store_item_map()
846
+ else
847
+ update_store_commands(packet_hash, clear_old: false)
848
+ end
849
+
850
+ # Build dynamic file for cmd_tlm
851
+ configs = {}
852
+ packets.each do |packet|
853
+ target_name = packet.target_name.upcase
854
+ configs[target_name] ||= ""
855
+ config = configs[target_name]
856
+ config << packet.to_config(cmd_or_tlm)
857
+ config << "\n"
858
+ end
859
+ configs.each do |target_name, config|
860
+ begin
861
+ bucket_key = "#{@scope}/targets_modified/#{target_name}/cmd_tlm/#{filename}"
862
+ client = Bucket.getClient()
863
+ client.put_object(
864
+ # Use targets_modified to save modifications
865
+ # This keeps the original target clean (read-only)
866
+ bucket: ENV['OPENC3_CONFIG_BUCKET'],
867
+ key: bucket_key,
868
+ body: config
869
+ )
870
+ end
871
+ end
872
+ end
873
+
814
874
  def deploy_commmandlog_microservice(gem_path, variables, topics, instance = nil, parent = nil)
815
875
  microservice_name = "#{@scope}__COMMANDLOG#{instance}__#{@name}"
816
876
  microservice = MicroserviceModel.new(
@@ -990,6 +1050,7 @@ module OpenC3
990
1050
  cmd: ["ruby", "multi_microservice.rb", *@children],
991
1051
  work_dir: '/openc3/lib/openc3/microservices',
992
1052
  plugin: @plugin,
1053
+ needs_dependencies: @needs_dependencies,
993
1054
  scope: @scope
994
1055
  )
995
1056
  microservice.create