openc3 5.14.0 → 5.14.2

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +19 -15
  3. data/bin/pipinstall +9 -0
  4. data/data/config/target_config.yaml +2 -1
  5. data/lib/openc3/config/config_parser.rb +12 -12
  6. data/lib/openc3/conversions/conversion.rb +4 -4
  7. data/lib/openc3/interfaces/interface.rb +25 -18
  8. data/lib/openc3/microservices/decom_microservice.rb +4 -2
  9. data/lib/openc3/microservices/interface_microservice.rb +27 -23
  10. data/lib/openc3/microservices/log_microservice.rb +6 -1
  11. data/lib/openc3/microservices/microservice.rb +28 -0
  12. data/lib/openc3/models/microservice_model.rb +5 -1
  13. data/lib/openc3/models/plugin_model.rb +11 -7
  14. data/lib/openc3/models/target_model.rb +36 -4
  15. data/lib/openc3/operators/microservice_operator.rb +23 -21
  16. data/lib/openc3/packets/commands.rb +4 -0
  17. data/lib/openc3/packets/limits_response.rb +1 -1
  18. data/lib/openc3/packets/packet_config.rb +78 -31
  19. data/lib/openc3/packets/packet_item.rb +3 -2
  20. data/lib/openc3/packets/packet_item_limits.rb +2 -1
  21. data/lib/openc3/packets/parsers/limits_response_parser.rb +21 -10
  22. data/lib/openc3/packets/parsers/processor_parser.rb +19 -10
  23. data/lib/openc3/packets/telemetry.rb +4 -0
  24. data/lib/openc3/system/system.rb +31 -1
  25. data/lib/openc3/system/system_config.rb +9 -10
  26. data/lib/openc3/system/target.rb +6 -6
  27. data/lib/openc3/utilities/cli_generator.rb +18 -3
  28. data/lib/openc3/utilities/python_proxy.rb +52 -0
  29. data/lib/openc3/version.rb +5 -5
  30. data/templates/conversion/conversion.py +1 -1
  31. data/templates/conversion/conversion.rb +1 -1
  32. data/templates/limits_response/response.py +2 -2
  33. data/templates/limits_response/response.rb +2 -2
  34. data/templates/microservice/microservices/TEMPLATE/microservice.py +6 -3
  35. data/templates/target/targets/TARGET/lib/target.py +2 -2
  36. data/templates/target/targets/TARGET/target.txt +2 -0
  37. data/templates/tool_angular/package.json +2 -2
  38. data/templates/tool_svelte/package.json +1 -1
  39. data/templates/tool_svelte/src/services/config-parser.js +5 -6
  40. data/templates/tool_svelte/src/services/openc3-api.js +9 -1
  41. data/templates/tool_vue/package.json +2 -2
  42. data/templates/widget/package.json +2 -2
  43. metadata +4 -2
@@ -869,6 +869,42 @@ module OpenC3
869
869
  )
870
870
  end
871
871
  end
872
+
873
+ # Inform microservices of new topics
874
+ # Need to tell loggers to log, and decom to decom
875
+ # We do this for no downtime
876
+ raw_topics = []
877
+ decom_topics = []
878
+ packets.each do |packet|
879
+ if cmd_or_tlm == :TELEMETRY
880
+ raw_topics << "#{@scope}__TELEMETRY__{#{@name}}__#{packet.packet_name.upcase}"
881
+ decom_topics << "#{@scope}__DECOM__{#{@name}}__#{packet.packet_name.upcase}"
882
+ else
883
+ raw_topics << "#{@scope}__COMMAND__{#{@name}}__#{packet.packet_name.upcase}"
884
+ decom_topics << "#{@scope}__DECOMCMD__{#{@name}}__#{packet.packet_name.upcase}"
885
+ end
886
+ end
887
+ if cmd_or_tlm == :TELEMETRY
888
+ Topic.write_topic("MICROSERVICE__#{@scope}__PACKETLOG__#{@name}", {'command' => 'ADD_TOPICS', 'topics' => raw_topics.as_json.to_json})
889
+ add_topics_to_microservice("#{@scope}__PACKETLOG__#{@name}", raw_topics)
890
+ Topic.write_topic("MICROSERVICE__#{@scope}__DECOMLOG__#{@name}", {'command' => 'ADD_TOPICS', 'topics' => decom_topics.as_json.to_json})
891
+ add_topics_to_microservice("#{@scope}__DECOMLOG__#{@name}", decom_topics)
892
+ Topic.write_topic("MICROSERVICE__#{@scope}__DECOM__#{@name}", {'command' => 'ADD_TOPICS', 'topics' => raw_topics.as_json.to_json})
893
+ add_topics_to_microservice("#{@scope}__DECOM__#{@name}", raw_topics)
894
+ else
895
+ Topic.write_topic("MICROSERVICE__#{@scope}__COMMANDLOG__#{@name}", {'command' => 'ADD_TOPICS', 'topics' => raw_topics.as_json.to_json})
896
+ add_topics_to_microservice("#{@scope}__COMMANDLOG__#{@name}", raw_topics)
897
+ Topic.write_topic("MICROSERVICE__#{@scope}__DECOMCMDLOG__#{@name}", {'command' => 'ADD_TOPICS', 'topics' => decom_topics.as_json.to_json})
898
+ add_topics_to_microservice("#{@scope}__DECOMCMDLOG__#{@name}", decom_topics)
899
+ end
900
+ end
901
+
902
+ def add_topics_to_microservice(microservice_name, topics)
903
+ model = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
904
+ model.topics.concat(topics)
905
+ model.topics.uniq!
906
+ model.ignore_changes = true # Don't restart the microservice right now
907
+ model.update
872
908
  end
873
909
 
874
910
  def deploy_commmandlog_microservice(gem_path, variables, topics, instance = nil, parent = nil)
@@ -1109,7 +1145,6 @@ module OpenC3
1109
1145
  decom_command_topic_list = []
1110
1146
  packet_topic_list = []
1111
1147
  decom_topic_list = []
1112
- reduced_topic_list = []
1113
1148
  begin
1114
1149
  system.commands.packets(@name).each do |packet_name, packet|
1115
1150
  command_topic_list << "#{@scope}__COMMAND__{#{@name}}__#{packet_name}"
@@ -1122,9 +1157,6 @@ module OpenC3
1122
1157
  system.telemetry.packets(@name).each do |packet_name, packet|
1123
1158
  packet_topic_list << "#{@scope}__TELEMETRY__{#{@name}}__#{packet_name}"
1124
1159
  decom_topic_list << "#{@scope}__DECOM__{#{@name}}__#{packet_name}"
1125
- reduced_topic_list << "#{@scope}__REDUCED_MINUTE__{#{@name}}__#{packet_name}"
1126
- reduced_topic_list << "#{@scope}__REDUCED_HOUR__{#{@name}}__#{packet_name}"
1127
- reduced_topic_list << "#{@scope}__REDUCED_DAY__{#{@name}}__#{packet_name}"
1128
1160
  end
1129
1161
  rescue
1130
1162
  # No telemetry packets for this target
@@ -98,28 +98,30 @@ module OpenC3
98
98
  previous_parent = @previous_microservices[microservice_name]['parent']
99
99
  if @previous_microservices[microservice_name] != microservice_config
100
100
  # CHANGED
101
- scope = microservice_name.split("__")[0]
102
- Logger.info("Changed microservice detected: #{microservice_name}\nWas: #{@previous_microservices[microservice_name]}\nIs: #{microservice_config}", scope: scope)
103
- if parent or previous_parent
104
- if parent == previous_parent
105
- # Same Parent - Respawn parent
106
- @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
107
- elsif parent and previous_parent
108
- # Parent changed - Respawn both parents
109
- @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
110
- @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
111
- elsif parent
112
- # Moved under a parent - Respawn parent and kill standalone
113
- @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
114
- @removed_microservices[microservice_name] = microservice_config
115
- else # previous_parent
116
- # Moved to standalone - Respawn previous parent and make new
117
- @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
118
- @new_microservices[microservice_name] = microservice_config
101
+ if not microservice_config['ignore_changes']
102
+ scope = microservice_name.split("__")[0]
103
+ Logger.info("Changed microservice detected: #{microservice_name}\nWas: #{@previous_microservices[microservice_name]}\nIs: #{microservice_config}", scope: scope)
104
+ if parent or previous_parent
105
+ if parent == previous_parent
106
+ # Same Parent - Respawn parent
107
+ @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
108
+ elsif parent and previous_parent
109
+ # Parent changed - Respawn both parents
110
+ @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
111
+ @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
112
+ elsif parent
113
+ # Moved under a parent - Respawn parent and kill standalone
114
+ @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
115
+ @removed_microservices[microservice_name] = microservice_config
116
+ else # previous_parent
117
+ # Moved to standalone - Respawn previous parent and make new
118
+ @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
119
+ @new_microservices[microservice_name] = microservice_config
120
+ end
121
+ else
122
+ # Respawn regular microservice
123
+ @changed_microservices[microservice_name] = microservice_config
119
124
  end
120
- else
121
- # Respawn regular microservice
122
- @changed_microservices[microservice_name] = microservice_config
123
125
  end
124
126
  end
125
127
  else
@@ -303,6 +303,10 @@ module OpenC3
303
303
  @config.commands
304
304
  end
305
305
 
306
+ def dynamic_add_packet(packet, affect_ids: false)
307
+ @config.dynamic_add_packet(packet, :COMMAND, affect_ids: affect_ids)
308
+ end
309
+
306
310
  protected
307
311
 
308
312
  def set_parameters(command, params, range_checking)
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  # This file implements a class to handle responses to limits state changes.
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
17
+ # All changes Copyright 2024, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -31,6 +31,7 @@ require 'openc3/packets/parsers/format_string_parser'
31
31
  require 'openc3/packets/parsers/processor_parser'
32
32
  require 'openc3/packets/parsers/xtce_parser'
33
33
  require 'openc3/packets/parsers/xtce_converter'
34
+ require 'openc3/utilities/python_proxy'
34
35
  require 'openc3/conversions'
35
36
  require 'openc3/processors'
36
37
  require 'nokogiri'
@@ -82,6 +83,9 @@ module OpenC3
82
83
  # defined by that identification. Telemetry version
83
84
  attr_reader :tlm_id_value_hash
84
85
 
86
+ # @return [String] Language of current target (ruby or python)
87
+ attr_reader :language
88
+
85
89
  COMMAND = "Command"
86
90
  TELEMETRY = "Telemetry"
87
91
 
@@ -117,7 +121,7 @@ module OpenC3
117
121
  # @param filename [String] The name of the configuration file
118
122
  # @param process_target_name [String] The target name. Pass nil when parsing
119
123
  # an xtce file to automatically determine the target name.
120
- def process_file(filename, process_target_name)
124
+ def process_file(filename, process_target_name, language = 'ruby')
121
125
  # Handle .xtce files
122
126
  extension = File.extname(filename).to_s.downcase
123
127
  if extension == ".xtce" or extension == ".xml"
@@ -128,13 +132,14 @@ module OpenC3
128
132
  # Partial files are included into another file and thus aren't directly processed
129
133
  return if File.basename(filename)[0] == '_' # Partials start with underscore
130
134
 
135
+ @language = language
131
136
  @converted_type = nil
132
137
  @converted_bit_size = nil
133
138
  @proc_text = ''
134
139
  @building_generic_conversion = false
135
140
 
136
141
  process_target_name = process_target_name.upcase
137
- parser = ConfigParser.new("https://openc3.com/docs/v5")
142
+ parser = ConfigParser.new("https://docs.openc3.com/docs")
138
143
  parser.instance_variable_set(:@target_name, process_target_name)
139
144
  parser.parse_file(filename) do |keyword, params|
140
145
  if @building_generic_conversion
@@ -258,7 +263,7 @@ module OpenC3
258
263
  rescue
259
264
  # Doesn't exist
260
265
  end
261
- packets.each do |packet_name, packet|
266
+ packets.each do |_packet_name, packet|
262
267
  File.open(filename, 'a') do |file|
263
268
  file.puts packet.to_config(:TELEMETRY)
264
269
  file.puts ""
@@ -276,7 +281,7 @@ module OpenC3
276
281
  rescue
277
282
  # Doesn't exist
278
283
  end
279
- packets.each do |packet_name, packet|
284
+ packets.each do |_packet_name, packet|
280
285
  File.open(filename, 'a') do |file|
281
286
  file.puts packet.to_config(:COMMAND)
282
287
  file.puts ""
@@ -315,30 +320,60 @@ module OpenC3
315
320
  hash = @cmd_id_value_hash[@current_packet.target_name]
316
321
  hash = {} unless hash
317
322
  @cmd_id_value_hash[@current_packet.target_name] = hash
318
- update_id_value_hash(hash)
323
+ update_id_value_hash(@current_packet, hash)
319
324
  else
320
325
  @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
321
326
  hash = @tlm_id_value_hash[@current_packet.target_name]
322
327
  hash = {} unless hash
323
328
  @tlm_id_value_hash[@current_packet.target_name] = hash
324
- update_id_value_hash(hash)
329
+ update_id_value_hash(@current_packet, hash)
325
330
  end
326
331
  @current_packet = nil
327
332
  @current_item = nil
328
333
  end
329
334
  end
330
335
 
336
+ def dynamic_add_packet(packet, cmd_or_tlm = :TELEMETRY, affect_ids: false)
337
+ if cmd_or_tlm == :COMMAND
338
+ @commands[packet.target_name][packet.packet_name] = packet
339
+
340
+ if affect_ids
341
+ hash = @cmd_id_value_hash[packet.target_name]
342
+ hash = {} unless hash
343
+ @cmd_id_value_hash[packet.target_name] = hash
344
+ update_id_value_hash(packet, hash)
345
+ end
346
+ else
347
+ @telemetry[packet.target_name][packet.packet_name] = packet
348
+
349
+ # Update latest_data lookup for telemetry
350
+ packet.sorted_items.each do |item|
351
+ target_latest_data = @latest_data[packet.target_name]
352
+ target_latest_data[item.name] ||= []
353
+ latest_data_packets = target_latest_data[item.name]
354
+ latest_data_packets << packet unless latest_data_packets.include?(packet)
355
+ end
356
+
357
+ if affect_ids
358
+ hash = @tlm_id_value_hash[packet.target_name]
359
+ hash = {} unless hash
360
+ @tlm_id_value_hash[packet.target_name] = hash
361
+ update_id_value_hash(packet, hash)
362
+ end
363
+ end
364
+ end
365
+
331
366
  protected
332
367
 
333
- def update_id_value_hash(hash)
334
- if @current_packet.id_items.length > 0
368
+ def update_id_value_hash(packet, hash)
369
+ if packet.id_items.length > 0
335
370
  key = []
336
- @current_packet.id_items.each do |item|
371
+ packet.id_items.each do |item|
337
372
  key << item.id_value
338
373
  end
339
- hash[key] = @current_packet
374
+ hash[key] = packet
340
375
  else
341
- hash['CATCHALL'.freeze] = @current_packet
376
+ hash['CATCHALL'.freeze] = packet
342
377
  end
343
378
  end
344
379
 
@@ -394,7 +429,7 @@ module OpenC3
394
429
 
395
430
  # Define a processor class that will be called once when a packet is received
396
431
  when 'PROCESSOR'
397
- ProcessorParser.parse(parser, @current_packet, @current_cmd_or_tlm)
432
+ ProcessorParser.parse(parser, @current_packet, @current_cmd_or_tlm, @language)
398
433
 
399
434
  when 'DISABLE_MESSAGES'
400
435
  usage = "#{keyword}"
@@ -433,18 +468,27 @@ module OpenC3
433
468
  parser.verify_num_parameters(0, 0, usage)
434
469
  @current_packet.hidden = true
435
470
  @current_packet.disabled = true
471
+
436
472
  when 'ACCESSOR'
437
473
  usage = "#{keyword} <Accessor class name>"
438
474
  parser.verify_num_parameters(1, nil, usage)
439
475
  begin
440
- klass = OpenC3.require_class(params[0])
441
- if params.length > 1
442
- @current_packet.accessor = klass.new(@current_packet, *params[1..-1])
476
+ if @language == 'ruby'
477
+ klass = OpenC3.require_class(params[0])
478
+ if params.length > 1
479
+ @current_packet.accessor = klass.new(@current_packet, *params[1..-1])
480
+ else
481
+ @current_packet.accessor = klass.new(@current_packet)
482
+ end
443
483
  else
444
- @current_packet.accessor = klass.new(@current_packet)
484
+ if params.length > 1
485
+ @current_packet.accessor = PythonProxy.new('Accessor', params[0], @current_packet, *params[1..-1])
486
+ else
487
+ @current_packet.accessor = PythonProxy.new('Accessor', params[0], @current_packet)
488
+ end
445
489
  end
446
- rescue Exception => err
447
- raise parser.error(err)
490
+ rescue Exception => e
491
+ raise parser.error(e)
448
492
  end
449
493
 
450
494
  when 'TEMPLATE'
@@ -458,8 +502,8 @@ module OpenC3
458
502
 
459
503
  begin
460
504
  @current_packet.template = parser.read_file(params[0])
461
- rescue Exception => err
462
- raise parser.error(err)
505
+ rescue Exception => e
506
+ raise parser.error(e)
463
507
  end
464
508
 
465
509
  when 'RESPONSE'
@@ -500,16 +544,19 @@ module OpenC3
500
544
  usage = "#{keyword} <conversion class filename> <custom parameters> ..."
501
545
  parser.verify_num_parameters(1, nil, usage)
502
546
  begin
503
- klass = OpenC3.require_class(params[0])
504
- conversion = klass.new(*params[1..(params.length - 1)])
505
- @current_item.public_send("#{keyword.downcase}=".to_sym, conversion)
506
- if klass != ProcessorConversion and (conversion.converted_type.nil? or conversion.converted_bit_size.nil?)
507
- msg = "Read Conversion #{params[0]} on item #{@current_item.name} does not specify converted type or bit size"
508
- @warnings << msg
509
- Logger.instance.warn @warnings[-1]
547
+ if @language == 'ruby'
548
+ klass = OpenC3.require_class(params[0])
549
+ conversion = klass.new(*params[1..(params.length - 1)])
550
+ @current_item.public_send("#{keyword.downcase}=".to_sym, conversion)
551
+ if klass != ProcessorConversion and (conversion.converted_type.nil? or conversion.converted_bit_size.nil?)
552
+ msg = "Read Conversion #{params[0]} on item #{@current_item.name} does not specify converted type or bit size"
553
+ @warnings << msg
554
+ Logger.instance.warn @warnings[-1]
555
+ end
556
+ else
557
+ conversion = PythonProxy.new('Conversion', params[0], *params[1..(params.length - 1)])
558
+ @current_item.public_send("#{keyword.downcase}=".to_sym, conversion)
510
559
  end
511
- rescue Exception => err
512
- raise parser.error(err)
513
560
  end
514
561
 
515
562
  # Apply a polynomial conversion to the current item
@@ -570,7 +617,7 @@ module OpenC3
570
617
  # Define a response class that will be called when the limits state of the
571
618
  # current item changes.
572
619
  when 'LIMITS_RESPONSE'
573
- LimitsResponseParser.parse(parser, @current_item, @current_cmd_or_tlm)
620
+ LimitsResponseParser.parse(parser, @current_item, @current_cmd_or_tlm, @language)
574
621
 
575
622
  # Define a printf style formatting string for the current telemetry item
576
623
  when 'FORMAT_STRING'
@@ -23,6 +23,7 @@
23
23
  require 'openc3/packets/structure_item'
24
24
  require 'openc3/packets/packet_item_limits'
25
25
  require 'openc3/conversions/conversion'
26
+ require 'openc3/utilities/python_proxy'
26
27
  require 'openc3/io/json_rpc' # Includes needed as_json code
27
28
 
28
29
  module OpenC3
@@ -129,7 +130,7 @@ module OpenC3
129
130
 
130
131
  def read_conversion=(read_conversion)
131
132
  if read_conversion
132
- raise ArgumentError, "#{@name}: read_conversion must be a OpenC3::Conversion but is a #{read_conversion.class}" unless OpenC3::Conversion === read_conversion
133
+ raise ArgumentError, "#{@name}: read_conversion must be a OpenC3::Conversion but is a #{read_conversion.class}" unless OpenC3::Conversion === read_conversion or OpenC3::PythonProxy === read_conversion
133
134
 
134
135
  @read_conversion = read_conversion.clone
135
136
  else
@@ -139,7 +140,7 @@ module OpenC3
139
140
 
140
141
  def write_conversion=(write_conversion)
141
142
  if write_conversion
142
- raise ArgumentError, "#{@name}: write_conversion must be a OpenC3::Conversion but is a #{write_conversion.class}" unless OpenC3::Conversion === write_conversion
143
+ raise ArgumentError, "#{@name}: write_conversion must be a OpenC3::Conversion but is a #{write_conversion.class}" unless OpenC3::Conversion === write_conversion or OpenC3::PythonProxy === write_conversion
143
144
 
144
145
  @write_conversion = write_conversion.clone
145
146
  else
@@ -21,6 +21,7 @@
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/packets/limits_response'
24
+ require 'openc3/utilities/python_proxy'
24
25
 
25
26
  module OpenC3
26
27
  # Maintains knowledge of limits for a PacketItem
@@ -96,7 +97,7 @@ module OpenC3
96
97
 
97
98
  def response=(response)
98
99
  if response
99
- raise ArgumentError, "response must be a OpenC3::LimitsResponse but is a #{response.class}" unless OpenC3::LimitsResponse === response
100
+ raise ArgumentError, "response must be a OpenC3::LimitsResponse but is a #{response.class}" unless OpenC3::LimitsResponse === response or OpenC3::PythonProxy === response
100
101
 
101
102
  @response = response.clone
102
103
  else
@@ -14,26 +14,29 @@
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 2024, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
+ require 'openc3/utilities/python_proxy'
24
+
23
25
  module OpenC3
24
26
  class LimitsResponseParser
25
27
  # @param parser [ConfigParser] Configuration parser
26
28
  # @param item [Packet] The current item
27
29
  # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
28
- def self.parse(parser, item, cmd_or_tlm)
29
- parser = LimitsResponseParser.new(parser)
30
+ def self.parse(parser, item, cmd_or_tlm, language = 'ruby')
31
+ parser = LimitsResponseParser.new(parser, language)
30
32
  parser.verify_parameters(cmd_or_tlm)
31
33
  parser.create_limits_response(item)
32
34
  end
33
35
 
34
36
  # @param parser [ConfigParser] Configuration parser
35
- def initialize(parser)
37
+ def initialize(parser, language = 'ruby')
36
38
  @parser = parser
39
+ @language = language
37
40
  end
38
41
 
39
42
  # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
@@ -48,15 +51,23 @@ module OpenC3
48
51
 
49
52
  # @param item [PacketItem] The item the limits response should be added to
50
53
  def create_limits_response(item)
51
- klass = OpenC3.require_class(@parser.parameters[0])
54
+ if @language == 'ruby'
55
+ klass = OpenC3.require_class(@parser.parameters[0])
52
56
 
53
- if @parser.parameters[1]
54
- item.limits.response = klass.new(*@parser.parameters[1..(@parser.parameters.length - 1)])
57
+ if @parser.parameters[1]
58
+ item.limits.response = klass.new(*@parser.parameters[1..(@parser.parameters.length - 1)])
59
+ else
60
+ item.limits.response = klass.new
61
+ end
55
62
  else
56
- item.limits.response = klass.new
63
+ if @parser.parameters[1]
64
+ item.limits.response = PythonProxy.new('LimitsResponse', @parser.parameters[0], *@parser.parameters[1..(@parser.parameters.length - 1)])
65
+ else
66
+ item.limits.response = PythonProxy.new('LimitsResponse', @parser.parameters[0], [])
67
+ end
57
68
  end
58
- rescue Exception => err
59
- raise @parser.error(err, @usage)
69
+ rescue Exception => e
70
+ raise @parser.error(e, @usage)
60
71
  end
61
72
  end
62
73
  end
@@ -21,21 +21,23 @@
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/processors'
24
+ require 'openc3/utilities/python_proxy'
24
25
 
25
26
  module OpenC3
26
27
  class ProcessorParser
27
28
  # @param parser [ConfigParser] Configuration parser
28
29
  # @param packet [Packet] The current packet
29
30
  # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
30
- def self.parse(parser, packet, cmd_or_tlm)
31
- parser = ProcessorParser.new(parser)
31
+ def self.parse(parser, packet, cmd_or_tlm, language = 'ruby')
32
+ parser = ProcessorParser.new(parser, language)
32
33
  parser.verify_parameters(cmd_or_tlm)
33
34
  parser.create_processor(packet)
34
35
  end
35
36
 
36
37
  # @param parser [ConfigParser] Configuration parser
37
- def initialize(parser)
38
+ def initialize(parser, language = 'ruby')
38
39
  @parser = parser
40
+ @language = language
39
41
  end
40
42
 
41
43
  # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
@@ -50,16 +52,23 @@ module OpenC3
50
52
 
51
53
  # @param packet [Packet] The packet the processor should be added to
52
54
  def create_processor(packet)
53
- # require should be performed in target.txt
54
- klass = OpenC3.require_class(@parser.parameters[1])
55
+ if @language == 'ruby'
56
+ # require should be performed in target.txt
57
+ klass = OpenC3.require_class(@parser.parameters[1])
55
58
 
56
- if @parser.parameters[2]
57
- processor = klass.new(*@parser.parameters[2..(@parser.parameters.length - 1)])
59
+ if @parser.parameters[2]
60
+ processor = klass.new(*@parser.parameters[2..(@parser.parameters.length - 1)])
61
+ else
62
+ processor = klass.new
63
+ end
64
+ raise ArgumentError, "processor must be a OpenC3::Processor but is a #{processor.class}" unless OpenC3::Processor === processor
58
65
  else
59
- processor = klass.new
66
+ if @parser.parameters[2]
67
+ processor = PythonProxy.new('Processor', @parser.parameters[1], *@parser.parameters[2..(@parser.parameters.length - 1)])
68
+ else
69
+ processor = PythonProxy.new('Processor', @parser.parameters[1], [])
70
+ end
60
71
  end
61
- raise ArgumentError, "processor must be a OpenC3::Processor but is a #{processor.class}" unless OpenC3::Processor === processor
62
-
63
72
  processor.name = get_processor_name()
64
73
  packet.processors[processor.name] = processor
65
74
  rescue Exception => err
@@ -442,5 +442,9 @@ module OpenC3
442
442
  def all
443
443
  @config.telemetry
444
444
  end
445
+
446
+ def dynamic_add_packet(packet, affect_ids: false)
447
+ @config.dynamic_add_packet(packet, :TELEMETRY, affect_ids: affect_ids)
448
+ end
445
449
  end # class Telemetry
446
450
  end
@@ -60,6 +60,9 @@ module OpenC3
60
60
  # The current limits set
61
61
  @@limits_set = nil
62
62
 
63
+ # Callbacks to call once @@instance is created
64
+ @@post_instance_callbacks = []
65
+
63
66
  # @return [Symbol] The current limits_set of the system returned from Redis
64
67
  def self.limits_set
65
68
  unless @@limits_set
@@ -72,6 +75,14 @@ module OpenC3
72
75
  @@limits_set = value.to_s.intern
73
76
  end
74
77
 
78
+ def self.add_post_instance_callback(callback)
79
+ if @@instance
80
+ callback.call()
81
+ else
82
+ @@post_instance_callbacks << callback
83
+ end
84
+ end
85
+
75
86
  def self.setup_targets(target_names, base_dir, scope:)
76
87
  # Nothing to do if there are no targets
77
88
  return if target_names.nil? or target_names.length == 0
@@ -121,11 +132,30 @@ module OpenC3
121
132
  raise "System.instance parameters are required on first call" unless target_names and target_config_dir
122
133
 
123
134
  @@instance_mutex.synchronize do
135
+ return @@instance if @@instance
124
136
  @@instance ||= self.new(target_names, target_config_dir)
137
+ @@post_instance_callbacks.each do |callback|
138
+ callback.call
139
+ end
125
140
  return @@instance
126
141
  end
127
142
  end
128
143
 
144
+ # Dynamically add packets to the system instance
145
+ #
146
+ # @param dynamic_packets [Array of packets]
147
+ # @param cmd_or_tlm [Symbol] :COMMAND or :TELEMETRY
148
+ # @param affect_ids [Boolean] Whether to affect packet id lookup or not
149
+ def self.dynamic_update(dynamic_packets, cmd_or_tlm = :TELEMETRY, affect_ids: false)
150
+ dynamic_packets.each do |packet|
151
+ if cmd_or_tlm == :TELEMETRY
152
+ @@instance.telemetry.dynamic_add_packet(packet, affect_ids: affect_ids)
153
+ else
154
+ @@instance.commands.dynamic_add_packet(packet, affect_ids: affect_ids)
155
+ end
156
+ end
157
+ end
158
+
129
159
  # Create a new System object.
130
160
  #
131
161
  # @param target_names [Array of target names]
@@ -149,7 +179,7 @@ module OpenC3
149
179
  @targets[target.name] = target
150
180
  errors = [] # Store all errors processing the cmd_tlm files
151
181
  target.cmd_tlm_files.each do |cmd_tlm_file|
152
- @packet_config.process_file(cmd_tlm_file, target.name)
182
+ @packet_config.process_file(cmd_tlm_file, target.name, target.language)
153
183
  rescue Exception => error
154
184
  errors << "Error processing #{cmd_tlm_file}:\n#{error.message}"
155
185
  end