openc3 5.14.2 → 5.15.1

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.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +1 -1
  3. data/data/config/_id_items.yaml +2 -3
  4. data/data/config/telemetry_modifiers.yaml +0 -1
  5. data/lib/openc3/accessors/accessor.rb +3 -3
  6. data/lib/openc3/accessors/http_accessor.rb +1 -1
  7. data/lib/openc3/api/cmd_api.rb +50 -7
  8. data/lib/openc3/api/tlm_api.rb +16 -5
  9. data/lib/openc3/logs/buffered_packet_log_writer.rb +5 -0
  10. data/lib/openc3/logs/packet_log_reader.rb +8 -9
  11. data/lib/openc3/microservices/interface_microservice.rb +29 -18
  12. data/lib/openc3/microservices/trigger_group_microservice.rb +9 -9
  13. data/lib/openc3/models/auth_model.rb +5 -7
  14. data/lib/openc3/models/cvt_model.rb +9 -4
  15. data/lib/openc3/models/gem_model.rb +7 -5
  16. data/lib/openc3/models/metric_model.rb +8 -0
  17. data/lib/openc3/models/model.rb +21 -6
  18. data/lib/openc3/models/plugin_model.rb +8 -2
  19. data/lib/openc3/models/python_package_model.rb +3 -0
  20. data/lib/openc3/models/scope_model.rb +2 -2
  21. data/lib/openc3/models/target_model.rb +21 -28
  22. data/lib/openc3/models/tool_model.rb +2 -2
  23. data/lib/openc3/packets/json_packet.rb +5 -3
  24. data/lib/openc3/script/commands.rb +2 -2
  25. data/lib/openc3/script/storage.rb +28 -12
  26. data/lib/openc3/script/suite_results.rb +9 -9
  27. data/lib/openc3/system/system.rb +4 -5
  28. data/lib/openc3/top_level.rb +32 -23
  29. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  30. data/lib/openc3/topics/command_topic.rb +2 -1
  31. data/lib/openc3/topics/telemetry_topic.rb +7 -2
  32. data/lib/openc3/utilities/authorization.rb +1 -1
  33. data/lib/openc3/utilities/aws_bucket.rb +21 -15
  34. data/lib/openc3/utilities/bucket.rb +1 -1
  35. data/lib/openc3/utilities/logger.rb +3 -3
  36. data/lib/openc3/utilities/process_manager.rb +15 -9
  37. data/lib/openc3/utilities/store_autoload.rb +33 -2
  38. data/lib/openc3/utilities/store_queued.rb +23 -24
  39. data/lib/openc3/version.rb +6 -6
  40. data/templates/tool_angular/package.json +2 -2
  41. data/templates/tool_svelte/package.json +1 -1
  42. data/templates/tool_vue/package.json +2 -2
  43. data/templates/widget/package.json +2 -2
  44. metadata +16 -7
  45. data/templates/tool_angular/yarn.lock +0 -8155
  46. data/templates/tool_react/yarn.lock +0 -7201
  47. data/templates/tool_svelte/yarn.lock +0 -5519
  48. data/templates/tool_vue/yarn.lock +0 -9455
  49. data/templates/widget/yarn.lock +0 -9338
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0eaf09072832c752cd1c0ae08160f9a5ef5bb24e585f10ee8be31d22c54ce2e7
4
- data.tar.gz: 17fbccdaf92dd9c6e7fae704ca4bacadef88b026c5023d7dbb69583a19f6fe3e
3
+ metadata.gz: e965c2b2463c93d4f1831b6d6cdfc53cae47bac8297e4335be407e420b37545e
4
+ data.tar.gz: 1f4a4d4f367daf99411d70d693f511a6550f911787d46f0602e6bc440edc19c5
5
5
  SHA512:
6
- metadata.gz: 92e1bfa1bbd283c4e94d49627ea8bfe6be9423ff88bc1a5453af778fb10dc3cefd78b2cef6b553fa6d1aa6f21559e70413e5343ec546b93841c62c124a3840a1
7
- data.tar.gz: ac243f0b29ee7cccd5c809c5625a084cb54a3920b283402ddc7da8ce4178816d9ce75d44cb8af8154438a846ff116dec41b27cd5623ef5ded506c0c2d320e590
6
+ metadata.gz: 37956addc6a7019369e710ea02fcf8c71b114b049e83eeef7b7a0c2e99f17c2f685311db55697cb31a518bea8cc815647d370d0bdb9640259d76df86b79dff9a
7
+ data.tar.gz: f90a6beae03680d8fdc256ea2bf4dc937667a4a9d29bc6d3daccd33de02ea485ce808a52ed73a1ce251e0a9a7dc9ef3e9ccf1acdbe5ab8fa360f2dfbfbb104d8
data/bin/openc3cli CHANGED
@@ -471,7 +471,7 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
471
471
  OpenC3::LocalMode.update_local_plugin(plugin_file_path, plugin_hash, scope: scope)
472
472
  end
473
473
  rescue => e
474
- abort("Error installing plugin: #{scope}: #{plugin_file_path}\n#{e.message}")
474
+ abort("Error installing plugin: #{scope}: #{plugin_file_path}\n#{e.formatted}")
475
475
  end
476
476
  else
477
477
  # Outside Cluster
@@ -4,13 +4,12 @@
4
4
  description:
5
5
  Bit size of this telemetry item. Zero or Negative values may be used
6
6
  to indicate that a string fills the packet up to the offset from the end of
7
- the packet specified by this value. If Bit Offset is 0 and Bit Size is 0 then
8
- this is a derived parameter and the Data Type must be set to 'DERIVED'.
7
+ the packet specified by this value.
9
8
  values: \d+
10
9
  - name: Data Type
11
10
  required: true
12
11
  description: Data Type of this telemetry item
13
- values: <%= %w(INT UINT FLOAT STRING BLOCK DERIVED) %>
12
+ values: <%= %w(INT UINT FLOAT STRING BLOCK) %>
14
13
  - name: ID Value
15
14
  required: true
16
15
  description: The value of this telemetry item that uniquely identifies this telemetry packet
@@ -45,7 +45,6 @@ ID_ITEM:
45
45
  required: true
46
46
  description: Bit offset into the telemetry packet of the Most Significant Bit of this item.
47
47
  May be negative to indicate on offset from the end of the packet.
48
- Always use a bit offset of 0 for derived item.
49
48
  values: '[-]?\d+'
50
49
  <%= MetaConfigParser.load('_id_items.yaml').to_meta_config_yaml(4) %>
51
50
  APPEND_ID_ITEM:
@@ -74,18 +74,18 @@ module OpenC3
74
74
  return false
75
75
  end
76
76
 
77
- # If this is true it will enfore that COSMOS DERIVED items must have a
77
+ # If this is true it will enforce that COSMOS DERIVED items must have a
78
78
  # write_conversion to be written
79
79
  def enforce_derived_write_conversion(_item)
80
80
  return true
81
81
  end
82
82
 
83
83
  def self.read_item(_item, _buffer)
84
- raise "Must be defined by subclass"
84
+ raise "Must be defined by subclass if needed"
85
85
  end
86
86
 
87
87
  def self.write_item(_item, _value, _buffer)
88
- raise "Must be defined by subclass"
88
+ raise "Must be defined by subclass if needed"
89
89
  end
90
90
 
91
91
  def self.read_items(items, buffer)
@@ -176,7 +176,7 @@ module OpenC3
176
176
  when 'HTTP_STATUS', 'HTTP_PATH', 'HTTP_METHOD', 'HTTP_PACKET', 'HTTP_ERROR_PACKET', /^HTTP_QUERY_/, /^HTTP_HEADER_/
177
177
  return false
178
178
  else
179
- return @body_accessor.enforce_derived_write_conversion
179
+ return @body_accessor.enforce_derived_write_conversion(item)
180
180
  end
181
181
  end
182
182
  end
@@ -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
@@ -41,6 +41,8 @@ module OpenC3
41
41
  'cmd_raw_no_checks',
42
42
  'build_cmd',
43
43
  'build_command', # DEPRECATED
44
+ 'enable_cmd',
45
+ 'disable_cmd',
44
46
  'send_raw',
45
47
  'get_all_cmds',
46
48
  'get_all_commands', # DEPRECATED
@@ -131,6 +133,30 @@ module OpenC3
131
133
  # build_command is DEPRECATED
132
134
  alias build_command build_cmd
133
135
 
136
+ # Helper method for disable_cmd / enable_cmd
137
+ def _get_and_set_cmd(method, *args, scope: $openc3_scope, token: $openc3_token)
138
+ target_name, command_name = _extract_target_command_names(method, *args)
139
+ authorize(permission: 'admin', target_name: target_name, packet_name: command_name, scope: scope, token: token)
140
+ command = yield TargetModel.packet(target_name, command_name, type: :CMD, scope: scope)
141
+ TargetModel.set_packet(target_name, command_name, command, type: :CMD, scope: scope)
142
+ end
143
+
144
+ # @since 5.15.1
145
+ def enable_cmd(*args, scope: $openc3_scope, token: $openc3_token)
146
+ _get_and_set_cmd('enable_cmd', *args, scope: scope, token: token) do |command|
147
+ command['disabled'] = false
148
+ command
149
+ end
150
+ end
151
+
152
+ # @since 5.15.1
153
+ def disable_cmd(*args, scope: $openc3_scope, token: $openc3_token)
154
+ _get_and_set_cmd('disable_cmd', *args, scope: scope, token: token) do |command|
155
+ command['disabled'] = true
156
+ command
157
+ end
158
+ end
159
+
134
160
  # Send a raw binary string to the specified interface.
135
161
  #
136
162
  # @param interface_name [String] The interface to send the raw binary
@@ -178,10 +204,21 @@ module OpenC3
178
204
  # @since 5.0.6
179
205
  # @param target_name [String] Name of the target
180
206
  # @return [Array<String>] Array of all command packet names
181
- def get_all_cmd_names(target_name, scope: $openc3_scope, token: $openc3_token)
182
- target_name = target_name.upcase
183
- authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
184
- TargetModel.packet_names(target_name, type: :CMD, scope: scope)
207
+ def get_all_cmd_names(target_name, hidden: false, scope: $openc3_scope, token: $openc3_token)
208
+ begin
209
+ packets = get_all_cmds(target_name, scope: scope, token: token)
210
+ rescue RuntimeError
211
+ packets = []
212
+ end
213
+ names = []
214
+ packets.each do |packet|
215
+ if hidden
216
+ names << packet['packet_name']
217
+ else
218
+ names << packet['packet_name'] unless packet['hidden']
219
+ end
220
+ end
221
+ return names
185
222
  end
186
223
  # get_all_command_names is DEPRECATED
187
224
  alias get_all_command_names get_all_cmd_names
@@ -445,6 +482,12 @@ module OpenC3
445
482
  cmd_params = cmd_params.transform_keys(&:upcase)
446
483
  authorize(permission: 'cmd', target_name: target_name, packet_name: cmd_name, scope: scope, token: token)
447
484
  packet = TargetModel.packet(target_name, cmd_name, type: :CMD, scope: scope)
485
+ if packet['disabled']
486
+ error = DisabledError.new
487
+ error.target_name = target_name
488
+ error.cmd_name = cmd_name
489
+ raise error
490
+ end
448
491
 
449
492
  command = {
450
493
  'target_name' => target_name,
@@ -474,7 +517,7 @@ module OpenC3
474
517
 
475
518
  def _build_cmd_output_string(method_name, target_name, cmd_name, cmd_params, packet)
476
519
  output_string = "#{method_name}(\""
477
- output_string << target_name + ' ' + cmd_name
520
+ output_string << (target_name + ' ' + cmd_name)
478
521
  if cmd_params.nil? or cmd_params.empty?
479
522
  output_string << '")'
480
523
  else
@@ -511,7 +554,7 @@ module OpenC3
511
554
  params << "#{key} #{value}"
512
555
  end
513
556
  params = params.join(", ")
514
- output_string << ' with ' + params + '")'
557
+ output_string << (' with ' + params + '")')
515
558
  end
516
559
  return output_string
517
560
  end
@@ -139,7 +139,7 @@ module OpenC3
139
139
 
140
140
  # See if this target has a tlm interface
141
141
  interface_name = nil
142
- InterfaceModel.all(scope: scope).each do |name, interface|
142
+ InterfaceModel.all(scope: scope).each do |_name, interface|
143
143
  if interface['tlm_target_names'].include? target_name
144
144
  interface_name = interface['name']
145
145
  break
@@ -287,10 +287,21 @@ module OpenC3
287
287
  # @since 5.0.6
288
288
  # @param target_name [String] Name of the target
289
289
  # @return [Array<String>] Array of all telemetry packet names
290
- def get_all_tlm_names(target_name, scope: $openc3_scope, token: $openc3_token)
291
- target_name = target_name.upcase
292
- authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
293
- TargetModel.packet_names(target_name, type: :TLM, scope: scope)
290
+ def get_all_tlm_names(target_name, hidden: false, scope: $openc3_scope, token: $openc3_token)
291
+ begin
292
+ packets = get_all_tlm(target_name, scope: scope, token: token)
293
+ rescue RuntimeError
294
+ packets = []
295
+ end
296
+ names = []
297
+ packets.each do |packet|
298
+ if hidden
299
+ names << packet['packet_name']
300
+ else
301
+ names << packet['packet_name'] unless packet['hidden']
302
+ end
303
+ end
304
+ return names
294
305
  end
295
306
  alias get_all_telemetry_names get_all_tlm_names
296
307
 
@@ -80,6 +80,11 @@ module OpenC3
80
80
  def buffered_write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0', received_time_nsec_since_epoch: nil, extra: nil)
81
81
  case entry_type
82
82
  when :RAW_PACKET, :JSON_PACKET
83
+ # If we have data in the buffer, a file should always be open so that cycle time logic will run
84
+ unless @file
85
+ @mutex.synchronize { start_new_file() }
86
+ end
87
+
83
88
  @buffer << [entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, redis_topic, redis_offset, received_time_nsec_since_epoch, extra]
84
89
  @buffer.sort! {|entry1, entry2| entry1[4] <=> entry2[4] }
85
90
  if @buffer.length >= @buffer_depth
@@ -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
@@ -96,9 +96,9 @@ module OpenC3
96
96
  @max_read_size = @file.size
97
97
  @max_read_size = MAX_READ_SIZE if @max_read_size > MAX_READ_SIZE
98
98
  return read_file_header()
99
- rescue => err
99
+ rescue => e
100
100
  close()
101
- raise err
101
+ raise e
102
102
  end
103
103
 
104
104
  # Closes the current log file
@@ -134,9 +134,8 @@ module OpenC3
134
134
 
135
135
  if flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_JSON_PACKET_ENTRY_TYPE_MASK
136
136
  packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
137
- next_offset = 12
138
137
  received_time_nsec_since_epoch, extra, json_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
139
- lookup_cmd_or_tlm, target_name, packet_name, id, key_map = @packets[packet_index]
138
+ lookup_cmd_or_tlm, target_name, packet_name, _id, key_map = @packets[packet_index]
140
139
  if cmd_or_tlm != lookup_cmd_or_tlm
141
140
  raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
142
141
  end
@@ -148,8 +147,8 @@ module OpenC3
148
147
  end
149
148
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_RAW_PACKET_ENTRY_TYPE_MASK
150
149
  packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
151
- received_time_nsec_since_epoch, extra, packet_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
152
- lookup_cmd_or_tlm, target_name, packet_name, id = @packets[packet_index]
150
+ received_time_nsec_since_epoch, _extra, packet_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
151
+ lookup_cmd_or_tlm, target_name, packet_name, _id = @packets[packet_index]
153
152
  if cmd_or_tlm != lookup_cmd_or_tlm
154
153
  raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
155
154
  end
@@ -220,9 +219,9 @@ module OpenC3
220
219
  else
221
220
  raise "Invalid Entry Flags: #{flags}"
222
221
  end
223
- rescue => err
222
+ rescue => e
224
223
  close()
225
- raise err
224
+ raise e
226
225
  end
227
226
 
228
227
  # @return [Integer] The size of the log file being processed
@@ -126,7 +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
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
130
130
  rescue => e
131
131
  @logger.error "#{@interface.name}: interface_cmd: #{e.formatted}"
132
132
  next e.message
@@ -138,7 +138,7 @@ module OpenC3
138
138
  begin
139
139
  @logger.info "#{@interface.name}: protocol_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')} read_write: #{params['read_write']} index: #{params['index']}"
140
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)
141
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
142
142
  rescue => e
143
143
  @logger.error "#{@interface.name}: protocol_cmd: #{e.formatted}"
144
144
  next e.message
@@ -206,7 +206,7 @@ module OpenC3
206
206
  @interface.write(command)
207
207
  CommandTopic.write_packet(command, scope: @scope)
208
208
  CommandDecomTopic.write_packet(command, scope: @scope)
209
- InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
209
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
210
210
  next 'SUCCESS'
211
211
  else
212
212
  next "Interface not connected: #{@interface.name}"
@@ -298,7 +298,7 @@ module OpenC3
298
298
  begin
299
299
  @logger.info "#{@router.name}: router_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')}"
300
300
  @router.interface_cmd(params['cmd_name'], *params['cmd_params'])
301
- RouterStatusModel.set(@router.as_json(:allow_nan => true), scope: @scope)
301
+ RouterStatusModel.set(@router.as_json(:allow_nan => true), queued: true, scope: @scope)
302
302
  rescue => e
303
303
  @logger.error "#{@router.name}: router_cmd: #{e.formatted}"
304
304
  next e.message
@@ -310,7 +310,7 @@ module OpenC3
310
310
  begin
311
311
  @logger.info "#{@router.name}: protocol_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')} read_write: #{params['read_write']} index: #{params['index']}"
312
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)
313
+ RouterStatusModel.set(@router.as_json(:allow_nan => true), queued: true, scope: @scope)
314
314
  rescue => e
315
315
  @logger.error "#{@router.name}: protoco_cmd: #{e.formatted}"
316
316
  next e.message
@@ -335,7 +335,7 @@ module OpenC3
335
335
 
336
336
  begin
337
337
  @router.write(packet)
338
- RouterStatusModel.set(@router.as_json(:allow_nan => true), scope: @scope)
338
+ RouterStatusModel.set(@router.as_json(:allow_nan => true), queued: true, scope: @scope)
339
339
  next 'SUCCESS'
340
340
  rescue => e
341
341
  @logger.error "#{@router.name}: #{e.formatted}"
@@ -352,6 +352,7 @@ module OpenC3
352
352
  def initialize(name)
353
353
  @mutex = Mutex.new
354
354
  super(name)
355
+
355
356
  @interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
356
357
  if @interface_or_router == 'INTERFACE'
357
358
  @metric.set(name: 'interface_tlm_total', value: @count, type: 'counter')
@@ -359,7 +360,6 @@ module OpenC3
359
360
  @metric.set(name: 'router_cmd_total', value: @count, type: 'counter')
360
361
  end
361
362
 
362
- @scope = name.split("__")[0]
363
363
  interface_name = name.split("__")[2]
364
364
  if @interface_or_router == 'INTERFACE'
365
365
  @interface = InterfaceModel.get_model(name: interface_name, scope: @scope).build
@@ -400,6 +400,17 @@ module OpenC3
400
400
  RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
401
401
  end
402
402
 
403
+ @queued = false
404
+ @interface.options.each do |option_name, option_values|
405
+ case option_name.upcase
406
+ when 'OPTIMIZE_THROUGHPUT'
407
+ @queued = true
408
+ update_interval = option_values[0].to_f
409
+ EphemeralStoreQueued.instance.set_update_interval(update_interval)
410
+ StoreQueued.instance.set_update_interval(update_interval)
411
+ end
412
+ end
413
+
403
414
  @interface_thread_sleeper = Sleeper.new
404
415
  @cancel_thread = false
405
416
  @connection_failed_messages = []
@@ -432,9 +443,9 @@ module OpenC3
432
443
 
433
444
  @interface.state = 'ATTEMPTING'
434
445
  if @interface_or_router == 'INTERFACE'
435
- InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
446
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
436
447
  else
437
- RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
448
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
438
449
  end
439
450
  @interface # Return the interface/router since we may have recreated it
440
451
  # Need to rescue Exception so we cover LoadError
@@ -507,15 +518,15 @@ module OpenC3
507
518
  disconnect(false)
508
519
  end
509
520
  if @interface_or_router == 'INTERFACE'
510
- InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
521
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
511
522
  else
512
- RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
523
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
513
524
  end
514
525
  @logger.info "#{@interface.name}: Stopped packet reading"
515
526
  end
516
527
 
517
528
  def handle_packet(packet)
518
- InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
529
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
519
530
  packet.received_time = Time.now.sys unless packet.received_time
520
531
 
521
532
  if packet.stored
@@ -557,7 +568,7 @@ module OpenC3
557
568
  unknown_packet.extra = packet.extra
558
569
  packet = unknown_packet
559
570
  json_hash = CvtModel.build_json_from_packet(packet)
560
- CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: @scope)
571
+ CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, queued: @queued, scope: @scope)
561
572
  num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, packet.length].min
562
573
  data = packet.buffer(false)[0..(num_bytes_to_print - 1)]
563
574
  prefix = data.each_byte.map { | byte | sprintf("%02X", byte) }.join()
@@ -566,7 +577,7 @@ module OpenC3
566
577
 
567
578
  # Write to stream
568
579
  packet.received_count += 1
569
- TelemetryTopic.write_packet(packet, scope: @scope)
580
+ TelemetryTopic.write_packet(packet, queued: @queued, scope: @scope)
570
581
  end
571
582
 
572
583
  def handle_connection_failed(connect_error)
@@ -629,9 +640,9 @@ module OpenC3
629
640
  end
630
641
  @interface.state = 'CONNECTED'
631
642
  if @interface_or_router == 'INTERFACE'
632
- InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
643
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
633
644
  else
634
- RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
645
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
635
646
  end
636
647
  @logger.info "#{@interface.name}: Connection Success"
637
648
  end
@@ -661,9 +672,9 @@ module OpenC3
661
672
  else
662
673
  @interface.state = 'DISCONNECTED'
663
674
  if @interface_or_router == 'INTERFACE'
664
- InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
675
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
665
676
  else
666
- RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
677
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
667
678
  end
668
679
  end
669
680
  end
@@ -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
@@ -418,7 +418,7 @@ module OpenC3
418
418
  when 'OR'
419
419
  return left || right ? 1 : 0
420
420
  end
421
- rescue ArgumentError => error
421
+ rescue ArgumentError
422
422
  message = "invalid evaluate: (#{left} #{operator} #{right})"
423
423
  notify(name: name, severity: 'error', message: message)
424
424
  return -1
@@ -447,7 +447,7 @@ module OpenC3
447
447
  if visited["#{head.name}__P"][trigger.name]
448
448
  # Not sure if this is posible as on create it validates that the dependents are already created
449
449
  message = "loop detected from #{head.name} -> #{trigger.name} path: #{visited["#{head.name}__P"]}"
450
- notify(name: trigger.name, severity: 'error', message: error.message)
450
+ notify(name: trigger.name, severity: 'error', message: message)
451
451
  return visited["#{trigger.name}__R"] = -1
452
452
  end
453
453
  trigger.roots.each do | root_trigger_name |
@@ -455,7 +455,7 @@ module OpenC3
455
455
  root_trigger = triggers[root_trigger_name]
456
456
  if head.name == root_trigger.name
457
457
  message = "loop detected from #{head.name} -> #{root_trigger_name} path: #{visited["#{head.name}__P"]}"
458
- notify(name: trigger.name, severity: 'error', message: error.message)
458
+ notify(name: trigger.name, severity: 'error', message: message)
459
459
  return visited["#{trigger.name}__R"] = -1
460
460
  end
461
461
  result = evaluate_trigger(
@@ -474,9 +474,9 @@ module OpenC3
474
474
  else
475
475
  right = operand_value(operand: trigger.right, other: trigger.left, visited: visited)
476
476
  end
477
- rescue => error
477
+ rescue => e
478
478
  # This will primarily happen when the user inputs a bad Regexp
479
- notify(name: trigger.name, severity: 'error', message: error.message)
479
+ notify(name: trigger.name, severity: 'error', message: e.message)
480
480
  return visited["#{trigger.name}__R"] = -1
481
481
  end
482
482
  # Convert the standard '==' and '!=' into Ruby Regexp operators
@@ -560,7 +560,7 @@ module OpenC3
560
560
  while @read_topic
561
561
  begin
562
562
  Topic.read_topics(@topics) do |topic, _msg_id, msg_hash, _redis|
563
- @logger.debug "TriggerGroupManager block_for_updates: #{topic} #{msg_hash.to_s}"
563
+ @logger.debug "TriggerGroupManager block_for_updates: #{topic} #{msg_hash}"
564
564
  if topic != @share.trigger_base.autonomic_topic
565
565
  packet = JsonPacket.new(:TLM, msg_hash['target_name'], msg_hash['packet_name'], msg_hash['time'].to_i, false, msg_hash["json_data"])
566
566
  @share.packet_base.add(topic: topic, packet: packet)
@@ -580,7 +580,7 @@ module OpenC3
580
580
  def shutdown
581
581
  @read_topic = false
582
582
  @cancel_thread = true
583
- @worker_count.times do | i |
583
+ @worker_count.times do | _i |
584
584
  @queue << nil
585
585
  end
586
586
  end
@@ -635,7 +635,7 @@ module OpenC3
635
635
  begin
636
636
  AutonomicTopic.read_topics(@topics) do |_topic, _msg_id, msg_hash, _redis|
637
637
  break if @cancel_thread
638
- @logger.debug "TriggerGroupMicroservice block_for_updates: #{msg_hash.to_s}"
638
+ @logger.debug "TriggerGroupMicroservice block_for_updates: #{msg_hash}"
639
639
  # Process trigger notifications created by TriggerModel notify
640
640
  if msg_hash['type'] == 'trigger'
641
641
  data = JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true)
@@ -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
@@ -31,11 +31,11 @@ module OpenC3
31
31
  @@token_cache = nil
32
32
  @@token_cache_time = nil
33
33
 
34
- def self.is_set?(key = PRIMARY_KEY)
34
+ def self.set?(key = PRIMARY_KEY)
35
35
  Store.exists(key) == 1
36
36
  end
37
37
 
38
- def self.verify(token, permission: nil)
38
+ def self.verify(token)
39
39
  return false if token.nil? or token.empty?
40
40
 
41
41
  token_hash = hash(token)
@@ -47,7 +47,7 @@ module OpenC3
47
47
 
48
48
  # Handle a service password - Generally only used by ScriptRunner
49
49
  service_password = ENV['OPENC3_SERVICE_PASSWORD']
50
- return true if service_password and service_password == token and permission != 'admin'
50
+ return true if service_password and service_password == token
51
51
 
52
52
  return false
53
53
  end
@@ -55,15 +55,13 @@ module OpenC3
55
55
  def self.set(token, old_token, key = PRIMARY_KEY)
56
56
  raise "token must not be nil or empty" if token.nil? or token.empty?
57
57
 
58
- if is_set?(key)
58
+ if set?(key)
59
59
  raise "old_token must not be nil or empty" if old_token.nil? or old_token.empty?
60
60
  raise "old_token incorrect" unless verify(old_token)
61
61
  end
62
62
  Store.set(key, hash(token))
63
63
  end
64
64
 
65
- private
66
-
67
65
  def self.hash(token)
68
66
  Digest::SHA2.hexdigest token
69
67
  end
@@ -21,6 +21,7 @@
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/utilities/store'
24
+ require 'openc3/utilities/store_queued'
24
25
  require 'openc3/models/target_model'
25
26
 
26
27
  module OpenC3
@@ -42,12 +43,16 @@ module OpenC3
42
43
  end
43
44
 
44
45
  # Set the current value table for a target, packet
45
- def self.set(hash, target_name:, packet_name:, scope: $openc3_scope)
46
+ def self.set(hash, target_name:, packet_name:, queued: false, scope: $openc3_scope)
46
47
  packet_json = JSON.generate(hash.as_json(:allow_nan => true))
47
48
  key = "#{scope}__tlm__#{target_name}"
48
49
  tgt_pkt_key = key + "__#{packet_name}"
49
50
  @@packet_cache[tgt_pkt_key] = [Time.now, hash]
50
- Store.hset(key, packet_name, packet_json)
51
+ if queued
52
+ StoreQueued.hset(key, packet_name, packet_json)
53
+ else
54
+ Store.hset(key, packet_name, packet_json)
55
+ end
51
56
  end
52
57
 
53
58
  # Get the hash for packet in the CVT
@@ -68,7 +73,7 @@ module OpenC3
68
73
  end
69
74
 
70
75
  # Set an item in the current value table
71
- def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
76
+ def self.set_item(target_name, packet_name, item_name, value, type:, queued: false, scope: $openc3_scope)
72
77
  hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: nil, scope: scope)
73
78
  case type
74
79
  when :WITH_UNITS
@@ -87,7 +92,7 @@ module OpenC3
87
92
  else
88
93
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
89
94
  end
90
- set(hash, target_name: target_name, packet_name: packet_name, scope: scope)
95
+ set(hash, target_name: target_name, packet_name: packet_name, queued: queued, scope: scope)
91
96
  end
92
97
 
93
98
  # Get an item from the current value table
@@ -102,13 +102,15 @@ module OpenC3
102
102
  raise err
103
103
  end
104
104
 
105
- def self.destroy(name)
105
+ def self.destroy(name, log_and_raise_needed_errors: true)
106
106
  gem_name, version = self.extract_name_and_version(name)
107
107
  plugin_gem_names = PluginModel.gem_names
108
108
  if plugin_gem_names.include?(name)
109
- message = "Gem file #{name} can't be uninstalled because needed by installed plugin"
110
- Logger.error message
111
- raise message
109
+ if log_and_raise_needed_errors
110
+ message = "Gem file #{name} can't be uninstalled because needed by installed plugin"
111
+ Logger.error message
112
+ raise message
113
+ end
112
114
  else
113
115
  begin
114
116
  Gem::Uninstaller.new(gem_name, {:version => version, :force => true}).uninstall
@@ -131,7 +133,7 @@ module OpenC3
131
133
  GemModel.names.each do |gem_full_name|
132
134
  gem_name, gem_version = GemModel.extract_name_and_version(gem_full_name)
133
135
  if gem_name == keep_gem_name and gem_version != keep_gem_version
134
- GemModel.destroy(gem_full_name)
136
+ GemModel.destroy(gem_full_name, log_and_raise_needed_errors: false)
135
137
  end
136
138
  end
137
139
  end
@@ -42,6 +42,14 @@ module OpenC3
42
42
  super("#{scope}#{PRIMARY_KEY}")
43
43
  end
44
44
 
45
+ # Sets (updates) the redis hash of this model
46
+ # Queued defaults to true for MetricModel
47
+ def self.set(json, scope:, queued: true)
48
+ json[:scope] = scope
49
+ json.transform_keys!(&:to_sym)
50
+ self.new(**json).create(force: true, queued: queued)
51
+ end
52
+
45
53
  def self.destroy(scope:, name:)
46
54
  EphemeralStore.hdel("#{scope}#{PRIMARY_KEY}", name)
47
55
  end