openc3 6.8.1 → 6.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/bin/openc3cli +5 -5
  4. data/data/config/command_modifiers.yaml +9 -1
  5. data/data/config/screen.yaml +1 -1
  6. data/lib/openc3/accessors/json_accessor.rb +5 -5
  7. data/lib/openc3/api/interface_api.rb +71 -4
  8. data/lib/openc3/api/router_api.rb +98 -8
  9. data/lib/openc3/api/stash_api.rb +3 -3
  10. data/lib/openc3/api/tlm_api.rb +1 -1
  11. data/lib/openc3/bridge/bridge_config.rb +1 -1
  12. data/lib/openc3/interfaces/file_interface.rb +18 -0
  13. data/lib/openc3/interfaces/http_client_interface.rb +11 -0
  14. data/lib/openc3/interfaces/http_server_interface.rb +8 -0
  15. data/lib/openc3/interfaces/interface.rb +90 -21
  16. data/lib/openc3/interfaces/mqtt_interface.rb +19 -0
  17. data/lib/openc3/interfaces/mqtt_stream_interface.rb +20 -0
  18. data/lib/openc3/interfaces/protocols/burst_protocol.rb +16 -0
  19. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +18 -0
  20. data/lib/openc3/interfaces/protocols/crc_protocol.rb +19 -0
  21. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +17 -1
  22. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +14 -0
  23. data/lib/openc3/interfaces/protocols/length_protocol.rb +25 -1
  24. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +16 -3
  25. data/lib/openc3/interfaces/protocols/protocol.rb +79 -1
  26. data/lib/openc3/interfaces/protocols/slip_protocol.rb +23 -0
  27. data/lib/openc3/interfaces/protocols/template_protocol.rb +38 -0
  28. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +14 -1
  29. data/lib/openc3/interfaces/serial_interface.rb +14 -0
  30. data/lib/openc3/interfaces/simulated_target_interface.rb +1 -1
  31. data/lib/openc3/interfaces/tcpip_client_interface.rb +16 -2
  32. data/lib/openc3/interfaces/tcpip_server_interface.rb +11 -1
  33. data/lib/openc3/interfaces/udp_interface.rb +14 -0
  34. data/lib/openc3/io/json_api_object.rb +1 -1
  35. data/lib/openc3/io/json_drb.rb +1 -1
  36. data/lib/openc3/io/json_drb_object.rb +1 -1
  37. data/lib/openc3/io/json_rpc.rb +5 -4
  38. data/lib/openc3/logs/packet_log_reader.rb +1 -1
  39. data/lib/openc3/logs/packet_log_writer.rb +6 -6
  40. data/lib/openc3/microservices/decom_microservice.rb +5 -1
  41. data/lib/openc3/microservices/interface_microservice.rb +103 -38
  42. data/lib/openc3/microservices/microservice.rb +4 -4
  43. data/lib/openc3/microservices/queue_microservice.rb +11 -1
  44. data/lib/openc3/microservices/reducer_microservice.rb +1 -1
  45. data/lib/openc3/microservices/router_microservice.rb +28 -25
  46. data/lib/openc3/models/activity_model.rb +18 -17
  47. data/lib/openc3/models/cvt_model.rb +12 -9
  48. data/lib/openc3/models/interface_model.rb +70 -12
  49. data/lib/openc3/models/metadata_model.rb +2 -2
  50. data/lib/openc3/models/microservice_status_model.rb +2 -2
  51. data/lib/openc3/models/model.rb +4 -4
  52. data/lib/openc3/models/note_model.rb +2 -2
  53. data/lib/openc3/models/plugin_model.rb +9 -4
  54. data/lib/openc3/models/queue_model.rb +1 -1
  55. data/lib/openc3/models/reaction_model.rb +6 -6
  56. data/lib/openc3/models/script_engine_model.rb +1 -1
  57. data/lib/openc3/models/script_status_model.rb +3 -3
  58. data/lib/openc3/models/sorted_model.rb +5 -5
  59. data/lib/openc3/models/target_model.rb +11 -11
  60. data/lib/openc3/models/timeline_model.rb +2 -2
  61. data/lib/openc3/models/tool_model.rb +1 -1
  62. data/lib/openc3/models/trigger_group_model.rb +3 -3
  63. data/lib/openc3/models/trigger_model.rb +6 -6
  64. data/lib/openc3/models/widget_model.rb +1 -1
  65. data/lib/openc3/operators/operator.rb +2 -2
  66. data/lib/openc3/packets/json_packet.rb +1 -1
  67. data/lib/openc3/packets/packet.rb +1 -1
  68. data/lib/openc3/script/calendar.rb +2 -2
  69. data/lib/openc3/script/metadata.rb +4 -4
  70. data/lib/openc3/script/queue.rb +2 -2
  71. data/lib/openc3/script/script_runner.rb +9 -9
  72. data/lib/openc3/script/storage.rb +1 -1
  73. data/lib/openc3/script/tables.rb +2 -2
  74. data/lib/openc3/script/web_socket_api.rb +7 -7
  75. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +0 -12
  76. data/lib/openc3/tools/table_manager/table_manager_core.rb +1 -1
  77. data/lib/openc3/topics/command_decom_topic.rb +3 -3
  78. data/lib/openc3/topics/command_topic.rb +1 -1
  79. data/lib/openc3/topics/interface_topic.rb +45 -5
  80. data/lib/openc3/topics/limits_event_topic.rb +8 -8
  81. data/lib/openc3/topics/router_topic.rb +42 -3
  82. data/lib/openc3/topics/system_events_topic.rb +1 -1
  83. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  84. data/lib/openc3/utilities/authentication.rb +1 -1
  85. data/lib/openc3/utilities/cosmos_rails_formatter.rb +2 -3
  86. data/lib/openc3/utilities/local_mode.rb +8 -8
  87. data/lib/openc3/utilities/logger.rb +3 -3
  88. data/lib/openc3/utilities/running_script.rb +8 -8
  89. data/lib/openc3/version.rb +6 -6
  90. data/templates/plugin/README.md +3 -3
  91. data/templates/plugin/Rakefile +3 -3
  92. data/templates/plugin/plugin.gemspec +1 -0
  93. data/templates/tool_angular/.gitignore +1 -1
  94. data/templates/tool_angular/package.json +2 -48
  95. data/templates/tool_react/.gitignore +1 -2
  96. data/templates/tool_react/package.json +1 -51
  97. data/templates/tool_svelte/.gitignore +1 -2
  98. data/templates/tool_svelte/package.json +1 -49
  99. data/templates/tool_vue/package.json +3 -36
  100. data/templates/widget/Rakefile +1 -1
  101. data/templates/widget/package.json +2 -28
  102. metadata +9 -9
@@ -25,7 +25,7 @@ require 'openc3/microservices/interface_microservice'
25
25
  module OpenC3
26
26
  class RouterMicroservice < InterfaceMicroservice
27
27
  def handle_packet(packet)
28
- RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
28
+ RouterStatusModel.set(@interface.as_json(), scope: @scope)
29
29
  if !packet.identified?
30
30
  # Need to identify so we can find the target
31
31
  identified_packet = System.commands.identify(packet.buffer(false), @interface.cmd_target_names)
@@ -50,37 +50,40 @@ module OpenC3
50
50
  target_name = 'UNKNOWN' unless target_name
51
51
  target = System.targets[target_name]
52
52
 
53
- begin
53
+ # Don't route disabled target names
54
+ if @interface.cmd_target_enabled[target_name]
54
55
  begin
55
- log_message = true # Default is true
56
- # If the packet has the DISABLE_MESSAGES keyword then no messages by default
57
- log_message = false if packet.messages_disabled
58
- # Check if any of the parameters have DISABLE_MESSAGES
59
- packet.sorted_items.each do |item|
60
- if item.states and item.messages_disabled
61
- value = packet.read_item(item)
62
- if item.messages_disabled[value]
63
- log_message = false
64
- break
56
+ begin
57
+ log_message = true # Default is true
58
+ # If the packet has the DISABLE_MESSAGES keyword then no messages by default
59
+ log_message = false if packet.messages_disabled
60
+ # Check if any of the parameters have DISABLE_MESSAGES
61
+ packet.sorted_items.each do |item|
62
+ if item.states and item.messages_disabled
63
+ value = packet.read_item(item)
64
+ if item.messages_disabled[value]
65
+ log_message = false
66
+ break
67
+ end
65
68
  end
66
69
  end
67
- end
68
70
 
69
- if log_message
70
- if target and target_name != 'UNKNOWN'
71
- @logger.info System.commands.format(packet, target.ignored_parameters)
72
- else
73
- @logger.warn "Unidentified packet of #{packet.length} bytes being routed to target #{@interface.cmd_target_names[0]}"
71
+ if log_message
72
+ if target and target_name != 'UNKNOWN'
73
+ @logger.info System.commands.format(packet, target.ignored_parameters)
74
+ else
75
+ @logger.warn "Unidentified packet of #{packet.length} bytes being routed to target #{@interface.cmd_target_names[0]}"
76
+ end
74
77
  end
78
+ rescue => err
79
+ @logger.error "Problem formatting command from router:\n#{err.formatted}"
75
80
  end
76
- rescue => err
77
- @logger.error "Problem formatting command from router:\n#{err.formatted}"
78
- end
79
81
 
80
- RouterTopic.route_command(packet, @interface.cmd_target_names, scope: @scope)
81
- rescue Exception => err
82
- @error = err
83
- @logger.error "Error routing command from #{@interface.name}\n#{err.formatted}"
82
+ RouterTopic.route_command(packet, @interface.cmd_target_names, scope: @scope)
83
+ rescue Exception => err
84
+ @error = err
85
+ @logger.error "Error routing command from #{@interface.name}\n#{err.formatted}"
86
+ end
84
87
  end
85
88
  end
86
89
  end
@@ -56,13 +56,13 @@ module OpenC3
56
56
  raise ActivityInputError.new "start: #{start} must be <= stop: #{stop}"
57
57
  end
58
58
  array = Store.zrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", start, stop, :limit => [0, limit])
59
- return array.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
59
+ return array.map { |value| JSON.parse(value, allow_nan: true, create_additions: true) }
60
60
  end
61
61
 
62
62
  # @return [Array<Hash>] Array up to the limit of the models (as Hash objects) stored under the primary key
63
63
  def self.all(name:, scope:, limit: 100)
64
64
  array = Store.zrange("#{scope}#{PRIMARY_KEY}__#{name}", 0, -1, :limit => [0, limit])
65
- return array.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
65
+ return array.map { |value| JSON.parse(value, allow_nan: true, create_additions: true) }
66
66
  end
67
67
 
68
68
  # @return [String|nil] String of the saved json or nil if score not found under primary_key
@@ -108,7 +108,7 @@ module OpenC3
108
108
 
109
109
  # First find all the activities at the score
110
110
  json = Store.zrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", score, score, :limit => [0, 100])
111
- parsed = json.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
111
+ parsed = json.map { |value| JSON.parse(value, allow_nan: true, create_additions: true) }
112
112
  parsed.each_with_index do |value, index|
113
113
  if uuid
114
114
  # If the uuid is given then only delete activities that match the uuid
@@ -128,7 +128,7 @@ module OpenC3
128
128
 
129
129
  notification = {
130
130
  # start / stop to match SortedModel
131
- 'data' => JSON.generate({'start' => score, 'uuid' => uuid}),
131
+ 'data' => JSON.generate({'start' => score, 'uuid' => uuid}, allow_nan: true),
132
132
  'kind' => 'deleted',
133
133
  'type' => 'activity',
134
134
  'timeline' => name
@@ -143,7 +143,7 @@ module OpenC3
143
143
  result = Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", min, max)
144
144
  notification = {
145
145
  # start / stop to match SortedModel
146
- 'data' => JSON.generate({'start' => min, 'stop' => max}),
146
+ 'data' => JSON.generate({'start' => min, 'stop' => max}, allow_nan: true),
147
147
  'kind' => 'deleted',
148
148
  'type' => 'activity',
149
149
  'timeline' => name
@@ -154,7 +154,7 @@ module OpenC3
154
154
 
155
155
  # @return [ActivityModel] Model generated from the passed JSON
156
156
  def self.from_json(json, name:, scope:)
157
- json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
157
+ json = JSON.parse(json, allow_nan: true, create_additions: true) if String === json
158
158
  raise "json data is nil" if json.nil?
159
159
  self.new(**json.transform_keys(&:to_sym), name: name, scope: scope)
160
160
  end
@@ -204,7 +204,7 @@ module OpenC3
204
204
  # Adding a '(' makes the max value exclusive
205
205
  array = Store.zrevrangebyscore(@primary_key, "(#{stop}", start - MAX_DURATION)
206
206
  array.each do |value|
207
- activity = JSON.parse(value, :allow_nan => true, :create_additions => true)
207
+ activity = JSON.parse(value, allow_nan: true, create_additions: true)
208
208
  if ignore_score == activity['start']
209
209
  next
210
210
  elsif activity['stop'] > start
@@ -289,7 +289,7 @@ module OpenC3
289
289
  # Get all the existing events in the recurring time range as well as those before
290
290
  # the start of the recurring time range to ensure we don't start inside an existing event
291
291
  existing = Store.zrevrangebyscore(@primary_key, @recurring['end'] - 1, @recurring['start'] - MAX_DURATION)
292
- existing.map! {|value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
292
+ existing.map! {|value| JSON.parse(value, allow_nan: true, create_additions: true) }
293
293
  end
294
294
  last_stop = nil
295
295
 
@@ -315,7 +315,7 @@ module OpenC3
315
315
  end
316
316
  end
317
317
  end
318
- multi.zadd(@primary_key, @start, JSON.generate(self.as_json(:allow_nan => true)))
318
+ multi.zadd(@primary_key, @start, JSON.generate(self.as_json, allow_nan: true))
319
319
  last_stop = @stop
320
320
  end
321
321
  end
@@ -331,7 +331,7 @@ module OpenC3
331
331
  end
332
332
  @updated_at = Time.now.to_nsec_from_epoch
333
333
  add_event(status: 'created')
334
- Store.zadd(@primary_key, @start, JSON.generate(self.as_json(:allow_nan => true)))
334
+ Store.zadd(@primary_key, @start, JSON.generate(self.as_json, allow_nan: true))
335
335
  notify(kind: 'created')
336
336
  end
337
337
  end
@@ -359,12 +359,12 @@ module OpenC3
359
359
 
360
360
  add_event(status: 'updated')
361
361
  json = Store.zrangebyscore(@primary_key, old_start, old_start)
362
- parsed = json.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
362
+ parsed = json.map { |value| JSON.parse(value, allow_nan: true, create_additions: true) }
363
363
  parsed.each_with_index do |value, index|
364
364
  if value['uuid'] == old_uuid
365
365
  Store.multi do |multi|
366
366
  multi.zrem(@primary_key, json[index])
367
- multi.zadd(@primary_key, @start, JSON.generate(self.as_json(:allow_nan => true)))
367
+ multi.zadd(@primary_key, @start, JSON.generate(self.as_json, allow_nan: true))
368
368
  end
369
369
  end
370
370
  end
@@ -375,9 +375,10 @@ module OpenC3
375
375
  # commit will make an event and save the object to the redis database
376
376
  # @param [String] status - the event status such as "complete" or "failed"
377
377
  # @param [String] message - an optional message to include in the event
378
- def commit(status:, message: nil, fulfillment: nil)
378
+ # @param [Time] timestamp - optional timestamp to use instead of current time
379
+ def commit(status:, message: nil, fulfillment: nil, timestamp: nil)
379
380
  event = {
380
- 'time' => Time.now.to_i,
381
+ 'time' => timestamp ? timestamp.to_i : Time.now.to_i,
381
382
  'event' => status,
382
383
  'commit' => true
383
384
  }
@@ -386,12 +387,12 @@ module OpenC3
386
387
  @events << event
387
388
 
388
389
  json = Store.zrangebyscore(@primary_key, @start, @start)
389
- parsed = json.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
390
+ parsed = json.map { |value| JSON.parse(value, allow_nan: true, create_additions: true) }
390
391
  parsed.each_with_index do |value, index|
391
392
  if value['uuid'] == @uuid
392
393
  Store.multi do |multi|
393
394
  multi.zrem(@primary_key, json[index])
394
- multi.zadd(@primary_key, @start, JSON.generate(self.as_json(:allow_nan => true)))
395
+ multi.zadd(@primary_key, @start, JSON.generate(self.as_json, allow_nan: true))
395
396
  end
396
397
  end
397
398
  end
@@ -411,7 +412,7 @@ module OpenC3
411
412
  # update the redis stream / timeline topic that something has changed
412
413
  def notify(kind:, extra: nil)
413
414
  notification = {
414
- 'data' => JSON.generate(as_json(:allow_nan => true)),
415
+ 'data' => JSON.generate(as_json, allow_nan: true),
415
416
  'kind' => kind,
416
417
  'type' => 'activity',
417
418
  'timeline' => @name
@@ -48,7 +48,7 @@ module OpenC3
48
48
 
49
49
  # Set the current value table for a target, packet
50
50
  def self.set(hash, target_name:, packet_name:, queued: false, scope: $openc3_scope)
51
- packet_json = JSON.generate(hash.as_json(:allow_nan => true))
51
+ packet_json = JSON.generate(hash.as_json, allow_nan: true)
52
52
  key = "#{scope}__tlm__#{target_name}"
53
53
  tgt_pkt_key = key + "__#{packet_name}"
54
54
  @@packet_cache[tgt_pkt_key] = [Time.now, hash]
@@ -71,7 +71,7 @@ module OpenC3
71
71
  end
72
72
  packet = Store.hget(key, packet_name)
73
73
  raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
74
- hash = JSON.parse(packet, :allow_nan => true, :create_additions => true)
74
+ hash = JSON.parse(packet, allow_nan: true, create_additions: true)
75
75
  @@packet_cache[tgt_pkt_key] = [now, hash]
76
76
  hash
77
77
  end
@@ -310,7 +310,7 @@ module OpenC3
310
310
  all = Store.hgetall("#{scope}__override__#{target_name}")
311
311
  next if all.nil? or all.empty?
312
312
  all.each do |packet_name, hash|
313
- items = JSON.parse(hash, :allow_nan => true, :create_additions => true)
313
+ items = JSON.parse(hash, allow_nan: true, create_additions: true)
314
314
  items.each do |key, value|
315
315
  item = {}
316
316
  item['target_name'] = target_name
@@ -339,7 +339,7 @@ module OpenC3
339
339
  # for the given type
340
340
  def self.override(target_name, packet_name, item_name, value, type: :ALL, scope: $openc3_scope)
341
341
  hash = Store.hget("#{scope}__override__#{target_name}", packet_name)
342
- hash = JSON.parse(hash, :allow_nan => true, :create_additions => true) if hash
342
+ hash = JSON.parse(hash, allow_nan: true, create_additions: true) if hash
343
343
  hash ||= {} # In case the above didn't create anything
344
344
  case type
345
345
  when :ALL
@@ -361,13 +361,13 @@ module OpenC3
361
361
 
362
362
  tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
363
363
  @@override_cache[tgt_pkt_key] = [Time.now, hash]
364
- Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
364
+ Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json, allow_nan: true))
365
365
  end
366
366
 
367
367
  # Normalize a current value table item such that it returns the actual value
368
368
  def self.normalize(target_name, packet_name, item_name, type: :ALL, scope: $openc3_scope)
369
369
  hash = Store.hget("#{scope}__override__#{target_name}", packet_name)
370
- hash = JSON.parse(hash, :allow_nan => true, :create_additions => true) if hash
370
+ hash = JSON.parse(hash, allow_nan: true, create_additions: true) if hash
371
371
  hash ||= {} # In case the above didn't create anything
372
372
  case type
373
373
  when :ALL
@@ -393,7 +393,7 @@ module OpenC3
393
393
  Store.hdel("#{scope}__override__#{target_name}", packet_name)
394
394
  else
395
395
  @@override_cache[tgt_pkt_key] = [Time.now, hash]
396
- Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
396
+ Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json, allow_nan: true))
397
397
  end
398
398
  end
399
399
 
@@ -411,7 +411,10 @@ module OpenC3
411
411
  latest_packet_name = packet_name
412
412
  end
413
413
  end
414
- raise "Item '#{target_name} LATEST #{item_name}' does not exist for scope: #{scope}" if latest == -1
414
+ # Return the first packet name if no packets have been received
415
+ if latest == -1
416
+ latest_packet_name = packet_names[0]
417
+ end
415
418
  return latest_packet_name
416
419
  end
417
420
 
@@ -453,7 +456,7 @@ module OpenC3
453
456
  end
454
457
  override_data = Store.hget("#{scope}__override__#{target_name}", packet_name)
455
458
  if override_data
456
- hash = JSON.parse(override_data, :allow_nan => true, :create_additions => true)
459
+ hash = JSON.parse(override_data, allow_nan: true, create_additions: true)
457
460
  overrides[tgt_pkt_key] = hash
458
461
  else
459
462
  hash = {}
@@ -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 2023, OpenC3, Inc.
17
+ # All changes Copyright 2025, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -33,6 +33,8 @@ module OpenC3
33
33
  attr_accessor :target_names # Redundant superset of cmd_target_names and tlm_target_names for backwards compat
34
34
  attr_accessor :cmd_target_names
35
35
  attr_accessor :tlm_target_names
36
+ attr_accessor :cmd_target_enabled
37
+ attr_accessor :tlm_target_enabled
36
38
  attr_accessor :connect_on_startup
37
39
  attr_accessor :auto_reconnect
38
40
  attr_accessor :reconnect_delay
@@ -104,6 +106,8 @@ module OpenC3
104
106
  target_names: [],
105
107
  cmd_target_names: [],
106
108
  tlm_target_names: [],
109
+ cmd_target_enabled: nil,
110
+ tlm_target_enabled: nil,
107
111
  connect_on_startup: true,
108
112
  auto_reconnect: true,
109
113
  reconnect_delay: 5.0,
@@ -134,6 +138,22 @@ module OpenC3
134
138
  @target_names = target_names
135
139
  @cmd_target_names = cmd_target_names
136
140
  @tlm_target_names = tlm_target_names
141
+ @cmd_target_enabled = cmd_target_enabled
142
+ if @cmd_target_enabled.nil?
143
+ @cmd_target_enabled = {}
144
+ @cmd_target_names.each do |target_name|
145
+ @cmd_target_enabled[target_name] = true
146
+ end
147
+ @cmd_target_enabled['UNKNOWN'] = true
148
+ end
149
+ @tlm_target_enabled = tlm_target_enabled
150
+ if @tlm_target_enabled.nil?
151
+ @tlm_target_enabled = {}
152
+ @tlm_target_names.each do |target_name|
153
+ @tlm_target_enabled[target_name] = true
154
+ end
155
+ @tlm_target_enabled['UNKNOWN'] = true
156
+ end
137
157
  @connect_on_startup = connect_on_startup
138
158
  @auto_reconnect = auto_reconnect
139
159
  @reconnect_delay = reconnect_delay
@@ -178,6 +198,8 @@ module OpenC3
178
198
  interface_or_router.target_names = @target_names.dup
179
199
  interface_or_router.cmd_target_names = @cmd_target_names.dup
180
200
  interface_or_router.tlm_target_names = @tlm_target_names.dup
201
+ interface_or_router.cmd_target_enabled = @cmd_target_enabled.dup
202
+ interface_or_router.tlm_target_enabled = @tlm_target_enabled.dup
181
203
  interface_or_router.connect_on_startup = @connect_on_startup
182
204
  interface_or_router.auto_reconnect = @auto_reconnect
183
205
  interface_or_router.reconnect_delay = @reconnect_delay
@@ -208,6 +230,8 @@ module OpenC3
208
230
  'target_names' => @target_names,
209
231
  'cmd_target_names' => @cmd_target_names,
210
232
  'tlm_target_names' => @tlm_target_names,
233
+ 'cmd_target_enabled' => @cmd_target_enabled,
234
+ 'tlm_target_enabled' => @tlm_target_enabled,
211
235
  'connect_on_startup' => @connect_on_startup,
212
236
  'auto_reconnect' => @auto_reconnect,
213
237
  'reconnect_delay' => @reconnect_delay,
@@ -236,27 +260,59 @@ module OpenC3
236
260
  target
237
261
  end
238
262
 
263
+ def handle_enabled(parser)
264
+ if parser.parameters[1].nil?
265
+ return true
266
+ else
267
+ enabled = parser.parameters[1].to_s.upcase
268
+ if enabled == 'ENABLED'
269
+ return true
270
+ elsif enabled == 'DISABLED'
271
+ return false
272
+ else
273
+ raise parser.error("MAP_TARGET enabled state must be ENABLED or DISABLED.", usage)
274
+ end
275
+ end
276
+ end
277
+
239
278
  # Handles Interface/Router specific configuration keywords
240
279
  def handle_config(parser, keyword, parameters)
241
280
  case keyword
242
281
  when 'MAP_TARGET'
243
- parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
282
+ usage = "#{keyword} <Target Name> <ENABLED/DISABLED>"
283
+ parser.verify_num_parameters(1, 2, usage)
244
284
  target_name = parameters[0].upcase
245
- @target_names << target_name unless @target_names.include?(target_name)
246
- @cmd_target_names << target_name unless @cmd_target_names.include?(target_name)
247
- @tlm_target_names << target_name unless @tlm_target_names.include?(target_name)
285
+ enabled = handle_enabled(parser)
286
+
287
+ if target_name != 'UNKNOWN'
288
+ @target_names << target_name unless @target_names.include?(target_name)
289
+ @cmd_target_names << target_name unless @cmd_target_names.include?(target_name)
290
+ @tlm_target_names << target_name unless @tlm_target_names.include?(target_name)
291
+ end
292
+ @cmd_target_enabled[target_name] = enabled
293
+ @tlm_target_enabled[target_name] = enabled
248
294
 
249
295
  when 'MAP_CMD_TARGET'
250
- parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
296
+ parser.verify_num_parameters(1, 2, "#{keyword} <Target Name> <ENABLED/DISABLED>")
251
297
  target_name = parameters[0].upcase
252
- @target_names << target_name unless @target_names.include?(target_name)
253
- @cmd_target_names << target_name unless @cmd_target_names.include?(target_name)
298
+ enabled = handle_enabled(parser)
299
+
300
+ if target_name != 'UNKNOWN'
301
+ @target_names << target_name unless @target_names.include?(target_name)
302
+ @cmd_target_names << target_name unless @cmd_target_names.include?(target_name)
303
+ end
304
+ @cmd_target_enabled[target_name] = enabled
254
305
 
255
306
  when 'MAP_TLM_TARGET'
256
- parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
307
+ parser.verify_num_parameters(1, 2, "#{keyword} <Target Name> <ENABLED/DISABLED>")
257
308
  target_name = parameters[0].upcase
258
- @target_names << target_name unless @target_names.include?(target_name)
259
- @tlm_target_names << target_name unless @tlm_target_names.include?(target_name)
309
+ enabled = handle_enabled(parser)
310
+
311
+ if target_name != 'UNKNOWN'
312
+ @target_names << target_name unless @target_names.include?(target_name)
313
+ @tlm_target_names << target_name unless @tlm_target_names.include?(target_name)
314
+ end
315
+ @tlm_target_enabled[target_name] = enabled
260
316
 
261
317
  when 'DONT_CONNECT'
262
318
  parser.verify_num_parameters(0, 0, "#{keyword}")
@@ -434,7 +490,7 @@ module OpenC3
434
490
  microservice.update
435
491
  end
436
492
 
437
- def map_target(target_name, cmd_only: false, tlm_only: false, unmap_old: true)
493
+ def map_target(target_name, cmd_only: false, tlm_only: false, unmap_old: true, cmd_enabled: true, tlm_enabled: true)
438
494
  if cmd_only and tlm_only
439
495
  cmd_only = false
440
496
  tlm_only = false
@@ -458,6 +514,8 @@ module OpenC3
458
514
  @target_names << target_name unless @target_names.include?(target_name)
459
515
  @cmd_target_names << target_name unless @cmd_target_names.include?(target_name) or tlm_only
460
516
  @tlm_target_names << target_name unless @tlm_target_names.include?(target_name) or cmd_only
517
+ @cmd_target_enabled[target_name] = cmd_enabled if not cmd_enabled.nil? and @cmd_target_names.include?(target_name)
518
+ @tlm_target_enabled[target_name] = tlm_enabled if not tlm_enabled.nil? and @tlm_target_names.include?(target_name)
461
519
  update()
462
520
 
463
521
  # Respawn the microservice
@@ -37,7 +37,7 @@ module OpenC3
37
37
  json = {'type' => METADATA_TYPE, 'start' => start}
38
38
  json['stop'] = stop if stop
39
39
  notification = {
40
- 'data' => JSON.generate(json),
40
+ 'data' => JSON.generate(json, allow_nan: true),
41
41
  'kind' => kind,
42
42
  'type' => 'calendar',
43
43
  }
@@ -118,7 +118,7 @@ module OpenC3
118
118
  validate(update: update)
119
119
  @updated_at = Time.now.to_nsec_from_epoch
120
120
  MetadataModel.destroy(scope: @scope, start: update) if update
121
- Store.zadd(@primary_key, @start, JSON.generate(as_json(:allow_nan => true)))
121
+ Store.zadd(@primary_key, @start, JSON.generate(as_json, allow_nan: true))
122
122
  if update
123
123
  notify(kind: 'updated')
124
124
  else
@@ -67,8 +67,8 @@ module OpenC3
67
67
  'name' => @name,
68
68
  'state' => @state,
69
69
  'count' => @count,
70
- 'error' => @error.as_json(:allow_nan => true),
71
- 'custom' => @custom.as_json(:allow_nan => true),
70
+ 'error' => @error.as_json(),
71
+ 'custom' => @custom.as_json(),
72
72
  'plugin' => @plugin,
73
73
  'updated_at' => @updated_at
74
74
  }
@@ -46,7 +46,7 @@ module OpenC3
46
46
  def self.get(primary_key, name:)
47
47
  json = store.hget(primary_key, name)
48
48
  if json
49
- return JSON.parse(json, :allow_nan => true, :create_additions => true)
49
+ return JSON.parse(json, allow_nan: true, create_additions: true)
50
50
  else
51
51
  return nil
52
52
  end
@@ -61,7 +61,7 @@ module OpenC3
61
61
  def self.all(primary_key)
62
62
  hash = store.hgetall(primary_key)
63
63
  hash.each do |key, value|
64
- hash[key] = JSON.parse(value, :allow_nan => true, :create_additions => true)
64
+ hash[key] = JSON.parse(value, allow_nan: true, create_additions: true)
65
65
  end
66
66
  hash
67
67
  end
@@ -88,7 +88,7 @@ module OpenC3
88
88
 
89
89
  # @return [Model] Model generated from the passed JSON
90
90
  def self.from_json(json, scope:)
91
- json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
91
+ json = JSON.parse(json, allow_nan: true, create_additions: true) if String === json
92
92
  raise "json data is nil" if json.nil?
93
93
  json[:scope] = scope
94
94
  self.new(**json.transform_keys(&:to_sym), scope: scope)
@@ -159,7 +159,7 @@ module OpenC3
159
159
  else
160
160
  write_store = self.class.store
161
161
  end
162
- write_store.hset(@primary_key, @name, JSON.generate(self.as_json(:allow_nan => true), :allow_nan => true))
162
+ write_store.hset(@primary_key, @name, JSON.generate(self.as_json(), allow_nan: true))
163
163
  end
164
164
 
165
165
  # Alias for create(update: true)
@@ -37,7 +37,7 @@ module OpenC3
37
37
  json = {'type' => NOTE_TYPE, 'start' => start}
38
38
  json['stop'] = stop if stop
39
39
  notification = {
40
- 'data' => JSON.generate(json),
40
+ 'data' => JSON.generate(json, allow_nan: true),
41
41
  'kind' => kind,
42
42
  'type' => 'calendar',
43
43
  }
@@ -101,7 +101,7 @@ module OpenC3
101
101
  validate(update: update)
102
102
  @updated_at = Time.now.to_nsec_from_epoch
103
103
  NoteModel.destroy(scope: @scope, start: update) if update
104
- Store.zadd(@primary_key, @start, JSON.generate(as_json(:allow_nan => true)))
104
+ Store.zadd(@primary_key, @start, JSON.generate(as_json, allow_nan: true))
105
105
  if update
106
106
  notify(kind: 'updated')
107
107
  else
@@ -133,7 +133,7 @@ module OpenC3
133
133
 
134
134
  store_id = Integer(store_id) if store_id
135
135
  model = PluginModel.new(name: gem_name, variables: variables, plugin_txt_lines: plugin_txt_lines, store_id: store_id, scope: scope)
136
- result = model.as_json(:allow_nan => true)
136
+ result = model.as_json()
137
137
  result['existing_plugin_txt_lines'] = existing_plugin_txt_lines if existing_plugin_txt_lines and not process_existing and existing_plugin_txt_lines != result['plugin_txt_lines']
138
138
  return result
139
139
  ensure
@@ -293,7 +293,7 @@ module OpenC3
293
293
  FileUtils.remove_entry_secure(temp_dir, true)
294
294
  tf.unlink if tf
295
295
  end
296
- return plugin_model.as_json(:allow_nan => true)
296
+ return plugin_model.as_json()
297
297
  end
298
298
 
299
299
  def initialize(
@@ -313,7 +313,12 @@ module OpenC3
313
313
  end
314
314
 
315
315
  def create(update: false, force: false, queued: false)
316
- @name = @name + "__#{Time.now.utc.strftime("%Y%m%d%H%M%S")}" if not update and not @name.index("__")
316
+ if not update and not @name.index("__")
317
+ existing_names = Set.new(self.class.names(scope: @scope))
318
+ counter = 0
319
+ counter += 1 while existing_names.include?("#{@name}__#{counter}")
320
+ @name = "#{@name}__#{counter}"
321
+ end
317
322
  super(update: update, force: force, queued: queued)
318
323
  end
319
324
 
@@ -389,7 +394,7 @@ module OpenC3
389
394
 
390
395
  # Reinstall
391
396
  def restore
392
- plugin_hash = self.as_json(:allow_nan => true)
397
+ plugin_hash = self.as_json()
393
398
  OpenC3::PluginModel.install_phase2(plugin_hash, scope: @scope)
394
399
  @destroyed = false
395
400
  end
@@ -98,7 +98,7 @@ module OpenC3
98
98
  def notify(kind:)
99
99
  notification = {
100
100
  'kind' => kind,
101
- 'data' => JSON.generate(as_json(:allow_nan => true)),
101
+ 'data' => JSON.generate(as_json, allow_nan: true),
102
102
  }
103
103
  QueueTopic.write_notification(notification, scope: @scope)
104
104
  end
@@ -206,14 +206,14 @@ module OpenC3
206
206
  end
207
207
  verify_triggers()
208
208
  @updated_at = Time.now.to_nsec_from_epoch
209
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
209
+ Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true))
210
210
  notify(kind: 'created')
211
211
  end
212
212
 
213
213
  def update
214
214
  verify_triggers()
215
215
  @updated_at = Time.now.to_nsec_from_epoch
216
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
216
+ Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true))
217
217
  # No notification as this is only called via reaction_controller which already notifies
218
218
  end
219
219
 
@@ -241,7 +241,7 @@ module OpenC3
241
241
  if @snooze > 0
242
242
  @snoozed_until = Time.now.to_i + @snooze
243
243
  @updated_at = Time.now.to_nsec_from_epoch
244
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
244
+ Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true))
245
245
  notify(kind: 'snoozed')
246
246
  end
247
247
  end
@@ -249,7 +249,7 @@ module OpenC3
249
249
  def awaken
250
250
  @snoozed_until = nil
251
251
  @updated_at = Time.now.to_nsec_from_epoch
252
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
252
+ Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true))
253
253
  notify(kind: 'awakened')
254
254
  end
255
255
 
@@ -272,7 +272,7 @@ module OpenC3
272
272
 
273
273
  # @return [ReactionModel] Model generated from the passed JSON
274
274
  def self.from_json(json, name:, scope:)
275
- json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
275
+ json = JSON.parse(json, allow_nan: true, create_additions: true) if String === json
276
276
  raise "json data is nil" if json.nil?
277
277
  self.new(**json.transform_keys(&:to_sym), name: name, scope: scope)
278
278
  end
@@ -282,7 +282,7 @@ module OpenC3
282
282
  notification = {
283
283
  'kind' => kind,
284
284
  'type' => 'reaction',
285
- 'data' => JSON.generate(as_json(:allow_nan => true)),
285
+ 'data' => JSON.generate(as_json, allow_nan: true),
286
286
  }
287
287
  AutonomicTopic.write_notification(notification, scope: @scope)
288
288
  end
@@ -45,7 +45,7 @@ module OpenC3
45
45
  def self.all(scope: nil)
46
46
  tools = Store.hgetall(PRIMARY_KEY)
47
47
  tools.each do |key, value|
48
- tools[key] = JSON.parse(value, :allow_nan => true, :create_additions => true)
48
+ tools[key] = JSON.parse(value, allow_nan: true, create_additions: true)
49
49
  end
50
50
  return tools
51
51
  end