openc3 5.11.3 → 5.12.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/bin/openc3cli +26 -12
  4. data/data/config/_id_items.yaml +6 -4
  5. data/data/config/_id_params.yaml +9 -6
  6. data/data/config/_items.yaml +6 -4
  7. data/data/config/_params.yaml +3 -2
  8. data/data/config/interface_modifiers.yaml +1 -1
  9. data/data/config/microservice.yaml +10 -1
  10. data/data/config/plugins.yaml +13 -3
  11. data/data/config/target.yaml +9 -0
  12. data/data/config/target_config.yaml +12 -0
  13. data/data/config/tool.yaml +12 -3
  14. data/lib/openc3/api/api.rb +1 -1
  15. data/lib/openc3/api/cmd_api.rb +24 -24
  16. data/lib/openc3/api/config_api.rb +12 -12
  17. data/lib/openc3/api/limits_api.rb +4 -3
  18. data/lib/openc3/api/settings_api.rb +5 -2
  19. data/lib/openc3/api/tlm_api.rb +7 -10
  20. data/lib/openc3/conversions/unix_time_conversion.rb +8 -6
  21. data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -7
  22. data/lib/openc3/io/json_drb.rb +3 -2
  23. data/lib/openc3/io/json_rpc.rb +6 -6
  24. data/lib/openc3/logs/buffered_packet_log_writer.rb +4 -2
  25. data/lib/openc3/logs/packet_log_writer.rb +22 -7
  26. data/lib/openc3/microservices/cleanup_microservice.rb +8 -1
  27. data/lib/openc3/microservices/decom_microservice.rb +1 -1
  28. data/lib/openc3/microservices/interface_microservice.rb +2 -2
  29. data/lib/openc3/microservices/microservice.rb +5 -2
  30. data/lib/openc3/microservices/reaction_microservice.rb +1 -0
  31. data/lib/openc3/microservices/timeline_microservice.rb +7 -5
  32. data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +22 -0
  33. data/lib/openc3/models/activity_model.rb +21 -3
  34. data/lib/openc3/models/cvt_model.rb +2 -1
  35. data/lib/openc3/models/gem_model.rb +4 -1
  36. data/lib/openc3/models/interface_model.rb +11 -5
  37. data/lib/openc3/models/metadata_model.rb +11 -0
  38. data/lib/openc3/models/microservice_model.rb +16 -3
  39. data/lib/openc3/models/model.rb +18 -0
  40. data/lib/openc3/models/note_model.rb +11 -0
  41. data/lib/openc3/models/plugin_model.rb +18 -0
  42. data/lib/openc3/models/python_package_model.rb +104 -0
  43. data/lib/openc3/models/scope_model.rb +2 -0
  44. data/lib/openc3/models/sorted_model.rb +17 -8
  45. data/lib/openc3/models/target_model.rb +53 -18
  46. data/lib/openc3/models/tool_config_model.rb +9 -3
  47. data/lib/openc3/models/tool_model.rb +22 -7
  48. data/lib/openc3/models/widget_model.rb +19 -3
  49. data/lib/openc3/operators/microservice_operator.rb +2 -0
  50. data/lib/openc3/packets/limits.rb +6 -18
  51. data/lib/openc3/packets/packet.rb +1 -0
  52. data/lib/openc3/packets/parsers/format_string_parser.rb +4 -4
  53. data/lib/openc3/packets/parsers/limits_parser.rb +4 -4
  54. data/lib/openc3/packets/parsers/limits_response_parser.rb +5 -5
  55. data/lib/openc3/packets/parsers/processor_parser.rb +4 -4
  56. data/lib/openc3/packets/parsers/state_parser.rb +3 -3
  57. data/lib/openc3/script/api_shared.rb +50 -32
  58. data/lib/openc3/script/calendar.rb +109 -0
  59. data/lib/openc3/script/commands.rb +1 -8
  60. data/lib/openc3/script/{gems.rb → packages.rb} +20 -16
  61. data/lib/openc3/script/script.rb +49 -38
  62. data/lib/openc3/system/system.rb +2 -0
  63. data/lib/openc3/system/target.rb +10 -1
  64. data/lib/openc3/top_level.rb +2 -2
  65. data/lib/openc3/utilities/aws_bucket.rb +3 -2
  66. data/lib/openc3/utilities/bucket_file_cache.rb +1 -1
  67. data/lib/openc3/utilities/local_mode.rb +3 -1
  68. data/lib/openc3/utilities/logger.rb +1 -1
  69. data/lib/openc3/utilities/ruby_lex_utils.rb +0 -8
  70. data/lib/openc3/version.rb +6 -6
  71. data/templates/tool_angular/package.json +14 -14
  72. data/templates/tool_angular/yarn.lock +282 -129
  73. data/templates/tool_react/package.json +11 -11
  74. data/templates/tool_react/yarn.lock +353 -300
  75. data/templates/tool_svelte/package.json +12 -12
  76. data/templates/tool_svelte/src/services/openc3-api.js +16 -60
  77. data/templates/tool_svelte/yarn.lock +338 -212
  78. data/templates/tool_vue/package.json +9 -9
  79. data/templates/tool_vue/yarn.lock +55 -41
  80. data/templates/widget/package.json +10 -10
  81. data/templates/widget/yarn.lock +59 -45
  82. metadata +36 -5
@@ -67,22 +67,19 @@ module OpenC3
67
67
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
68
68
  # @return [Object] The telemetry value formatted as requested
69
69
  def tlm(*args, type: :CONVERTED, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
70
- target_name, packet_name, item_name = tlm_process_args(args, 'tlm', cache_timeout: cache_timeout, scope: scope)
70
+ target_name, packet_name, item_name = _tlm_process_args(args, 'tlm', cache_timeout: cache_timeout, scope: scope)
71
71
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
72
72
  CvtModel.get_item(target_name, packet_name, item_name, type: type.intern, cache_timeout: cache_timeout, scope: scope)
73
73
  end
74
74
 
75
- # @deprecated Use tlm with type: :RAW
76
75
  def tlm_raw(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
77
76
  tlm(*args, type: :RAW, cache_timeout: cache_timeout, scope: scope, token: token)
78
77
  end
79
78
 
80
- # @deprecated Use tlm with type: :FORMATTED
81
79
  def tlm_formatted(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
82
80
  tlm(*args, type: :FORMATTED, cache_timeout: cache_timeout, scope: scope, token: token)
83
81
  end
84
82
 
85
- # @deprecated Use tlm with type: :WITH_UNITS
86
83
  def tlm_with_units(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
87
84
  tlm(*args, type: :WITH_UNITS, cache_timeout: cache_timeout, scope: scope, token: token)
88
85
  end
@@ -108,7 +105,7 @@ module OpenC3
108
105
  # @param args [String|Array<String>] See the description for calling style
109
106
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
110
107
  def set_tlm(*args, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
111
- target_name, packet_name, item_name, value = set_tlm_process_args(args, __method__, scope: scope)
108
+ target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, scope: scope)
112
109
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
113
110
  CvtModel.set_item(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
114
111
  end
@@ -169,7 +166,7 @@ module OpenC3
169
166
  # description).
170
167
  # @param type [Symbol] Telemetry type, :ALL (default), :RAW, :CONVERTED, :FORMATTED, :WITH_UNITS
171
168
  def override_tlm(*args, type: :ALL, scope: $openc3_scope, token: $openc3_token)
172
- target_name, packet_name, item_name, value = set_tlm_process_args(args, __method__, scope: scope)
169
+ target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, scope: scope)
173
170
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
174
171
  CvtModel.override(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
175
172
  end
@@ -194,7 +191,7 @@ module OpenC3
194
191
  # @param type [Symbol] Telemetry type, :ALL (default), :RAW, :CONVERTED, :FORMATTED, :WITH_UNITS
195
192
  # Also takes :ALL which means to normalize all telemetry types
196
193
  def normalize_tlm(*args, type: :ALL, scope: $openc3_scope, token: $openc3_token)
197
- target_name, packet_name, item_name = tlm_process_args(args, __method__, scope: scope)
194
+ target_name, packet_name, item_name = _tlm_process_args(args, __method__, scope: scope)
198
195
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
199
196
  CvtModel.normalize(target_name, packet_name, item_name, type: type.intern, scope: scope)
200
197
  end
@@ -389,7 +386,7 @@ module OpenC3
389
386
  # Get the transmit counts for telemetry packets
390
387
  #
391
388
  # @param target_packets [Array<Array<String, String>>] Array of arrays containing target_name, packet_name
392
- # @return [Numeric] Transmit count for the command
389
+ # @return [Array<Numeric>] Receive count for the telemetry packets
393
390
  def get_tlm_cnts(target_packets, scope: $openc3_scope, token: $openc3_token)
394
391
  authorize(permission: 'system', scope: scope, token: token)
395
392
  counts = []
@@ -430,7 +427,7 @@ module OpenC3
430
427
  return nil
431
428
  end
432
429
 
433
- def tlm_process_args(args, method_name, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
430
+ def _tlm_process_args(args, method_name, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
434
431
  case args.length
435
432
  when 1
436
433
  target_name, packet_name, item_name = extract_fields_from_tlm_text(args[0])
@@ -456,7 +453,7 @@ module OpenC3
456
453
  return [target_name, packet_name, item_name]
457
454
  end
458
455
 
459
- def set_tlm_process_args(args, method_name, scope: $openc3_scope, token: $openc3_token)
456
+ def _set_tlm_process_args(args, method_name, scope: $openc3_scope, token: $openc3_token)
460
457
  case args.length
461
458
  when 1
462
459
  target_name, packet_name, item_name, value = extract_fields_from_set_tlm_text(args[0])
@@ -31,30 +31,32 @@ module OpenC3
31
31
  # represents the number of seconds since the UNIX time epoch
32
32
  # @param microseconds_item_name [String] The telemetry item in the packet
33
33
  # which represents microseconds
34
- def initialize(seconds_item_name, microseconds_item_name = nil)
34
+ def initialize(seconds_item_name, microseconds_item_name = nil, seconds_type = 'RAW', microseconds_type = 'RAW')
35
35
  super()
36
36
  @seconds_item_name = seconds_item_name
37
37
  @microseconds_item_name = microseconds_item_name
38
38
  @converted_type = :RUBY_TIME
39
39
  @converted_bit_size = 0
40
+ @seconds_type = seconds_type.to_sym
41
+ @microseconds_type = microseconds_type.to_sym
40
42
  end
41
43
 
42
44
  # @param (see Conversion#call)
43
45
  # @return [Float] Packet time in seconds since UNIX epoch
44
46
  def call(value, packet, buffer)
45
47
  if @microseconds_item_name
46
- return Time.at(packet.read(@seconds_item_name, :RAW, buffer), packet.read(@microseconds_item_name, :RAW, buffer)).sys
48
+ return Time.at(packet.read(@seconds_item_name, @seconds_type, buffer), packet.read(@microseconds_item_name, @microseconds_type, buffer)).sys
47
49
  else
48
- return Time.at(packet.read(@seconds_item_name, :RAW, buffer), 0).sys
50
+ return Time.at(packet.read(@seconds_item_name, @seconds_type, buffer), 0).sys
49
51
  end
50
52
  end
51
53
 
52
54
  # @return [String] The name of the class followed by the time conversion
53
55
  def to_s
54
56
  if @microseconds_item_name
55
- return "Time.at(packet.read('#{@seconds_item_name}', :RAW, buffer), packet.read('#{@microseconds_item_name}', :RAW, buffer)).sys"
57
+ return "Time.at(packet.read('#{@seconds_item_name}', :#{@seconds_type}, buffer), packet.read('#{@microseconds_item_name}', :#{@microseconds_type}, buffer)).sys"
56
58
  else
57
- return "Time.at(packet.read('#{@seconds_item_name}', :RAW, buffer), 0).sys"
59
+ return "Time.at(packet.read('#{@seconds_item_name}', :#{@seconds_type}, buffer), 0).sys"
58
60
  end
59
61
  end
60
62
 
@@ -66,7 +68,7 @@ module OpenC3
66
68
 
67
69
  def as_json(*a)
68
70
  result = super(*a)
69
- result['params'] = [@seconds_item_name, @microseconds_item_name]
71
+ result['params'] = [@seconds_item_name, @microseconds_item_name, @seconds_type, @microseconds_type]
70
72
  result
71
73
  end
72
74
  end # class UnixTimeConversion
@@ -500,13 +500,6 @@ module OpenC3
500
500
  write_to_clients(:write_raw, data) if data
501
501
  end
502
502
 
503
- def interface_disconnect(interface_info)
504
- Logger.info "#{@name}: Tcpip server lost write connection to "\
505
- "#{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
506
- interface_info.interface.disconnect
507
- interface_info.interface.stream_log_pair.stop if interface_info.interface.stream_log_pair
508
- end
509
-
510
503
  def write_thread_hook(packet)
511
504
  packet # By default just return the packet
512
505
  end
@@ -28,7 +28,8 @@ require 'drb/drb'
28
28
  require 'set'
29
29
  require 'openc3/io/json_rpc'
30
30
  require 'openc3/io/json_drb_rack'
31
- require 'rack/handler/puma'
31
+ require 'rackup'
32
+ require 'puma'
32
33
 
33
34
  # Add methods to the Puma::Launcher and Puma::Single class so we can tell
34
35
  # if the server has been started.
@@ -150,7 +151,7 @@ module OpenC3
150
151
  }
151
152
 
152
153
  # The run call will block until the server is stopped.
153
- Rack::Handler::Puma.run(JsonDrbRack.new(self), server_config) do |server|
154
+ Rackup::Handler::Puma.run(JsonDrbRack.new(self), server_config) do |server|
154
155
  @server_mutex.synchronize do
155
156
  @server = server
156
157
  end
@@ -36,9 +36,9 @@ end
36
36
 
37
37
  class Struct #:nodoc:
38
38
  def as_json(options = nil)
39
- pairs = []
40
- self.each_pair { |k, v| pairs << k.to_s; pairs << v.as_json(options) }
41
- Hash[*pairs]
39
+ hash = {}
40
+ self.each_pair { |k, v| hash[k.to_s] = v.as_json(options) }
41
+ hash
42
42
  end
43
43
  end
44
44
 
@@ -115,9 +115,9 @@ end
115
115
 
116
116
  class Hash
117
117
  def as_json(options = nil) #:nodoc:
118
- pairs = []
119
- self.each { |k, v| pairs << k.to_s; pairs << v.as_json(options) }
120
- Hash[*pairs]
118
+ hash = {}
119
+ self.each {|k,v| hash[k.to_s] = v.as_json(options) }
120
+ hash
121
121
  end
122
122
  end
123
123
 
@@ -44,7 +44,8 @@ module OpenC3
44
44
  cycle_hour = nil,
45
45
  cycle_minute = nil,
46
46
  enforce_time_order = true,
47
- buffer_depth = 60 # Default assumes 1 minute of 1Hz data
47
+ buffer_depth = 60, # Default assumes 1 minute of 1Hz data
48
+ scope: $openc3_scope
48
49
  )
49
50
  super(
50
51
  remote_log_directory,
@@ -54,7 +55,8 @@ module OpenC3
54
55
  cycle_size,
55
56
  cycle_hour,
56
57
  cycle_minute,
57
- enforce_time_order
58
+ enforce_time_order,
59
+ scope: scope
58
60
  )
59
61
  @buffer_depth = Integer(buffer_depth)
60
62
  @buffer = []
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'openc3/logs/log_writer'
24
24
  require 'openc3/logs/packet_log_constants'
25
+ require 'openc3/models/target_model'
25
26
  require 'cbor'
26
27
 
27
28
  module OpenC3
@@ -52,7 +53,8 @@ module OpenC3
52
53
  cycle_size = 1_000_000_000,
53
54
  cycle_hour = nil,
54
55
  cycle_minute = nil,
55
- enforce_time_order = true
56
+ enforce_time_order = true,
57
+ scope: $openc3_scope
56
58
  )
57
59
  super(
58
60
  remote_log_directory,
@@ -73,6 +75,9 @@ module OpenC3
73
75
  @target_indexes = {}
74
76
  @next_target_index = 0
75
77
  @data_format = :CBOR # Default to CBOR for improved compression
78
+ @target_id_cache = {}
79
+ @packet_id_cache = {}
80
+ @scope = scope
76
81
  end
77
82
 
78
83
  # Write a packet to the log file.
@@ -171,8 +176,14 @@ module OpenC3
171
176
  @tlm_packet_table[target_name] = target_table
172
177
  end
173
178
  id = nil
174
- target = System.targets[target_name]
175
- id = target.id if target
179
+ unless ENV['OPENC3_NO_STORE']
180
+ id = @target_id_cache[target_name]
181
+ unless id
182
+ target = TargetModel.get(name: target_name, scope: @scope)
183
+ id = target["id"] if target
184
+ @target_id_cache[target_name] = id
185
+ end
186
+ end
176
187
  write_entry(:TARGET_DECLARATION, cmd_or_tlm, target_name, packet_name, nil, nil, nil, id)
177
188
  end
178
189
 
@@ -187,10 +198,14 @@ module OpenC3
187
198
 
188
199
  id = nil
189
200
  begin
190
- if cmd_or_tlm == :CMD
191
- id = System.commands.packet(target_nam, packet_name).config_name
192
- else
193
- id = System.telemetry.packet(target_name, packet_name).config_name
201
+ unless ENV['OPENC3_NO_STORE']
202
+ cache_key = "#{cmd_or_tlm}__#{target_name}__#{packet_name}"
203
+ id = @packet_id_cache[cache_key]
204
+ unless id
205
+ target_model_packet = TargetModel.packet(target_name, packet_name, type: cmd_or_tlm, scope: @scope)
206
+ id = target_model_packet["config_name"] if target_model_packet
207
+ @packet_id_cache[cache_key] = id
208
+ end
194
209
  end
195
210
  rescue
196
211
  # No packet def
@@ -49,7 +49,14 @@ module OpenC3
49
49
  if oldest_list.length > 0
50
50
  @state = 'DELETING_OBJECTS'
51
51
  oldest_list.each_slice(1000) do |slice|
52
- bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
52
+ # The delete_objects function utilizes an MD5 hash when verifying the checksums, which is not
53
+ # FIPS compliant (https://github.com/aws/aws-sdk-ruby/issues/2645).
54
+ # delete_object does NOT require an MD5 hash and will work on FIPS compliant systems. It is
55
+ # probably less performant, but we can instead delete each item one at a time.
56
+ # bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
57
+ slice.each do |item|
58
+ bucket.delete_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: item)
59
+ end
53
60
  @logger.debug("Cleanup deleted #{slice.length} log files")
54
61
  @delete_count += slice.length
55
62
  @metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
@@ -34,7 +34,7 @@ module OpenC3
34
34
  # Should only be one target, but there might be multiple decom microservices for a given target
35
35
  # First Decom microservice has no number in the name
36
36
  if @name =~ /__DECOM__/
37
- @topics << "#{scope}__DECOMINTERFACE__{#{@target_names[0]}}"
37
+ @topics << "#{@scope}__DECOMINTERFACE__{#{@target_names[0]}}"
38
38
  end
39
39
  Topic.update_topic_offsets(@topics)
40
40
  System.telemetry.limits_change_callback = method(:limits_change_callback)
@@ -348,13 +348,13 @@ module OpenC3
348
348
  def initialize(name)
349
349
  @mutex = Mutex.new
350
350
  super(name)
351
+ @interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
351
352
  if @interface_or_router == 'INTERFACE'
352
353
  @metric.set(name: 'interface_tlm_total', value: @count, type: 'counter')
353
354
  else
354
355
  @metric.set(name: 'router_cmd_total', value: @count, type: 'counter')
355
356
  end
356
357
 
357
- @interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
358
358
  @scope = name.split("__")[0]
359
359
  interface_name = name.split("__")[2]
360
360
  if @interface_or_router == 'INTERFACE'
@@ -553,7 +553,7 @@ module OpenC3
553
553
  unknown_packet.extra = packet.extra
554
554
  packet = unknown_packet
555
555
  json_hash = CvtModel.build_json_from_packet(packet)
556
- CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: scope)
556
+ CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: @scope)
557
557
  num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, packet.length].min
558
558
  data = packet.buffer(false)[0..(num_bytes_to_print - 1)]
559
559
  prefix = data.each_byte.map { | byte | sprintf("%02X", byte) }.join()
@@ -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 2023, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -79,7 +79,7 @@ module OpenC3
79
79
  end
80
80
 
81
81
  def initialize(name, is_plugin: false)
82
- Logger.info("Microservice running from: ruby #{$0} #{ARGV.join(" ")}")
82
+ @shutdown_complete = false
83
83
  raise "Microservice must be named" unless name
84
84
 
85
85
  @name = name
@@ -120,6 +120,7 @@ module OpenC3
120
120
  # Get configuration for any targets
121
121
  @target_names = @config["target_names"]
122
122
  @target_names ||= []
123
+ # NOTE: setup_targets doesn't do anything if @target_names is empty
123
124
  System.setup_targets(@target_names, @temp_dir, scope: @scope) unless is_plugin
124
125
 
125
126
  # Use at_exit to shutdown cleanly no matter how we die
@@ -199,6 +200,7 @@ module OpenC3
199
200
  end
200
201
 
201
202
  def shutdown
203
+ return if @shutdown_complete
202
204
  @logger.info("Shutting down microservice: #{@name}")
203
205
  @cancel_thread = true
204
206
  @microservice_status_sleeper.cancel if @microservice_status_sleeper
@@ -206,6 +208,7 @@ module OpenC3
206
208
  FileUtils.remove_entry(@temp_dir) if File.exist?(@temp_dir)
207
209
  @metric.shutdown
208
210
  @logger.info("Shutting down microservice complete: #{@name}")
211
+ @shutdown_complete = true
209
212
  end
210
213
  end
211
214
  end
@@ -229,6 +229,7 @@ module OpenC3
229
229
 
230
230
  def get_token(username)
231
231
  if ENV['OPENC3_API_CLIENT'].nil?
232
+ ENV['OPENC3_API_PASSWORD'] ||= ENV['OPENC3_SERVICE_PASSWORD']
232
233
  return OpenC3Authentication.new().token
233
234
  else
234
235
  # Check for offline access token
@@ -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 2023, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -43,6 +43,7 @@ module OpenC3
43
43
 
44
44
  def get_token(username)
45
45
  if ENV['OPENC3_API_CLIENT'].nil?
46
+ ENV['OPENC3_API_PASSWORD'] ||= ENV['OPENC3_SERVICE_PASSWORD']
46
47
  return OpenC3Authentication.new().token
47
48
  else
48
49
  # Check for offline access token
@@ -125,7 +126,8 @@ module OpenC3
125
126
 
126
127
  def clear_expired(activity)
127
128
  begin
128
- ActivityModel.range_destroy(name: @timeline_name, scope: @scope, min: activity.start, max: activity.stop)
129
+ num = ActivityModel.range_destroy(name: @timeline_name, scope: @scope, min: activity.start, max: activity.stop)
130
+ @logger.info "#{@timeline_name} clear_expired removed #{num} items from #{activity.start} to #{activity.stop}"
129
131
  activity.add_event(status: 'completed')
130
132
  rescue StandardError => e
131
133
  @logger.error "#{@timeline_name} clear_expired failed > #{activity.as_json(:allow_nan => true)} #{e.message}"
@@ -231,15 +233,15 @@ module OpenC3
231
233
  @logger.info "#{@timeline_name} timeine manager exiting"
232
234
  end
233
235
 
234
- # Add task to remove events older than 7 time
236
+ # Add task to remove events older than 7 days
235
237
  def add_expire_activity
236
238
  now = Time.now.to_i
237
239
  @expire = now + 3_000
238
240
  activity = ActivityModel.new(
239
241
  name: @timeline_name,
240
242
  scope: @scope,
241
- start: (now - 86_400 * 7),
242
- stop: (now - 82_800 * 7),
243
+ start: 0,
244
+ stop: (now - 86_400 * 7),
243
245
  kind: 'EXPIRE',
244
246
  data: {}
245
247
  )
@@ -0,0 +1,22 @@
1
+ require 'openc3/utilities/migration'
2
+ require 'openc3/models/tool_config_model'
3
+
4
+ module OpenC3
5
+ class TlmViewerConfig < Migration
6
+ def self.run
7
+ ScopeModel.names.each do |scope|
8
+ # Get all existing ToolConfigModels and change keys from tlm_viewer to telemetry_viewer
9
+ names = ToolConfigModel.list_configs('tlm_viewer')
10
+ names.each do |name|
11
+ config = ToolConfigModel.load_config('tlm_viewer', name)
12
+ ToolConfigModel.save_config('telemetry_viewer', name, config)
13
+ ToolConfigModel.delete_config('tlm_viewer', name)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ unless ENV['OPENC3_NO_MIGRATE']
21
+ OpenC3::TlmViewerConfig.run
22
+ 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 2023, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -94,13 +94,31 @@ module OpenC3
94
94
  # Remove one member from a sorted set.
95
95
  # @return [Integer] count of the members removed
96
96
  def self.destroy(name:, scope:, score:)
97
- Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", score, score)
97
+ result = Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", score, score)
98
+ notification = {
99
+ # start / stop to match SortedModel
100
+ 'data' => JSON.generate({'start' => score}),
101
+ 'kind' => 'deleted',
102
+ 'type' => 'activity',
103
+ 'timeline' => name
104
+ }
105
+ TimelineTopic.write_activity(notification, scope: scope)
106
+ return result
98
107
  end
99
108
 
100
109
  # Remove members from min to max of the sorted set.
101
110
  # @return [Integer] count of the members removed
102
111
  def self.range_destroy(name:, scope:, min:, max:)
103
- Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", min, max)
112
+ result = Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", min, max)
113
+ notification = {
114
+ # start / stop to match SortedModel
115
+ 'data' => JSON.generate({'start' => min, 'stop' => max}),
116
+ 'kind' => 'deleted',
117
+ 'type' => 'activity',
118
+ 'timeline' => name
119
+ }
120
+ TimelineTopic.write_activity(notification, scope: scope)
121
+ return result
104
122
  end
105
123
 
106
124
  # @return [ActivityModel] Model generated from the passed JSON
@@ -237,10 +237,11 @@ module OpenC3
237
237
  end
238
238
 
239
239
  tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
240
- @@override_cache[tgt_pkt_key] = [Time.now, hash]
241
240
  if hash.empty?
241
+ @@override_cache.delete(tgt_pkt_key)
242
242
  Store.hdel("#{scope}__override__#{target_name}", packet_name)
243
243
  else
244
+ @@override_cache[tgt_pkt_key] = [Time.now, hash]
244
245
  Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
245
246
  end
246
247
  end
@@ -27,6 +27,7 @@ require 'rubygems'
27
27
  require 'rubygems/uninstaller'
28
28
  require 'tempfile'
29
29
  require 'openc3/utilities/process_manager'
30
+ require 'openc3/api/api'
30
31
  require 'pathname'
31
32
 
32
33
  module OpenC3
@@ -35,6 +36,8 @@ module OpenC3
35
36
  # and destroy to allow interaction with gem files from the PluginModel and
36
37
  # the GemsController.
37
38
  class GemModel
39
+ include Api
40
+
38
41
  def self.names
39
42
  result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
40
43
  return result.sort
@@ -53,7 +56,7 @@ module OpenC3
53
56
  FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cache/#{File.basename(gem_file_path)}")
54
57
  if gem_install
55
58
  Logger.info "Installing gem: #{gem_filename}"
56
- result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "gem_install", gem_filename, Time.now + 3600.0, scope: scope)
59
+ result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "package_install", gem_filename, Time.now + 3600.0, scope: scope)
57
60
  return result.name
58
61
  end
59
62
  else
@@ -145,7 +145,13 @@ module OpenC3
145
145
  unless @cmd
146
146
  type = self.class._get_type
147
147
  microservice_name = "#{@scope}__#{type}__#{@name}"
148
- @cmd = ["ruby", "#{type.downcase}_microservice.rb", microservice_name]
148
+ if config_params[0] and File.extname(config_params[0]) == '.py'
149
+ work_dir.sub!('openc3/lib', 'openc3/python')
150
+ @cmd = ["python", "#{type.downcase}_microservice.py", microservice_name]
151
+ else
152
+ # If there are no config_params we assume ruby
153
+ @cmd = ["ruby", "#{type.downcase}_microservice.rb", microservice_name]
154
+ end
149
155
  end
150
156
  @work_dir = work_dir
151
157
  @ports = ports
@@ -417,7 +423,7 @@ module OpenC3
417
423
  # Respawn the microservice
418
424
  type = self.class._get_type
419
425
  microservice_name = "#{@scope}__#{type}__#{@name}"
420
- microservice = MicroserviceModel.get_model(name: microservice_name, scope: scope)
426
+ microservice = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
421
427
  microservice.target_names.delete(target_name) unless @target_names.include?(target_name)
422
428
  microservice.update
423
429
  end
@@ -432,11 +438,11 @@ module OpenC3
432
438
 
433
439
  if unmap_old
434
440
  # Remove from old interface
435
- all_interfaces = InterfaceModel.all(scope: scope)
441
+ all_interfaces = InterfaceModel.all(scope: @scope)
436
442
  old_interface = nil
437
443
  all_interfaces.each do |old_interface_name, old_interface_details|
438
444
  if old_interface_details['target_names'].include?(target_name)
439
- old_interface = InterfaceModel.from_json(old_interface_details, scope: scope)
445
+ old_interface = InterfaceModel.from_json(old_interface_details, scope: @scope)
440
446
  old_interface.unmap_target(target_name, cmd_only: cmd_only, tlm_only: tlm_only) if old_interface
441
447
  end
442
448
  end
@@ -451,7 +457,7 @@ module OpenC3
451
457
  # Respawn the microservice
452
458
  type = self.class._get_type
453
459
  microservice_name = "#{@scope}__#{type}__#{@name}"
454
- microservice = MicroserviceModel.get_model(name: microservice_name, scope: scope)
460
+ microservice = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
455
461
  microservice.target_names << target_name unless microservice.target_names.include?(target_name)
456
462
  microservice.update
457
463
  end
@@ -33,6 +33,17 @@ module OpenC3
33
33
  "#{scope}#{PRIMARY_KEY}"
34
34
  end
35
35
 
36
+ def self.notify(scope:, kind:, start:, stop: nil)
37
+ json = {'type' => METADATA_TYPE, 'start' => start}
38
+ json['stop'] = stop if stop
39
+ notification = {
40
+ 'data' => JSON.generate(json),
41
+ 'kind' => kind,
42
+ 'type' => 'calendar',
43
+ }
44
+ CalendarTopic.write_entry(notification, scope: scope)
45
+ end
46
+
36
47
  attr_reader :color, :metadata, :constraints, :type
37
48
 
38
49
  # @param [Integer] start - Time metadata is active in seconds from Epoch
@@ -42,6 +42,7 @@ module OpenC3
42
42
  attr_accessor :parent
43
43
  attr_accessor :secrets
44
44
  attr_accessor :prefix
45
+ attr_accessor :disable_erb
45
46
 
46
47
  # NOTE: The following three class methods are used by the ModelController
47
48
  # and are reimplemented to enable various Model class methods to work
@@ -101,6 +102,7 @@ module OpenC3
101
102
  needs_dependencies: false,
102
103
  secrets: [],
103
104
  prefix: nil,
105
+ disable_erb: nil,
104
106
  scope:
105
107
  )
106
108
  parts = name.split("__")
@@ -125,6 +127,7 @@ module OpenC3
125
127
  @needs_dependencies = needs_dependencies
126
128
  @secrets = secrets
127
129
  @prefix = prefix
130
+ @disable_erb = disable_erb
128
131
  @bucket = Bucket.getClient()
129
132
  end
130
133
 
@@ -145,7 +148,8 @@ module OpenC3
145
148
  'plugin' => @plugin,
146
149
  'needs_dependencies' => @needs_dependencies,
147
150
  'secrets' => @secrets.as_json(*a),
148
- 'prefix' => @prefix
151
+ 'prefix' => @prefix,
152
+ 'disable_erb' => @disable_erb
149
153
  }
150
154
  end
151
155
 
@@ -201,6 +205,12 @@ module OpenC3
201
205
  when 'ROUTE_PREFIX'
202
206
  parser.verify_num_parameters(1, 1, "#{keyword} <Route Prefix>")
203
207
  @prefix = parameters[0]
208
+ when 'DISABLE_ERB'
209
+ # 0 to unlimited parameters
210
+ @disable_erb ||= []
211
+ if parameters
212
+ @disable_erb.concat(parameters)
213
+ end
204
214
  else
205
215
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Microservice: #{keyword} #{parameters.join(" ")}")
206
216
  end
@@ -220,8 +230,11 @@ module OpenC3
220
230
 
221
231
  # Load microservice files
222
232
  data = File.read(filename, mode: "rb")
223
- OpenC3.set_working_dir(File.dirname(filename)) do
224
- data = ERB.new(data.comment_erb(), trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
233
+ erb_disabled = check_disable_erb(filename)
234
+ unless erb_disabled
235
+ OpenC3.set_working_dir(File.dirname(filename)) do
236
+ data = ERB.new(data.comment_erb(), trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
237
+ end
225
238
  end
226
239
  unless validate_only
227
240
  @bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: data)