openc3 5.14.2 → 5.15.1

Sign up to get free protection for your applications and to get access to all the features.
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