openc3 5.14.0 → 5.14.2

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +19 -15
  3. data/bin/pipinstall +9 -0
  4. data/data/config/target_config.yaml +2 -1
  5. data/lib/openc3/config/config_parser.rb +12 -12
  6. data/lib/openc3/conversions/conversion.rb +4 -4
  7. data/lib/openc3/interfaces/interface.rb +25 -18
  8. data/lib/openc3/microservices/decom_microservice.rb +4 -2
  9. data/lib/openc3/microservices/interface_microservice.rb +27 -23
  10. data/lib/openc3/microservices/log_microservice.rb +6 -1
  11. data/lib/openc3/microservices/microservice.rb +28 -0
  12. data/lib/openc3/models/microservice_model.rb +5 -1
  13. data/lib/openc3/models/plugin_model.rb +11 -7
  14. data/lib/openc3/models/target_model.rb +36 -4
  15. data/lib/openc3/operators/microservice_operator.rb +23 -21
  16. data/lib/openc3/packets/commands.rb +4 -0
  17. data/lib/openc3/packets/limits_response.rb +1 -1
  18. data/lib/openc3/packets/packet_config.rb +78 -31
  19. data/lib/openc3/packets/packet_item.rb +3 -2
  20. data/lib/openc3/packets/packet_item_limits.rb +2 -1
  21. data/lib/openc3/packets/parsers/limits_response_parser.rb +21 -10
  22. data/lib/openc3/packets/parsers/processor_parser.rb +19 -10
  23. data/lib/openc3/packets/telemetry.rb +4 -0
  24. data/lib/openc3/system/system.rb +31 -1
  25. data/lib/openc3/system/system_config.rb +9 -10
  26. data/lib/openc3/system/target.rb +6 -6
  27. data/lib/openc3/utilities/cli_generator.rb +18 -3
  28. data/lib/openc3/utilities/python_proxy.rb +52 -0
  29. data/lib/openc3/version.rb +5 -5
  30. data/templates/conversion/conversion.py +1 -1
  31. data/templates/conversion/conversion.rb +1 -1
  32. data/templates/limits_response/response.py +2 -2
  33. data/templates/limits_response/response.rb +2 -2
  34. data/templates/microservice/microservices/TEMPLATE/microservice.py +6 -3
  35. data/templates/target/targets/TARGET/lib/target.py +2 -2
  36. data/templates/target/targets/TARGET/target.txt +2 -0
  37. data/templates/tool_angular/package.json +2 -2
  38. data/templates/tool_svelte/package.json +1 -1
  39. data/templates/tool_svelte/src/services/config-parser.js +5 -6
  40. data/templates/tool_svelte/src/services/openc3-api.js +9 -1
  41. data/templates/tool_vue/package.json +2 -2
  42. data/templates/widget/package.json +2 -2
  43. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b009f636dd8566127001ad9ee48972d584fb03baaf88389646048e7231dc54b
4
- data.tar.gz: 0ce2ff7aad089eba4794f4f15075bcf5e3087632f776eb76127c80af5256bda6
3
+ metadata.gz: 0eaf09072832c752cd1c0ae08160f9a5ef5bb24e585f10ee8be31d22c54ce2e7
4
+ data.tar.gz: 17fbccdaf92dd9c6e7fae704ca4bacadef88b026c5023d7dbb69583a19f6fe3e
5
5
  SHA512:
6
- metadata.gz: 1d87b341b985cca628485419b0406b449289364497e4061024d0407551920fc8f62529fb35897761a1ae471c838cf2ce38d706fcf84eea4960abdd24a7608af5
7
- data.tar.gz: '0758dfda18babfc4c47f870efe074e5a6bbd371aee1ba2a362f3d851791f20c2f5955e7f144972198a461a314b0a2a334c02d8191c7387503c30a11d8bd53c50'
6
+ metadata.gz: 92e1bfa1bbd283c4e94d49627ea8bfe6be9423ff88bc1a5453af778fb10dc3cefd78b2cef6b553fa6d1aa6f21559e70413e5343ec546b93841c62c124a3840a1
7
+ data.tar.gz: ac243f0b29ee7cccd5c809c5625a084cb54a3920b283402ddc7da8ce4178816d9ce75d44cb8af8154438a846ff116dec41b27cd5623ef5ded506c0c2d320e590
data/bin/openc3cli CHANGED
@@ -50,9 +50,9 @@ $redis_url = "redis://#{ENV['OPENC3_REDIS_HOSTNAME']}:#{ENV['OPENC3_REDIS_PORT']
50
50
  # Build the OpenStruct and OptionParser here as constants so we can use in methods
51
51
  MIGRATE_OPTIONS = OpenStruct.new
52
52
  MIGRATE_OPTIONS.all = false
53
- MIGRATE_PARSER = OptionParser.new do |op|
54
- op.banner = "cli migrate PLUGIN [TGT1...] # Create a OpenC3 plugin from existing COSMOS 4 targets"
55
- op.on("-a", "--all", " Move all COSMOS 4 targets into a single OpenC3 plugin") do
53
+ MIGRATE_PARSER = OptionParser.new do |opts|
54
+ opts.banner = "cli migrate PLUGIN [TGT1...] # Create a OpenC3 plugin from existing COSMOS 4 targets"
55
+ opts.on("-a", "--all", " Move all COSMOS 4 targets into a single OpenC3 plugin") do
56
56
  MIGRATE_OPTIONS.all = true
57
57
  end
58
58
  end
@@ -67,7 +67,7 @@ def print_usage
67
67
  puts " cli validate /PATH/FILENAME.gem SCOPE variables.txt # Validate a COSMOS plugin gem file"
68
68
  puts " cli load /PATH/FILENAME.gem SCOPE variables.txt # Loads a COSMOS plugin gem file"
69
69
  puts " cli generate TYPE OPTIONS # Generate various COSMOS entities"
70
- puts " OPTIONS includes either --ruby or --python to specify the language in the generated code"
70
+ puts " OPTIONS: --ruby or --python to specify the language in the generated code"
71
71
  puts " #{MIGRATE_PARSER}"
72
72
  puts " cli bridge CONFIG_FILENAME # Run COSMOS host bridge"
73
73
  puts " cli bridgegem gem_name variable1=value1 variable2=value2 # Runs bridge using gem bridge.txt"
@@ -208,31 +208,31 @@ end
208
208
 
209
209
  def xtce_converter(args)
210
210
  options = {}
211
- option_parser = OptionParser.new do |option_parser|
212
- option_parser.banner = "Usage: xtce_converter [options] --import input_xtce_filename --output output_dir\n"+
211
+ option_parser = OptionParser.new do |opts|
212
+ opts.banner = "Usage: xtce_converter [options] --import input_xtce_filename --output output_dir\n"+
213
213
  " xtce_converter [options] --plugin /PATH/FILENAME.gem --output output_dir --variables variables.txt"
214
- option_parser.separator("")
215
- option_parser.on("-h", "--help", "Show this message") do
216
- puts option_parser
214
+ opts.separator("")
215
+ opts.on("-h", "--help", "Show this message") do
216
+ puts opts
217
217
  exit
218
218
  end
219
- option_parser.on("-i VALUE", "--import VALUE", "Import the specified .xtce file") do |arg|
219
+ opts.on("-i VALUE", "--import VALUE", "Import the specified .xtce file") do |arg|
220
220
  options[:import] = arg
221
221
  end
222
- option_parser.on("-o", "--output DIRECTORY", "Create files in the directory") do |arg|
222
+ opts.on("-o", "--output DIRECTORY", "Create files in the directory") do |arg|
223
223
  options[:output] = arg
224
224
  end
225
- option_parser.on("-p", "--plugin PLUGIN", "Export .xtce file(s) from the plugin") do |arg|
225
+ opts.on("-p", "--plugin PLUGIN", "Export .xtce file(s) from the plugin") do |arg|
226
226
  options[:plugin] = arg
227
227
  end
228
- option_parser.on("-v", "--variables", "Optional variables file to pass to the plugin") do |arg|
228
+ opts.on("-v", "--variables", "Optional variables file to pass to the plugin") do |arg|
229
229
  options[:variables] = arg
230
230
  end
231
231
  end
232
232
 
233
233
  begin
234
234
  option_parser.parse!(args)
235
- rescue => e
235
+ rescue
236
236
  abort(option_parser.to_s)
237
237
  end
238
238
 
@@ -641,7 +641,11 @@ if not ARGV[0].nil? # argument(s) given
641
641
  IRB.start
642
642
 
643
643
  when 'rake'
644
- puts `rake #{ARGV[1..-1].join(' ')}`
644
+ if File.exist?('Rakefile')
645
+ puts `rake #{ARGV[1..-1].join(' ')}`
646
+ else
647
+ puts "No Rakefile found! Only run 'rake' in the presense of a Rakefile which is typically at the root of your COSMOS project."
648
+ end
645
649
 
646
650
  when 'validate'
647
651
  validate_plugin(ARGV[1], scope: ARGV[2], variables_file: ARGV[3])
data/bin/pipinstall ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ pip install "$@"
4
+ if [ $? -eq 0 ]; then
5
+ echo "Command succeeded"
6
+ else
7
+ echo "Command failed - retrying with --no-index"
8
+ pip install --no-index "$@"
9
+ fi
@@ -13,7 +13,8 @@ LANGUAGE:
13
13
  since: 5.11.1
14
14
  REQUIRE:
15
15
  summary: Requires a Ruby file
16
- description: Ruby files optionally be required to explicitly declare dependencies.
16
+ description: List the Ruby files required to explicitly declare dependencies.
17
+ This is now completely optional.
17
18
  example: REQUIRE limits_response.rb
18
19
  parameters:
19
20
  - name: Filename
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
17
+ # All changes Copyright 2024, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -143,7 +143,7 @@ module OpenC3
143
143
  end
144
144
 
145
145
  # @param url [String] The url to link to in error messages
146
- def initialize(url = "https:/openc3.com/docs/v5")
146
+ def initialize(url = "https://docs.openc3.com/docs")
147
147
  @url = url
148
148
  end
149
149
 
@@ -199,7 +199,7 @@ module OpenC3
199
199
  remove_quotes = true,
200
200
  run_erb = true,
201
201
  variables = {},
202
- &block)
202
+ &)
203
203
  raise Error.new(self, "Configuration file #{filename} does not exist.") unless filename && File.exist?(filename)
204
204
 
205
205
  @filename = filename
@@ -219,7 +219,7 @@ module OpenC3
219
219
  remove_quotes,
220
220
  size,
221
221
  PARSING_REGEX,
222
- &block)
222
+ &)
223
223
  ensure
224
224
  file.close unless file.closed?
225
225
  end
@@ -267,7 +267,7 @@ module OpenC3
267
267
  end
268
268
  end
269
269
 
270
- # Converts a String containing '', 'NIL' or 'NULL' to nil Ruby primitive.
270
+ # Converts a String containing '', 'NIL', 'NULL', or 'NONE' to nil Ruby primitive.
271
271
  # All other arguments are simply returned.
272
272
  #
273
273
  # @param value [Object]
@@ -275,7 +275,7 @@ module OpenC3
275
275
  def self.handle_nil(value)
276
276
  if String === value
277
277
  case value.upcase
278
- when '', 'NIL', 'NULL'
278
+ when '', 'NIL', 'NULL', 'NONE'
279
279
  return nil
280
280
  end
281
281
  end
@@ -421,12 +421,12 @@ module OpenC3
421
421
  if type == 'MIN'
422
422
  value = -2**(bit_size - 1)
423
423
  else # 'MAX'
424
- value = 2**(bit_size - 1) - 1
424
+ value = (2**(bit_size - 1)) - 1
425
425
  end
426
426
  when :UINT
427
427
  # Default is 0 for 'MIN'
428
428
  if type == 'MAX'
429
- value = 2**bit_size - 1
429
+ value = (2**bit_size) - 1
430
430
  end
431
431
  when :FLOAT
432
432
  case bit_size
@@ -536,8 +536,8 @@ module OpenC3
536
536
  if yield_non_keyword_lines
537
537
  begin
538
538
  yield(@keyword, @parameters)
539
- rescue => error
540
- errors << error
539
+ rescue => e
540
+ errors << e
541
541
  end
542
542
  end
543
543
  @line = ''
@@ -569,8 +569,8 @@ module OpenC3
569
569
 
570
570
  begin
571
571
  yield(@keyword, @parameters)
572
- rescue => error
573
- errors << error
572
+ rescue => e
573
+ errors << e
574
574
  end
575
575
  @line = ''
576
576
  end
@@ -14,10 +14,10 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
17
+ # All changes Copyright 2024, 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
  module OpenC3
@@ -46,7 +46,7 @@ module OpenC3
46
46
  # conversion.
47
47
  # @param buffer [String] The packet buffer
48
48
  # @return The converted value
49
- def call(value, packet, buffer)
49
+ def call(_value, _packet, _buffer)
50
50
  raise "call method must be defined by subclass"
51
51
  end
52
52
 
@@ -61,7 +61,7 @@ module OpenC3
61
61
  " #{read_or_write}_CONVERSION #{self.class.name.class_name_to_filename}\n"
62
62
  end
63
63
 
64
- def as_json(*a)
64
+ def as_json(*_a)
65
65
  result = {}
66
66
  result['class'] = self.class.name.to_s
67
67
  result['converted_type'] = @converted_type if @converted_type
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2023, OpenC3, Inc.
17
+ # All changes Copyright 2024, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -205,8 +205,8 @@ module OpenC3
205
205
  else
206
206
  cmd(cmd_string)
207
207
  end
208
- rescue Exception => err
209
- Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{err.formatted}")
208
+ rescue Exception => e
209
+ Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{e.formatted}")
210
210
  end
211
211
  end
212
212
  end
@@ -238,7 +238,7 @@ module OpenC3
238
238
  raise "read_interface not defined by Interface"
239
239
  end
240
240
 
241
- def write_interface(data, extra = nil)
241
+ def write_interface(_data, _extra = nil)
242
242
  raise "write_interface not defined by Interface"
243
243
  end
244
244
 
@@ -305,10 +305,10 @@ module OpenC3
305
305
  Logger.warn("#{@name}: Interface unexpectedly requested disconnect") unless packet
306
306
  return packet
307
307
  end
308
- rescue Exception => err
308
+ rescue Exception => e
309
309
  Logger.error("#{@name}: Error reading from interface")
310
310
  disconnect()
311
- raise err
311
+ raise e
312
312
  end
313
313
 
314
314
  # Method to send a packet on the interface.
@@ -395,16 +395,16 @@ module OpenC3
395
395
  else
396
396
  @write_mutex.synchronize { yield }
397
397
  end
398
- rescue WriteRejectError => err
399
- Logger.error("#{@name}: Write rejected by interface: #{err.message}")
400
- raise err
401
- rescue Exception => err
398
+ rescue WriteRejectError => e
399
+ Logger.error("#{@name}: Write rejected by interface: #{e.message}")
400
+ raise e
401
+ rescue Exception => e
402
402
  Logger.error("#{@name}: Error writing to interface")
403
403
  disconnect()
404
- raise err
404
+ raise e
405
405
  end
406
406
 
407
- def as_json(*a)
407
+ def as_json(*_a)
408
408
  config = {}
409
409
  config['name'] = @name
410
410
  config['state'] = @state
@@ -528,7 +528,7 @@ module OpenC3
528
528
  # method is called. Subclasses must implement this method.
529
529
  #
530
530
  # @return [String] Raw packet data
531
- def read_interface_base(data, extra = nil)
531
+ def read_interface_base(data, _extra = nil)
532
532
  @read_raw_data_time = Time.now
533
533
  @read_raw_data = data.clone
534
534
  @bytes_read += data.length
@@ -541,7 +541,7 @@ module OpenC3
541
541
  #
542
542
  # @param data [String] Raw packet data
543
543
  # @return [String] The exact data written
544
- def write_interface_base(data, extra = nil)
544
+ def write_interface_base(data, _extra = nil)
545
545
  @written_raw_data_time = Time.now
546
546
  @written_raw_data = data.clone
547
547
  @bytes_written += data.length
@@ -567,9 +567,16 @@ module OpenC3
567
567
  return protocol
568
568
  end
569
569
 
570
- def interface_cmd(cmd_name, *cmd_args)
571
- # Default do nothing - Implemented by subclasses
572
- return false
570
+ def interface_cmd(cmd_name, *_cmd_args)
571
+ case cmd_name
572
+ when 'clear_counters'
573
+ @write_queue_size = 0
574
+ @read_queue_size = 0
575
+ @bytes_written = 0
576
+ @bytes_read = 0
577
+ @write_count = 0
578
+ @read_count = 0
579
+ end
573
580
  end
574
581
 
575
582
  def protocol_cmd(cmd_name, *cmd_args, read_write: :READ_WRITE, index: -1)
@@ -584,7 +591,7 @@ module OpenC3
584
591
  write_protocols = @write_protocols.reverse
585
592
  read_index = 0
586
593
  write_index = 0
587
- @protocol_info.each do |protocol_class, protocol_args, protocol_read_write|
594
+ @protocol_info.each do |_protocol_class, _protocol_args, protocol_read_write|
588
595
  case protocol_read_write
589
596
  when :READ
590
597
  protocols << read_protocols[read_index]
@@ -45,6 +45,7 @@ module OpenC3
45
45
  end
46
46
 
47
47
  def run
48
+ setup_microservice_topic()
48
49
  while true
49
50
  break if @cancel_thread
50
51
 
@@ -52,8 +53,9 @@ module OpenC3
52
53
  OpenC3.in_span("read_topics") do
53
54
  Topic.read_topics(@topics) do |topic, msg_id, msg_hash, redis|
54
55
  break if @cancel_thread
55
-
56
- if topic =~ /__DECOMINTERFACE/
56
+ if topic == @microservice_topic
57
+ microservice_cmd(topic, msg_id, msg_hash, redis)
58
+ elsif topic =~ /__DECOMINTERFACE/
57
59
  if msg_hash.key?('inject_tlm')
58
60
  handle_inject_tlm(msg_hash['inject_tlm'])
59
61
  next
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2023, OpenC3, Inc.
17
+ # All changes Copyright 2024, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -52,9 +52,9 @@ module OpenC3
52
52
  def start
53
53
  @thread = Thread.new do
54
54
  run()
55
- rescue Exception => err
56
- @logger.error "#{@interface.name}: Command handler thread died: #{err.formatted}"
57
- raise err
55
+ rescue Exception => e
56
+ @logger.error "#{@interface.name}: Command handler thread died: #{e.formatted}"
57
+ raise e
58
58
  end
59
59
  end
60
60
 
@@ -67,7 +67,7 @@ module OpenC3
67
67
  end
68
68
 
69
69
  def run
70
- InterfaceTopic.receive_commands(@interface, scope: @scope) do |topic, msg_id, msg_hash, redis|
70
+ InterfaceTopic.receive_commands(@interface, scope: @scope) do |topic, msg_id, msg_hash, _redis|
71
71
  OpenC3.with_context(msg_hash) do
72
72
  msgid_seconds_from_epoch = msg_id.split('-')[0].to_i / 1000.0
73
73
  delta = Time.now.to_f - msgid_seconds_from_epoch
@@ -126,6 +126,7 @@ module OpenC3
126
126
  begin
127
127
  @logger.info "#{@interface.name}: interface_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')}"
128
128
  @interface.interface_cmd(params['cmd_name'], *params['cmd_params'])
129
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
129
130
  rescue => e
130
131
  @logger.error "#{@interface.name}: interface_cmd: #{e.formatted}"
131
132
  next e.message
@@ -137,6 +138,7 @@ module OpenC3
137
138
  begin
138
139
  @logger.info "#{@interface.name}: protocol_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')} read_write: #{params['read_write']} index: #{params['index']}"
139
140
  @interface.protocol_cmd(params['cmd_name'], *params['cmd_params'], read_write: params['read_write'], index: params['index'])
141
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
140
142
  rescue => e
141
143
  @logger.error "#{@interface.name}: protocol_cmd: #{e.formatted}"
142
144
  next e.message
@@ -241,9 +243,9 @@ module OpenC3
241
243
  def start
242
244
  @thread = Thread.new do
243
245
  run()
244
- rescue Exception => err
245
- @logger.error "#{@router.name}: Telemetry handler thread died: #{err.formatted}"
246
- raise err
246
+ rescue Exception => e
247
+ @logger.error "#{@router.name}: Telemetry handler thread died: #{e.formatted}"
248
+ raise e
247
249
  end
248
250
  end
249
251
 
@@ -256,7 +258,7 @@ module OpenC3
256
258
  end
257
259
 
258
260
  def run
259
- RouterTopic.receive_telemetry(@router, scope: @scope) do |topic, msg_id, msg_hash, redis|
261
+ RouterTopic.receive_telemetry(@router, scope: @scope) do |topic, msg_id, msg_hash, _redis|
260
262
  msgid_seconds_from_epoch = msg_id.split('-')[0].to_i / 1000.0
261
263
  delta = Time.now.to_f - msgid_seconds_from_epoch
262
264
  @metric.set(name: 'router_topic_delta_seconds', value: delta, type: 'gauge', unit: 'seconds', help: 'Delta time between data written to stream and router tlm start') if @metric
@@ -296,6 +298,7 @@ module OpenC3
296
298
  begin
297
299
  @logger.info "#{@router.name}: router_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')}"
298
300
  @router.interface_cmd(params['cmd_name'], *params['cmd_params'])
301
+ RouterStatusModel.set(@router.as_json(:allow_nan => true), scope: @scope)
299
302
  rescue => e
300
303
  @logger.error "#{@router.name}: router_cmd: #{e.formatted}"
301
304
  next e.message
@@ -307,6 +310,7 @@ module OpenC3
307
310
  begin
308
311
  @logger.info "#{@router.name}: protocol_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')} read_write: #{params['read_write']} index: #{params['index']}"
309
312
  @router.protocol_cmd(params['cmd_name'], *params['cmd_params'], read_write: params['read_write'], index: params['index'])
313
+ RouterStatusModel.set(@router.as_json(:allow_nan => true), scope: @scope)
310
314
  rescue => e
311
315
  @logger.error "#{@router.name}: protoco_cmd: #{e.formatted}"
312
316
  next e.message
@@ -434,9 +438,9 @@ module OpenC3
434
438
  end
435
439
  @interface # Return the interface/router since we may have recreated it
436
440
  # Need to rescue Exception so we cover LoadError
437
- rescue Exception => error
438
- @logger.error("Attempting connection failed with params #{params} due to #{error.message}")
439
- if SignalException === error
441
+ rescue Exception => e
442
+ @logger.error("Attempting connection failed with params #{params} due to #{e.message}")
443
+ if SignalException === e
440
444
  @logger.info "#{@interface.name}: Closing from signal"
441
445
  @cancel_thread = true
442
446
  end
@@ -463,8 +467,8 @@ module OpenC3
463
467
  # We need to make sure connect is not called after stop() has been called
464
468
  connect() unless @cancel_thread
465
469
  end
466
- rescue Exception => connect_error
467
- handle_connection_failed(connect_error)
470
+ rescue Exception => e
471
+ handle_connection_failed(e)
468
472
  break if @cancel_thread
469
473
  end
470
474
  when 'CONNECTED'
@@ -484,8 +488,8 @@ module OpenC3
484
488
  handle_connection_lost()
485
489
  break if @cancel_thread
486
490
  end
487
- rescue Exception => err
488
- handle_connection_lost(err)
491
+ rescue Exception => e
492
+ handle_connection_lost(e)
489
493
  break if @cancel_thread
490
494
  end
491
495
  else
@@ -494,10 +498,10 @@ module OpenC3
494
498
  end
495
499
  end
496
500
  end
497
- rescue Exception => error
498
- unless SystemExit === error or SignalException === error
499
- @logger.error "#{@interface.name}: Packet reading thread died: #{error.formatted}"
500
- OpenC3.handle_fatal_exception(error)
501
+ rescue Exception => e
502
+ unless SystemExit === e or SignalException === e
503
+ @logger.error "#{@interface.name}: Packet reading thread died: #{e.formatted}"
504
+ OpenC3.handle_fatal_exception(e)
501
505
  end
502
506
  # Try to do clean disconnect because we're going down
503
507
  disconnect(false)
@@ -615,13 +619,13 @@ module OpenC3
615
619
  @logger.info "#{@interface.name}: Connecting ..."
616
620
  begin
617
621
  @interface.connect
618
- rescue Exception => error
622
+ rescue Exception => e
619
623
  begin
620
624
  @interface.disconnect # Ensure disconnect is called at least once on a partial connect
621
625
  rescue Exception
622
626
  # We want to report any connect errors, not disconnect in this case
623
627
  end
624
- raise error
628
+ raise e
625
629
  end
626
630
  @interface.state = 'CONNECTED'
627
631
  if @interface_or_router == 'INTERFACE'
@@ -685,7 +689,7 @@ module OpenC3
685
689
  end
686
690
  end
687
691
 
688
- def shutdown(sig = nil)
692
+ def shutdown(_sig = nil)
689
693
  @logger.info "#{@interface ? @interface.name : @name}: shutdown requested"
690
694
  stop()
691
695
  super()
@@ -62,12 +62,17 @@ module OpenC3
62
62
 
63
63
  def run
64
64
  setup_plws()
65
+ setup_microservice_topic()
65
66
  while true
66
67
  break if @cancel_thread
67
68
 
68
69
  Topic.read_topics(@topics) do |topic, msg_id, msg_hash, redis|
69
70
  break if @cancel_thread
70
- log_data(topic, msg_id, msg_hash, redis)
71
+ if topic == @microservice_topic
72
+ microservice_cmd(topic, msg_id, msg_hash, redis)
73
+ else
74
+ log_data(topic, msg_id, msg_hash, redis)
75
+ end
71
76
  @count += 1
72
77
  @metric.set(name: 'log_total', value: @count, type: 'counter')
73
78
  end
@@ -116,6 +116,7 @@ module OpenC3
116
116
  end
117
117
  @logger.info("Microservice initialized with config:\n#{@config}")
118
118
  @topics ||= []
119
+ @microservice_topic = "MICROSERVICE__#{@name}"
119
120
 
120
121
  # Get configuration for any targets
121
122
  @target_names = @config["target_names"]
@@ -210,5 +211,32 @@ module OpenC3
210
211
  @logger.info("Shutting down microservice complete: #{@name}")
211
212
  @shutdown_complete = true
212
213
  end
214
+
215
+ def setup_microservice_topic
216
+ @topics.append(@microservice_topic)
217
+ Thread.current[:topic_offsets] ||= {}
218
+ topic_offsets = Thread.current[:topic_offsets]
219
+ topic_offsets[@microservice_topic] = "0-0" # Always get all available
220
+ end
221
+
222
+ # Returns if the command was handled
223
+ def microservice_cmd(topic, msg_id, msg_hash, redis)
224
+ command = msg_hash['command']
225
+ case command
226
+ when 'ADD_TOPICS'
227
+ topics = JSON.parse(msg_hash['topics'])
228
+ if topics and Array === topics
229
+ topics.each do |new_topic|
230
+ @topics << new_topic unless @topics.include?(new_topic)
231
+ end
232
+ else
233
+ raise "Invalid topics given to microservice_cmd: #{topics}"
234
+ end
235
+ Topic.trim_topic(topic, msg_id)
236
+ return true
237
+ end
238
+ Topic.trim_topic(topic, msg_id)
239
+ return false
240
+ end
213
241
  end
214
242
  end
@@ -43,6 +43,7 @@ module OpenC3
43
43
  attr_accessor :secrets
44
44
  attr_accessor :prefix
45
45
  attr_accessor :disable_erb
46
+ attr_accessor :ignore_changes
46
47
 
47
48
  # NOTE: The following three class methods are used by the ModelController
48
49
  # and are reimplemented to enable various Model class methods to work
@@ -103,6 +104,7 @@ module OpenC3
103
104
  secrets: [],
104
105
  prefix: nil,
105
106
  disable_erb: nil,
107
+ ignore_changes: nil,
106
108
  scope:
107
109
  )
108
110
  parts = name.split("__")
@@ -128,6 +130,7 @@ module OpenC3
128
130
  @secrets = secrets
129
131
  @prefix = prefix
130
132
  @disable_erb = disable_erb
133
+ @ignore_changes = ignore_changes
131
134
  @bucket = Bucket.getClient()
132
135
  end
133
136
 
@@ -149,7 +152,8 @@ module OpenC3
149
152
  'needs_dependencies' => @needs_dependencies,
150
153
  'secrets' => @secrets.as_json(*a),
151
154
  'prefix' => @prefix,
152
- 'disable_erb' => @disable_erb
155
+ 'disable_erb' => @disable_erb,
156
+ 'ignore_changes' => @ignore_changes
153
157
  }
154
158
  end
155
159
 
@@ -183,13 +183,17 @@ module OpenC3
183
183
  if File.exist?(File.join(gem_path, 'requirements.txt'))
184
184
  begin
185
185
  pypi_url = get_setting('pypi_url', scope: scope)
186
- rescue
187
- # If Redis isn't running try the ENV, then simply pypi.org/simple
188
- pypi_url = ENV['PYPI_URL']
189
- pypi_url ||= 'https://pypi.org/simple'
186
+ rescue => e
187
+ Logger.error("Failed to retrieve pypi_url: #{e.formatted}")
188
+ ensure
189
+ if pypi_url.nil?
190
+ # If Redis isn't running try the ENV, then simply pypi.org/simple
191
+ pypi_url = ENV['PYPI_URL']
192
+ pypi_url ||= 'https://pypi.org/simple'
193
+ end
190
194
  end
191
- Logger.info "Installing python packages from requirements.txt"
192
- puts `pip install --user -i #{pypi_url} -r #{File.join(gem_path, 'requirements.txt')}`
195
+ Logger.info "Installing python packages from requirements.txt with pypi_url=#{pypi_url}"
196
+ puts `/openc3/bin/pipinstall --user -i #{pypi_url} -r #{File.join(gem_path, 'requirements.txt')}`
193
197
  needs_dependencies = true
194
198
  end
195
199
 
@@ -342,7 +346,7 @@ module OpenC3
342
346
  raise message
343
347
  end
344
348
  rescue Exception => error
345
- Logger.error("Error undeploying plugin model #{@name} in scope #{@scope} due to: #{error}")
349
+ Logger.error("Error undeploying plugin model #{@name} in scope #{@scope} due to: #{error.formatted}")
346
350
  ensure
347
351
  # Double check everything is gone
348
352
  found = []