openc3 7.0.0.pre.rc2 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +13 -4
  3. data/bin/pipinstall +6 -7
  4. data/bin/pipuninstall +3 -5
  5. data/data/config/interface_modifiers.yaml +1 -1
  6. data/data/config/item_modifiers.yaml +18 -6
  7. data/data/config/telemetry.yaml +1 -1
  8. data/data/config/widgets.yaml +10 -0
  9. data/lib/openc3/accessors/json_accessor.rb +1 -1
  10. data/lib/openc3/api/cmd_api.rb +2 -0
  11. data/lib/openc3/api/settings_api.rb +2 -0
  12. data/lib/openc3/api/tlm_api.rb +3 -3
  13. data/lib/openc3/config/config_parser.rb +4 -4
  14. data/lib/openc3/conversions/conversion.rb +3 -3
  15. data/lib/openc3/core_ext/faraday.rb +4 -0
  16. data/lib/openc3/logs/log_writer.rb +24 -6
  17. data/lib/openc3/logs/packet_log_writer.rb +1 -4
  18. data/lib/openc3/logs/stream_log_pair.rb +11 -4
  19. data/lib/openc3/logs/text_log_writer.rb +1 -4
  20. data/lib/openc3/microservices/interface_microservice.rb +8 -2
  21. data/lib/openc3/microservices/log_microservice.rb +7 -2
  22. data/lib/openc3/microservices/microservice.rb +10 -4
  23. data/lib/openc3/microservices/queue_microservice.rb +9 -2
  24. data/lib/openc3/microservices/scope_cleanup_microservice.rb +116 -1
  25. data/lib/openc3/microservices/text_log_microservice.rb +4 -1
  26. data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +1 -1
  27. data/lib/openc3/migrations/20250402000000_periodic_only_default.rb +1 -1
  28. data/lib/openc3/migrations/20260203000000_remove_store_id.rb +28 -0
  29. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +29 -1
  30. data/lib/openc3/models/activity_model.rb +41 -9
  31. data/lib/openc3/models/auth_model.rb +54 -19
  32. data/lib/openc3/models/cvt_model.rb +2 -265
  33. data/lib/openc3/models/model.rb +16 -0
  34. data/lib/openc3/models/plugin_model.rb +18 -12
  35. data/lib/openc3/models/plugin_store_model.rb +1 -1
  36. data/lib/openc3/models/python_package_model.rb +2 -2
  37. data/lib/openc3/models/queue_model.rb +5 -3
  38. data/lib/openc3/models/script_engine_model.rb +1 -1
  39. data/lib/openc3/models/target_model.rb +75 -42
  40. data/lib/openc3/models/tool_config_model.rb +12 -0
  41. data/lib/openc3/models/tool_model.rb +18 -5
  42. data/lib/openc3/models/trigger_model.rb +1 -1
  43. data/lib/openc3/models/widget_model.rb +2 -9
  44. data/lib/openc3/operators/operator.rb +9 -7
  45. data/lib/openc3/packets/json_packet.rb +2 -0
  46. data/lib/openc3/packets/packet.rb +1 -0
  47. data/lib/openc3/packets/packet_config.rb +28 -12
  48. data/lib/openc3/script/calendar.rb +8 -0
  49. data/lib/openc3/script/script.rb +19 -0
  50. data/lib/openc3/script/storage.rb +6 -6
  51. data/lib/openc3/script/web_socket_api.rb +1 -1
  52. data/lib/openc3/system/system.rb +6 -6
  53. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +0 -2
  54. data/lib/openc3/top_level.rb +15 -63
  55. data/lib/openc3/topics/command_topic.rb +1 -0
  56. data/lib/openc3/topics/limits_event_topic.rb +1 -1
  57. data/lib/openc3/utilities/authentication.rb +46 -7
  58. data/lib/openc3/utilities/authorization.rb +8 -1
  59. data/lib/openc3/utilities/aws_bucket.rb +2 -3
  60. data/lib/openc3/utilities/bucket_utilities.rb +3 -1
  61. data/lib/openc3/utilities/cli_generator.rb +7 -0
  62. data/lib/openc3/utilities/cmd_log.rb +1 -1
  63. data/lib/openc3/utilities/local_mode.rb +3 -0
  64. data/lib/openc3/utilities/process_manager.rb +1 -1
  65. data/lib/openc3/utilities/python_proxy.rb +11 -4
  66. data/lib/openc3/utilities/questdb_client.rb +764 -2
  67. data/lib/openc3/utilities/running_script.rb +25 -7
  68. data/lib/openc3/utilities/script.rb +452 -0
  69. data/lib/openc3/utilities/secrets.rb +1 -1
  70. data/lib/openc3/version.rb +5 -5
  71. data/templates/conversion/conversion.py +0 -8
  72. data/templates/conversion/conversion.rb +0 -11
  73. data/templates/tool_angular/package.json +2 -2
  74. data/templates/tool_react/package.json +1 -1
  75. data/templates/tool_svelte/package.json +1 -1
  76. data/templates/tool_vue/package.json +3 -3
  77. data/templates/widget/package.json +2 -2
  78. metadata +19 -19
  79. data/lib/openc3/migrations/20251022000000_remove_unique_id.rb +0 -23
  80. data/lib/openc3/migrations/20251213120000_reinstall_plugins.rb +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7c545191fe3248b560961c422b5906474a068d06df3d480a35a61dba621e469
4
- data.tar.gz: 77b9201a38591fad76787947918fd6910a8599e09368cdfdf9d0870520a9f073
3
+ metadata.gz: 0a06901f01bc9d8e7b6f0b123296da67475c08ea9cf50ef788a86b6da1308fcb
4
+ data.tar.gz: 34732b912cea1b4677f3e94ac0ba9f437e99217ccf54f9250816176ef5b63db1
5
5
  SHA512:
6
- metadata.gz: 164c14d7fd49d30bcaf8f8838cd2e3d63c3fbbccb514e7b5a40348868e68317100d8a48beda0a3015959d0f49d3093b40b1184e517093ae7a4df4aafb8710be0
7
- data.tar.gz: cb6276f37e41717565abbbb24d7df1006cf4dfbfdeff92c76b34638dff29279dcfed536aced5a488d1aacf63541568d3a0c8da4b7edd5ecaed9f792b3a12a7ae
6
+ metadata.gz: 5cda606ccb010606ad4b432685f53428cbf10b216f1cd06f2f55baac28fb723514dc3f8dfeadafc0958734237d3a7faf3f42439695481145914434e2a4bb1a58
7
+ data.tar.gz: 98532ea424c421554d45a6d155fadfc27e67ddf969a96be7653daebb30d5530dbb7601e13b5d3a3afa7dfd56718c161c060e81d67a5ee967a9248225554e1a03
data/bin/openc3cli CHANGED
@@ -1348,12 +1348,21 @@ if not ARGV[0].nil? # argument(s) given
1348
1348
  exit 0
1349
1349
  end
1350
1350
  client = OpenC3::Bucket.getClient()
1351
- ENV.map do |key, value|
1352
- if key.match(/^OPENC3_(.+)_BUCKET$/) && !value.empty?
1353
- client.create(value)
1351
+ if ENV.fetch('OPENC3_CLOUD', 'local') == 'local'
1352
+ # In local mode we want to create all buckets to ensure they exist
1353
+ # Cloud deployments will have created buckets during provisioning
1354
+ # so we only need to ensure the correct policies and permissions are in place
1355
+ ENV.map do |key, value|
1356
+ if key.match(/^OPENC3_(.+)_BUCKET$/) && !value.empty?
1357
+ client.create(value)
1358
+ end
1354
1359
  end
1355
1360
  end
1356
- client.ensure_public(ENV['OPENC3_TOOLS_BUCKET'])
1361
+ # Unless explicitly disabled, ensure the tools bucket is public
1362
+ unless ENV.fetch("OPENC3_NO_BUCKET_POLICY", false)
1363
+ client.ensure_public(ENV['OPENC3_TOOLS_BUCKET'])
1364
+ end
1365
+ # Always ensure the scriptrunner policy is in place since it is required for script execution
1357
1366
  client.ensure_scriptrunner_policy(ENV['OPENC3_CONFIG_BUCKET'], ENV['OPENC3_LOGS_BUCKET'])
1358
1367
 
1359
1368
  when 'runmigrations'
data/bin/pipinstall CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/bin/sh
2
- python3 -m venv $PYTHONUSERBASE
3
- source $PYTHONUSERBASE/bin/activate
4
- echo "pip3 install $@"
5
- pip3 install "$@"
2
+ uv venv "$PYTHONUSERBASE"
3
+ echo "uv pip install $@"
4
+ uv pip install --python "$PYTHONUSERBASE" "$@"
6
5
  if [ $? -eq 0 ]; then
7
6
  echo "Command succeeded"
8
7
  else
9
8
  echo "Command failed - retrying with --no-index"
10
- pip3 install --no-index "$@"
11
- if [ $? -eq 0 ]; then
12
- echo "ERROR: pip3 install failed"
9
+ uv pip install --python "$PYTHONUSERBASE" --no-index "$@"
10
+ if [ $? -ne 0 ]; then
11
+ echo "ERROR: uv pip install failed"
13
12
  fi
14
13
  fi
data/bin/pipuninstall CHANGED
@@ -1,10 +1,8 @@
1
1
  #!/bin/sh
2
- python3 -m venv $PYTHONUSERBASE
3
- source $PYTHONUSERBASE/bin/activate
4
- echo "pip3 uninstall $@"
5
- pip3 uninstall "$@"
2
+ echo "uv pip uninstall $@"
3
+ uv pip uninstall --python "$PYTHONUSERBASE" "$@"
6
4
  if [ $? -eq 0 ]; then
7
5
  echo "Command succeeded"
8
6
  else
9
- echo "ERROR: pip3 uninstall failed"
7
+ echo "ERROR: uv pip uninstall failed"
10
8
  fi
@@ -53,7 +53,7 @@ RECONNECT_DELAY:
53
53
  parameters:
54
54
  - name: Delay
55
55
  required: true
56
- description: Delay in seconds between reconnect attempts. The default is 15 seconds.
56
+ description: Delay in seconds between reconnect attempts. The default is 5 seconds.
57
57
  values: ([0-9]*[.])?[0-9]+
58
58
  DISABLE_DISCONNECT:
59
59
  summary: Disable the Disconnect button on the Interfaces tab in the Server
@@ -72,7 +72,8 @@ GENERIC_READ_CONVERSION_START:
72
72
  class (Note, referencing the packet as 'myself' is still supported for backwards
73
73
  compatibility). The last line of code should return the converted
74
74
  value. The GENERIC_READ_CONVERSION_END keyword specifies that all lines of
75
- code for the conversion have been given.
75
+ code for the conversion have been given. To specify the bit size, type, and array size of the converted data,
76
+ use the CONVERTED_DATA keyword.
76
77
  warning: Generic conversions are not a good long term solution. Consider creating
77
78
  a conversion class and using READ_CONVERSION instead. READ_CONVERSION is easier
78
79
  to debug and has higher performance.
@@ -81,22 +82,33 @@ GENERIC_READ_CONVERSION_START:
81
82
  GENERIC_READ_CONVERSION_START
82
83
  (value * 1.5).to_i # Convert the value by a scale factor
83
84
  GENERIC_READ_CONVERSION_END
85
+ CONVERTED_DATA 32 UINT
84
86
  python_example: |
85
87
  APPEND_ITEM ITEM1 32 UINT
86
88
  GENERIC_READ_CONVERSION_START
87
89
  int(value * 1.5) # Convert the value by a scale factor
88
90
  GENERIC_READ_CONVERSION_END
91
+ CONVERTED_DATA 32 UINT
92
+ GENERIC_READ_CONVERSION_END:
93
+ summary: Complete a generic read conversion
94
+ CONVERTED_DATA:
95
+ summary: Defines the bit size, type, and array size of the converted data for a read conversion
96
+ description: This keyword is used in conjunction with DERIVED items to specify the bit size, type, and array size of the converted data.
97
+ If this keyword is not used, DERIVED items are stored as strings in the decommutated data.
98
+ since: 7.0.0
89
99
  parameters:
100
+ - name: Converted Bit Size
101
+ required: true
102
+ description: Bit size of converted value
103
+ values: \d+
90
104
  - name: Converted Type
91
- required: false
105
+ required: true
92
106
  description: Type of the converted value
93
107
  values: <%= %w(INT UINT FLOAT STRING BLOCK) %>
94
- - name: Converted Bit Size
108
+ - name: Converted Array Size
95
109
  required: false
96
- description: Bit size of converted value
110
+ description: Bit size of the total array if the converted value is an array. Only specified if the converted type is an array.
97
111
  values: \d+
98
- GENERIC_READ_CONVERSION_END:
99
- summary: Complete a generic read conversion
100
112
  LIMITS:
101
113
  summary: Defines a set of limits for a telemetry item
102
114
  description: If limits are violated a message is printed in the Command and Telemetry Server
@@ -10,7 +10,7 @@ TELEMETRY:
10
10
  required: true
11
11
  description: Name of the target this telemetry packet is associated with
12
12
  values: .+
13
- - name: Command
13
+ - name: Packet
14
14
  required: true
15
15
  description:
16
16
  Name of this telemetry packet. Also referred to as its mnemonic.
@@ -894,6 +894,16 @@ Telemetry Widgets:
894
894
  END
895
895
  LIMITSCOLOR INST HEALTH_STATUS TEMP2 # Default is label with just item name
896
896
  LIMITSCOLOR INST HEALTH_STATUS TEMP3 CONVERTED 20 TRUE # Full TGT/PKT/ITEM label
897
+ LIMITSCOLOR INST HEALTH_STATUS TEMP4
898
+ SETTING ASTRO TRUE
899
+ settings:
900
+ ASTRO:
901
+ summary: Display Astro status icons instead of a colored circle
902
+ description:
903
+ When set, the LIMITSCOLOR renders an Astro (rux-status) icon whose shape reflects
904
+ the severity level, improving accessibility for colorblind users.
905
+ Limits colors are automatically mapped to Astro statuses
906
+ (GREEN to normal, RED to critical, YELLOW to caution, BLUE to standby).
897
907
  VALUELIMITSBAR:
898
908
  summary: Displays an item VALUE followed by LIMITSBAR
899
909
  parameters:
@@ -20,7 +20,7 @@ require 'openc3/accessors/accessor'
20
20
  OpenC3.disable_warnings do
21
21
  class JsonPath
22
22
  def self.process_object(obj_or_str, opts = {})
23
- obj_or_str.is_a?(String) ? MultiJson.load(obj_or_str, max_nesting: opts[:max_nesting], create_additions: true, allow_nan: true) : obj_or_str
23
+ obj_or_str.is_a?(String) ? JSON.parse(obj_or_str, max_nesting: opts[:max_nesting], create_additions: true, allow_nan: true) : obj_or_str
24
24
  end
25
25
  end
26
26
  end
@@ -545,6 +545,8 @@ module OpenC3
545
545
  target_name: target_name,
546
546
  cmd_name: cmd_name,
547
547
  cmd_params: cmd_params,
548
+ validate: validate,
549
+ timeout: timeout,
548
550
  username: username,
549
551
  scope: scope)
550
552
  else
@@ -73,6 +73,8 @@ module OpenC3
73
73
  authorize(permission: 'admin', manual: manual, scope: scope, token: token)
74
74
  SettingModel.set({ name: name, data: data }, scope: scope)
75
75
  LocalMode.save_setting(scope, name, data)
76
+ username = user_info(token)['username'] || 'Anonymous'
77
+ Logger.info("User #{username} saved setting '#{name}': #{data}", scope: scope, user: username)
76
78
  end
77
79
  # save_setting is DEPRECATED
78
80
  alias save_setting set_setting
@@ -23,7 +23,7 @@
23
23
 
24
24
  require 'openc3/models/target_model'
25
25
  require 'openc3/models/cvt_model'
26
- require 'openc3/packets/packet'
26
+ # require 'openc3/packets/packet' # Circular require
27
27
  require 'openc3/topics/telemetry_topic'
28
28
  require 'openc3/topics/interface_topic'
29
29
  require 'openc3/topics/decom_interface_topic'
@@ -281,7 +281,7 @@ module OpenC3
281
281
 
282
282
  case value_type
283
283
  when 'FORMATTED', 'WITH_UNITS'
284
- if item['format_string']
284
+ if item['format_string'] or item['units']
285
285
  results << [target_name, orig_packet_name, item_name, 'FORMATTED'].join('__')
286
286
  # This logic must match the logic in Packet#decom
287
287
  elsif item['states'] or (item['read_conversion'] and item['data_type'] != 'DERIVED')
@@ -304,7 +304,7 @@ module OpenC3
304
304
  if item['limits']['DEFAULT']
305
305
  results[-1] += '__LIMITS'
306
306
  end
307
- rescue RuntimeError => e
307
+ rescue RuntimeError
308
308
  results << nil
309
309
  end
310
310
  end
@@ -15,10 +15,11 @@
15
15
  # This file may also be used under the terms of a commercial license
16
16
  # if purchased from OpenC3, Inc.
17
17
 
18
- require 'openc3/top_level'
18
+ # require 'openc3/top_level' # Circular require
19
19
  require 'openc3/ext/config_parser' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
20
20
  require 'erb'
21
21
  require 'fileutils'
22
+ require 'tempfile'
22
23
 
23
24
  module OpenC3
24
25
  # Reads OpenC3 style configuration data which consists of keywords followed
@@ -218,6 +219,7 @@ module OpenC3
218
219
  &)
219
220
  ensure
220
221
  file.close unless file.closed?
222
+ file.unlink
221
223
  end
222
224
  end
223
225
 
@@ -417,9 +419,7 @@ module OpenC3
417
419
  elsif copy.include?(':') # Check for Windows drive letter
418
420
  copy = copy.split(':')[1]
419
421
  end
420
- parsed_filename = File.join(Dir.tmpdir, 'openc3', 'tmp', copy)
421
- FileUtils.mkdir_p(File.dirname(parsed_filename)) # Create the path
422
- file = File.open(parsed_filename, 'w+')
422
+ file = Tempfile.new(copy)
423
423
  file.puts output
424
424
  file.rewind # Rewind so the file is ready to read
425
425
  file
@@ -20,11 +20,11 @@ module OpenC3
20
20
  class Conversion
21
21
  # @return [Symbol] The converted data type. Must be one of
22
22
  # {OpenC3::StructureItem#data_type}
23
- attr_reader :converted_type
23
+ attr_accessor :converted_type
24
24
  # @return [Integer] The size in bits of the converted value
25
- attr_reader :converted_bit_size
25
+ attr_accessor :converted_bit_size
26
26
  # @return [Integer] The size in bits of the converted array value
27
- attr_reader :converted_array_size
27
+ attr_accessor :converted_array_size
28
28
  # @return [Array] The arguments passed to the conversion
29
29
  attr_reader :params
30
30
 
@@ -1,4 +1,8 @@
1
+ # Remove warnings in CGI
2
+ saved_verbose = $VERBOSE
3
+ $VERBOSE = false
1
4
  require 'faraday'
5
+ $VERBOSE = saved_verbose
2
6
 
3
7
  module Faraday
4
8
  class Response
@@ -126,6 +126,7 @@ module OpenC3
126
126
  @cleanup_times = []
127
127
  @previous_time_nsec_since_epoch = nil
128
128
  @tmp_dir = Dir.mktmpdir
129
+ @wait_threads = []
129
130
 
130
131
  # This is an optimization to avoid creating a new entry object
131
132
  # each time we create an entry which we do a LOT!
@@ -154,9 +155,8 @@ module OpenC3
154
155
 
155
156
  # Stops all logging and closes the current log file.
156
157
  def stop
157
- threads = nil
158
- @mutex.synchronize { threads = close_file(false); @logging_enabled = false; }
159
- return threads
158
+ @mutex.synchronize { close_file(false); @logging_enabled = false; }
159
+ return @wait_threads
160
160
  end
161
161
 
162
162
  # Stop all logging, close the current log file, and kill the logging threads.
@@ -173,6 +173,13 @@ module OpenC3
173
173
  return threads
174
174
  end
175
175
 
176
+ def cleanup
177
+ if @tmp_dir
178
+ FileUtils.remove_entry_secure(@tmp_dir, true)
179
+ @tmp_dir = nil
180
+ end
181
+ end
182
+
176
183
  def graceful_kill
177
184
  @cancel_threads = true
178
185
  end
@@ -307,8 +314,19 @@ module OpenC3
307
314
  # to keep a full file's worth of data in the stream. This is what prevents continuous stream growth.
308
315
  # Returns thread that moves log to bucket
309
316
  def close_file(take_mutex = true)
310
- threads = []
311
317
  @mutex.lock if take_mutex
318
+
319
+ # Remove old wait_threads
320
+ to_remove = []
321
+ @wait_threads.each do |thread|
322
+ unless thread.alive?
323
+ to_remove << thread
324
+ end
325
+ end
326
+ to_remove.each do |thread|
327
+ @wait_threads.delete(thread)
328
+ end
329
+
312
330
  begin
313
331
  if @file
314
332
  begin
@@ -322,7 +340,7 @@ module OpenC3
322
340
  # Cleanup timestamps here so they are unset for the next file
323
341
  @first_time = nil
324
342
  @last_time = nil
325
- threads << BucketUtilities.move_log_file_to_bucket(@filename, bucket_key)
343
+ @wait_threads << BucketUtilities.move_log_file_to_bucket(@filename, bucket_key)
326
344
  # Now that the file is in storage, trim the Redis stream after a delay
327
345
  @cleanup_offsets << {}
328
346
  @last_offsets.each do |redis_topic, last_offset|
@@ -342,7 +360,7 @@ module OpenC3
342
360
  ensure
343
361
  @mutex.unlock if take_mutex
344
362
  end
345
- return threads
363
+ return @wait_threads
346
364
  end
347
365
 
348
366
  def bucket_filename
@@ -137,7 +137,6 @@ module OpenC3
137
137
  # Closing a log file isn't critical so we just log an error
138
138
  # Returns threads that moves log to bucket
139
139
  def close_file(take_mutex = true)
140
- threads = []
141
140
  @mutex.lock if take_mutex
142
141
  begin
143
142
  # Need to write the OFFSET_MARKER for each packet
@@ -145,12 +144,10 @@ module OpenC3
145
144
  write_entry(:OFFSET_MARKER, nil, nil, nil, nil, nil, last_offset + ',' + redis_topic, nil) if @file
146
145
  end
147
146
 
148
- threads.concat(super(false))
149
-
147
+ return super(false)
150
148
  ensure
151
149
  @mutex.unlock if take_mutex
152
150
  end
153
- return threads
154
151
  end
155
152
 
156
153
  def get_packet_index(cmd_or_tlm, target_name, packet_name, entry_type, data)
@@ -43,13 +43,20 @@ module OpenC3
43
43
 
44
44
  # Close any open stream log files
45
45
  def stop
46
- @read_log.stop
47
- @write_log.stop
46
+ threads = @read_log.stop
47
+ threads.concat(@write_log.stop)
48
+ return threads
48
49
  end
49
50
 
50
51
  def shutdown
51
- @read_log.shutdown
52
- @write_log.shutdown
52
+ threads = @read_log.shutdown
53
+ threads.concat(@write_log.shutdown)
54
+ return threads
55
+ end
56
+
57
+ def cleanup
58
+ @read_log.cleanup
59
+ @write_log.cleanup
53
60
  end
54
61
 
55
62
  # Clone the stream log pair
@@ -68,7 +68,6 @@ module OpenC3
68
68
  # Closing a log file isn't critical so we just log an error
69
69
  # Returns threads that moves log to bucket
70
70
  def close_file(take_mutex = true)
71
- threads = []
72
71
  @mutex.lock if take_mutex
73
72
  begin
74
73
  # Need to write the OFFSET_MARKER for each packet
@@ -79,12 +78,10 @@ module OpenC3
79
78
  write_entry(time.to_nsec_from_epoch, data.as_json(allow_nan: true).to_json(allow_nan: true)) if @file
80
79
  end
81
80
 
82
- threads.concat(super(false))
83
-
81
+ return super(false)
84
82
  ensure
85
83
  @mutex.unlock if take_mutex
86
84
  end
87
- return threads
88
85
  end
89
86
 
90
87
  def extension
@@ -779,7 +779,6 @@ module OpenC3
779
779
  else
780
780
  @logger.error "#{@interface.name}: #{connect_error.formatted}"
781
781
  unless @connection_failed_messages.include?(connect_error.message)
782
- OpenC3.write_exception_file(connect_error)
783
782
  @connection_failed_messages << connect_error.message
784
783
  end
785
784
  end
@@ -800,7 +799,6 @@ module OpenC3
800
799
  else
801
800
  @logger.error "#{@interface.name}: #{err.formatted}"
802
801
  unless @connection_lost_messages.include?(err.message)
803
- OpenC3.write_exception_file(err)
804
802
  @connection_lost_messages << err.message
805
803
  end
806
804
  end
@@ -888,6 +886,14 @@ module OpenC3
888
886
  def shutdown(_sig = nil)
889
887
  @logger.info "#{@interface ? @interface.name : @name}: shutdown requested"
890
888
  stop()
889
+ if @interface and @interface.stream_log_pair
890
+ threads = @interface.stream_log_pair.shutdown
891
+ # Wait for all the logging threads to move files to buckets
892
+ threads.flatten.compact.each do |thread|
893
+ thread.join
894
+ end
895
+ @interface.stream_log_pair.cleanup
896
+ end
891
897
  super()
892
898
  end
893
899
 
@@ -125,8 +125,8 @@ module OpenC3
125
125
  def shutdown
126
126
  # Make sure all the existing logs are properly closed down
127
127
  threads = []
128
- @plws.each do |target_name, plw_hash|
129
- plw_hash.each do |type, plw|
128
+ @plws.each do |_target_name, plw_hash|
129
+ plw_hash.each do |_type, plw|
130
130
  threads.concat(plw.shutdown)
131
131
  end
132
132
  end
@@ -134,6 +134,11 @@ module OpenC3
134
134
  threads.flatten.compact.each do |thread|
135
135
  thread.join
136
136
  end
137
+ @plws.each do |_target_name, plw_hash|
138
+ plw_hash.each do |_type, plw|
139
+ plw.cleanup
140
+ end
141
+ end
137
142
  super()
138
143
  end
139
144
  end
@@ -88,6 +88,7 @@ module OpenC3
88
88
  @name = name
89
89
  split_name = name.split("__")
90
90
  raise "Name #{name} doesn't match convention of SCOPE__TYPE__NAME" if split_name.length != 3
91
+ microservice_type = split_name[1].to_s.upcase
91
92
 
92
93
  @scope = split_name[0]
93
94
  $openc3_scope = @scope
@@ -102,8 +103,14 @@ module OpenC3
102
103
 
103
104
  OpenC3.setup_open_telemetry(@name, false)
104
105
 
106
+ @temp_dir = OpenC3.sanitize_path(File.join(Dir.tmpdir, @name))
107
+
105
108
  # Create temp folder for this microservice
106
- @temp_dir = Dir.mktmpdir
109
+ # This will already have been setup by plugin_microservice.rb if USER
110
+ if is_plugin or microservice_type != 'USER'
111
+ FileUtils.remove_entry_secure(@temp_dir, true)
112
+ Dir.mkdir(@temp_dir)
113
+ end
107
114
 
108
115
  # Get microservice configuration from Redis
109
116
  @config = MicroserviceModel.get(name: @name, scope: @scope)
@@ -142,14 +149,13 @@ module OpenC3
142
149
  cmd_array = @config["cmd"]
143
150
 
144
151
  # Get Microservice files from bucket storage
145
- temp_dir = Dir.mktmpdir
146
152
  bucket = ENV['OPENC3_CONFIG_BUCKET']
147
153
  client = Bucket.getClient()
148
154
 
149
155
  prefix = "#{@scope}/microservices/#{@name}/"
150
156
  file_count = 0
151
157
  client.list_objects(bucket: bucket, prefix: prefix).each do |object|
152
- response_target = File.join(temp_dir, object.key.split(prefix)[-1])
158
+ response_target = OpenC3.sanitize_path(File.join(@temp_dir, object.key.split(prefix)[-1]))
153
159
  FileUtils.mkdir_p(File.dirname(response_target))
154
160
  client.get_object(bucket: bucket, key: object.key, path: response_target)
155
161
  file_count += 1
@@ -157,7 +163,7 @@ module OpenC3
157
163
 
158
164
  # Adjust @work_dir to microservice files downloaded if files and a relative path
159
165
  if file_count > 0 and @work_dir[0] != '/'
160
- @work_dir = File.join(temp_dir, @work_dir)
166
+ @work_dir = OpenC3.sanitize_path(File.join(@temp_dir, @work_dir))
161
167
  end
162
168
 
163
169
  # Check Syntax on any ruby files
@@ -18,6 +18,8 @@ require 'openc3/utilities/authentication'
18
18
  require 'openc3/api/api'
19
19
 
20
20
  module OpenC3
21
+ saved_verbose = $VERBOSE
22
+ $VERBOSE = false
21
23
  module Script
22
24
  private
23
25
  # Override the prompt_for_hazardous method to always return true since there is no user to prompt
@@ -25,6 +27,7 @@ module OpenC3
25
27
  return true
26
28
  end
27
29
  end
30
+ $VERBOSE = saved_verbose
28
31
 
29
32
  # The queue processor runs in a single thread and processes commands via cmd_api.
30
33
  class QueueProcessor
@@ -71,10 +74,14 @@ module OpenC3
71
74
  else
72
75
  cmd_params = {}
73
76
  end
74
- cmd(command['target_name'], command['cmd_name'], cmd_params, queue: false, scope: @scope)
77
+ validate = command.key?('validate') ? command['validate'] : true
78
+ timeout = command['timeout']
79
+ cmd(command['target_name'], command['cmd_name'], cmd_params, queue: false, validate: validate, timeout: timeout, scope: @scope)
75
80
  elsif command['value']
76
81
  # Legacy format: use single string parameter for backwards compatibility
77
- cmd(command['value'], queue: false, scope: @scope)
82
+ validate = command.key?('validate') ? command['validate'] : true
83
+ timeout = command['timeout']
84
+ cmd(command['value'], queue: false, validate: validate, timeout: timeout, scope: @scope)
78
85
  else
79
86
  @logger.error "QueueProcessor: Invalid command format, missing required fields"
80
87
  end