openc3 5.14.2 → 5.15.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.

Potentially problematic release.


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

Files changed (41) 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 +17 -6
  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/microservices/interface_microservice.rb +29 -18
  11. data/lib/openc3/models/cvt_model.rb +9 -4
  12. data/lib/openc3/models/gem_model.rb +7 -5
  13. data/lib/openc3/models/metric_model.rb +8 -0
  14. data/lib/openc3/models/model.rb +21 -6
  15. data/lib/openc3/models/plugin_model.rb +8 -2
  16. data/lib/openc3/models/python_package_model.rb +3 -0
  17. data/lib/openc3/models/scope_model.rb +2 -2
  18. data/lib/openc3/models/target_model.rb +21 -28
  19. data/lib/openc3/models/tool_model.rb +2 -2
  20. data/lib/openc3/packets/json_packet.rb +5 -3
  21. data/lib/openc3/script/suite_results.rb +9 -9
  22. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  23. data/lib/openc3/topics/command_topic.rb +2 -1
  24. data/lib/openc3/topics/telemetry_topic.rb +7 -2
  25. data/lib/openc3/utilities/aws_bucket.rb +21 -15
  26. data/lib/openc3/utilities/bucket.rb +1 -1
  27. data/lib/openc3/utilities/logger.rb +3 -3
  28. data/lib/openc3/utilities/process_manager.rb +15 -9
  29. data/lib/openc3/utilities/store_autoload.rb +29 -2
  30. data/lib/openc3/utilities/store_queued.rb +23 -24
  31. data/lib/openc3/version.rb +6 -6
  32. data/templates/tool_angular/package.json +2 -2
  33. data/templates/tool_svelte/package.json +1 -1
  34. data/templates/tool_vue/package.json +2 -2
  35. data/templates/widget/package.json +2 -2
  36. metadata +16 -7
  37. data/templates/tool_angular/yarn.lock +0 -8155
  38. data/templates/tool_react/yarn.lock +0 -7201
  39. data/templates/tool_svelte/yarn.lock +0 -5519
  40. data/templates/tool_vue/yarn.lock +0 -9455
  41. 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: '0228b27d56d6fce2f445eb5842ec223be1351aaf09767da803e735ffb80875ec'
4
+ data.tar.gz: 7f78a835eb8d3d1c678afa7f93490821cac67dcbf3d854ec1973c452b139a647
5
5
  SHA512:
6
- metadata.gz: 92e1bfa1bbd283c4e94d49627ea8bfe6be9423ff88bc1a5453af778fb10dc3cefd78b2cef6b553fa6d1aa6f21559e70413e5343ec546b93841c62c124a3840a1
7
- data.tar.gz: ac243f0b29ee7cccd5c809c5625a084cb54a3920b283402ddc7da8ce4178816d9ce75d44cb8af8154438a846ff116dec41b27cd5623ef5ded506c0c2d320e590
6
+ metadata.gz: 545775f6dd330af7e6e4f566a07ea44f5f5b2e533d2dadb9704e5e1dba142377f4b63dacc89157b0c4b996e20c78851fba4d397d830619fa51ff0a313850935f
7
+ data.tar.gz: 9eb87fbc7b5410327f0142f1e2bf08ae8070169057a34fbef6eb0f7c9bd846c07aae7e49b70d9b8f4ff5647fe1258155eeb879d0ace4cbe782b1e7b5ca2c8019
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
@@ -178,10 +178,21 @@ module OpenC3
178
178
  # @since 5.0.6
179
179
  # @param target_name [String] Name of the target
180
180
  # @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)
181
+ def get_all_cmd_names(target_name, hidden: false, scope: $openc3_scope, token: $openc3_token)
182
+ begin
183
+ packets = get_all_cmds(target_name, scope: scope, token: token)
184
+ rescue RuntimeError
185
+ packets = []
186
+ end
187
+ names = []
188
+ packets.each do |packet|
189
+ if hidden
190
+ names << packet['packet_name']
191
+ else
192
+ names << packet['packet_name'] unless packet['hidden']
193
+ end
194
+ end
195
+ return names
185
196
  end
186
197
  # get_all_command_names is DEPRECATED
187
198
  alias get_all_command_names get_all_cmd_names
@@ -474,7 +485,7 @@ module OpenC3
474
485
 
475
486
  def _build_cmd_output_string(method_name, target_name, cmd_name, cmd_params, packet)
476
487
  output_string = "#{method_name}(\""
477
- output_string << target_name + ' ' + cmd_name
488
+ output_string << (target_name + ' ' + cmd_name)
478
489
  if cmd_params.nil? or cmd_params.empty?
479
490
  output_string << '")'
480
491
  else
@@ -511,7 +522,7 @@ module OpenC3
511
522
  params << "#{key} #{value}"
512
523
  end
513
524
  params = params.join(", ")
514
- output_string << ' with ' + params + '")'
525
+ output_string << (' with ' + params + '")')
515
526
  end
516
527
  return output_string
517
528
  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
@@ -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
@@ -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
@@ -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/config/config_parser'
25
26
 
26
27
  module OpenC3
@@ -34,6 +35,10 @@ module OpenC3
34
35
  Store
35
36
  end
36
37
 
38
+ def self.store_queued
39
+ StoreQueued
40
+ end
41
+
37
42
  # NOTE: The following three methods must be reimplemented by Model subclasses
38
43
  # without primary_key to support other class methods.
39
44
 
@@ -75,10 +80,10 @@ module OpenC3
75
80
  end
76
81
 
77
82
  # Sets (updates) the redis hash of this model
78
- def self.set(json, scope:)
83
+ def self.set(json, scope:, queued: false)
79
84
  json[:scope] = scope
80
85
  json.transform_keys!(&:to_sym)
81
- self.new(**json).create(force: true)
86
+ self.new(**json).create(force: true, queued: queued)
82
87
  end
83
88
 
84
89
  # @return [Model] Model generated from the passed JSON
@@ -134,7 +139,7 @@ module OpenC3
134
139
 
135
140
  # Update the Redis hash at primary_key and set the field "name"
136
141
  # to the JSON generated via calling as_json
137
- def create(update: false, force: false)
142
+ def create(update: false, force: false, queued: false)
138
143
  unless force
139
144
  existing = self.class.store.hget(@primary_key, @name)
140
145
  if existing
@@ -144,12 +149,18 @@ module OpenC3
144
149
  end
145
150
  end
146
151
  @updated_at = Time.now.to_nsec_from_epoch
147
- self.class.store.hset(@primary_key, @name, JSON.generate(self.as_json(:allow_nan => true), :allow_nan => true))
152
+
153
+ if queued
154
+ write_store = self.class.store_queued
155
+ else
156
+ write_store = self.class.store
157
+ end
158
+ write_store.hset(@primary_key, @name, JSON.generate(self.as_json(:allow_nan => true), :allow_nan => true))
148
159
  end
149
160
 
150
161
  # Alias for create(update: true)
151
- def update
152
- create(update: true)
162
+ def update(force: false, queued: false)
163
+ create(update: true, force: force, queued: queued)
153
164
  end
154
165
 
155
166
  # Deploy the model into the OpenC3 system. Subclasses must implement this
@@ -206,5 +217,9 @@ module OpenC3
206
217
  def self.store
207
218
  EphemeralStore
208
219
  end
220
+
221
+ def self.store_queued
222
+ EphemeralStoreQueued
223
+ end
209
224
  end
210
225
  end
@@ -183,12 +183,18 @@ 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
+ if pypi_url
187
+ pypi_url += '/simple'
188
+ end
186
189
  rescue => e
187
190
  Logger.error("Failed to retrieve pypi_url: #{e.formatted}")
188
191
  ensure
189
192
  if pypi_url.nil?
190
193
  # If Redis isn't running try the ENV, then simply pypi.org/simple
191
194
  pypi_url = ENV['PYPI_URL']
195
+ if pypi_url
196
+ pypi_url += '/simple'
197
+ end
192
198
  pypi_url ||= 'https://pypi.org/simple'
193
199
  end
194
200
  end
@@ -288,9 +294,9 @@ module OpenC3
288
294
  @needs_dependencies = ConfigParser.handle_true_false(needs_dependencies)
289
295
  end
290
296
 
291
- def create(update: false, force: false)
297
+ def create(update: false, force: false, queued: false)
292
298
  @name = @name + "__#{Time.now.utc.strftime("%Y%m%d%H%M%S")}" if not update and not @name.index("__")
293
- super(update: update, force: force)
299
+ super(update: update, force: force, queued: queued)
294
300
  end
295
301
 
296
302
  def as_json(*a)
@@ -74,6 +74,9 @@ module OpenC3
74
74
  rescue
75
75
  # If Redis isn't running try the ENV, then simply pypi.org/simple
76
76
  pypi_url = ENV['PYPI_URL']
77
+ if pypi_url
78
+ pypi_url += '/simple'
79
+ end
77
80
  pypi_url ||= 'https://pypi.org/simple'
78
81
  end
79
82
  Logger.info "Installing python package: #{name_or_path}"
@@ -95,11 +95,11 @@ module OpenC3
95
95
  @children = []
96
96
  end
97
97
 
98
- def create(update: false, force: false)
98
+ def create(update: false, force: false, queued: false)
99
99
  # Ensure there are no "." in the scope name - prevents gems accidently becoming scope names
100
100
  raise "Invalid scope name: #{@name}" if @name !~ /^[a-zA-Z0-9_-]+$/
101
101
  @name = @name.upcase
102
- super(update: update, force: force)
102
+ super(update: update, force: force, queued: queued)
103
103
  end
104
104
 
105
105
  def destroy