openc3 6.4.2 → 6.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f50276d75f7b083b8e4ab9bf5572978eb7b67b8d463d5757b7e5dcc64de861b1
4
- data.tar.gz: c7af952c813bf8ca3032443225046ac74dd40d366e42a26e0ab5fd8a13e3de04
3
+ metadata.gz: 5c28c663c7d3743f9bb095a0818ae486accf2f1bb69f5079ec95788153f3005f
4
+ data.tar.gz: 64a60dc5bad2e3cdc78953b2dbd49a689d4b91a08e80bf8a2c4668b308a811bf
5
5
  SHA512:
6
- metadata.gz: e9724e49c5fa0cceffea2b5f3584820542d50a47b5dee4be7e0fafa5af7b6fecf4d152e48b65752ce73f51391219770bc650041ee676d3943eb48efd67f98770
7
- data.tar.gz: 8e4429f6a6200454c4d98b1e8b5c881154be0582db9c096329f7c7039a93f7710533dce063d1d562835bfddc094dcc71e6b8ed0661ef2dcdb53ceae0c3412c8d
6
+ metadata.gz: 88c76af077c0feeec36831656367e9a5c2ff4968d8d567a6d3822f4a7dec1afea33d4921aa6d5941f97c4234a85da68aa7ef7b1767eeb629650a85589f9db673
7
+ data.tar.gz: 0c548964225e629edff50e0666347eedcc8e6b0d9fb7ab5d134fb0a206e481ba2353568fa6ab99f0fbdfdc6758c62eec7320deeb1c0a37a4448544b2ee26215d
data/bin/openc3cli CHANGED
@@ -15,7 +15,7 @@
15
15
  # GNU Affero General Public License for more details.
16
16
 
17
17
  # Modified by OpenC3, Inc.
18
- # All changes Copyright 2024, OpenC3, Inc.
18
+ # All changes Copyright 2025, OpenC3, Inc.
19
19
  # All Rights Reserved
20
20
  #
21
21
  # This file may also be used under the terms of a commercial license
@@ -62,14 +62,7 @@ def print_usage
62
62
  puts " cli help # Displays this information"
63
63
  puts " cli rake # Runs rake in the local directory"
64
64
  puts " cli irb # Runs irb in the local directory"
65
- puts " cli script list /PATH SCOPE # lists script names filtered by path within scope, 'DEFAULT' if not given"
66
- puts " cli script spawn NAME SCOPE variable1=value1 variable2=value2 # Starts named script remotely"
67
- puts " cli script run NAME SCOPE variable1=value1 variable2=value2 # Starts named script, monitoring status on console,\
68
- by default until error or exit"
69
- puts " cli script init # initialize running scripts (Enterprise Only)"
70
- puts " PARAMETERS name-value pairs to form the script's runtime environment"
71
- puts " OPTIONS: --wait 0 seconds to monitor status before detaching from the running script; ie --wait 100"
72
- puts " --disconnect run the script in disconnect mode"
65
+ puts " cli script # Interact with scripts. Run with --help for more info."
73
66
  puts " cli validate /PATH/FILENAME.gem SCOPE variables.txt # Validate a COSMOS plugin gem file"
74
67
  puts " cli load /PATH/FILENAME.gem SCOPE variables.txt # Loads a COSMOS plugin gem file"
75
68
  puts " cli list <SCOPE> # Lists installed plugins, SCOPE is DEFAULT if not given"
@@ -619,86 +612,180 @@ def cli_script_monitor(script_id)
619
612
  return ret_code
620
613
  end
621
614
 
622
- def cli_script_list(args=[])
623
- path = ''
624
- if (args[0] && args[0][0] == '/')
625
- path = (args.shift)[1..-1]+'/'
615
+ def get_env_from_args(args)
616
+ # Figure out if there are any optional environment variables
617
+ # args[0] is the command, args[1] is the script file
618
+ # args[2..-1] are environment variables specified as NAME=VALUE
619
+ environ = {}
620
+ if args.length > 2
621
+ args[2..-1].each do |arg|
622
+ name, value = arg.split('=')
623
+ if name and value
624
+ environ[name] = value
625
+ end
626
+ end
626
627
  end
627
- scope = args[1]
628
- scope ||= 'DEFAULT'
628
+ return environ
629
+ end
630
+
631
+ def cli_script_init
629
632
  require 'openc3/script'
630
- script_list(scope: scope).each do |script_name|
631
- puts(script_name) if script_name.start_with?(path)
632
- end
633
+ initialize_offline_access()
633
634
  return 0
634
635
  end
635
636
 
636
- def cli_script_run(disconnect=false, environment={}, args=[])
637
- # we are limiting the wait for status, not the script run time
638
- # we make 0 mean 'forever'
637
+ def cli_script_list(args, options)
638
+ require 'openc3/script'
639
+ puts script_list(scope: options[:scope])
640
+ return 0
641
+ end
642
+
643
+ def cli_script_run(args, options)
644
+ environment = get_env_from_args(args)
645
+
639
646
  ret_code = ERROR_CODE
640
- wait_limit = 0
641
- if (i = args.index('--wait'))
642
- begin
643
- args.delete('--wait')
644
- # pull out the flag
645
- seconds = args[i]
646
- wait_limit = Integer(seconds, 10) # only decimal, ignore leading 0
647
- args.delete_at(i)
648
- # and its value
649
- rescue ArgumentError
650
- abort(" --wait requires a number of seconds to wait, not [#{seconds}]")
651
- end
652
- end
653
- abort("No script file provided") if args[0].nil?
654
- scope = args[1]
655
- scope ||= 'DEFAULT'
656
647
  require 'openc3/script'
657
- id = script_run(args[0], disconnect: disconnect, environment: environment, scope: scope) # could raise
658
- $script_interrupt_text = " Script #{args[0]} still running remotely.\n" # for Ctrl-C
659
- if (wait_limit < 1) then
660
- ret_code = cli_script_monitor(id)
661
- else
662
- Timeout::timeout(wait_limit, nil, "--wait #{wait_limit} exceeded") do
648
+ if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, scope: options[:scope]))
649
+ puts id
650
+ $script_interrupt_text = " Script #{args[1]} still running remotely.\n" # for Ctrl-C
651
+ if (options[:wait] < 1) then
663
652
  ret_code = cli_script_monitor(id)
664
- rescue Timeout::ExitException, Timeout::Error => e
665
- # Timeout exceptions are also raised by the Websocket API, so we check
666
- if e.message =~ /^--wait /
667
- puts e.message + ", detaching from running script #{args[0]}"
668
- else
669
- raise
653
+ else
654
+ Timeout::timeout(options[:wait], nil, "--wait #{options[:wait]} exceeded") do
655
+ ret_code = cli_script_monitor(id)
656
+ rescue Timeout::ExitException, Timeout::Error => e
657
+ # Timeout exceptions are also raised by the Websocket API, so we check
658
+ if e.message =~ /^--wait /
659
+ puts e.message + ", detaching from running script #{args[1]}"
660
+ else
661
+ raise
662
+ end
670
663
  end
671
664
  end
672
665
  end
673
666
  return ret_code
667
+ rescue => e
668
+ puts "Error running script: #{e.message}"
669
+ puts "Have you called 'script init'?"
670
+ puts e.backtrace
671
+ return ERROR_CODE
674
672
  end
675
673
 
676
- def cli_script_spawn(disconnect=false, environment={}, args=[])
674
+ def cli_script_spawn(args, options)
675
+ environment = get_env_from_args(args)
676
+
677
677
  ret_code = ERROR_CODE
678
- if (args.index('--wait'))
679
- abort("Did you mean \"script run --wait <seconds> [...]\"?")
680
- end
681
- abort("No script file provided") if args[0].nil?
682
- # heaven help you if you left out the script name
683
- scope = args[1]
684
- scope ||= 'DEFAULT'
685
678
  require 'openc3/script'
686
- if (id = script_run(args[0], disconnect: disconnect, environment: environment, scope: scope))
679
+ if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, scope: options[:scope]))
687
680
  puts id
688
681
  ret_code = 0
689
682
  end
690
683
  return ret_code
684
+ rescue => e
685
+ puts "Error running script: #{e.message}"
686
+ puts "Have you called 'script init'?"
687
+ puts e.backtrace
688
+ return ERROR_CODE
691
689
  end
692
690
 
693
- def cli_script_init
691
+ def cli_script_running(args, options)
692
+ ret_code = ERROR_CODE
694
693
  require 'openc3/script'
695
- initialize_offline_access()
694
+ if args[1]
695
+ limit = args[1].to_i
696
+ else
697
+ limit = 100
698
+ end
699
+ if args[2]
700
+ offset = args[2].to_i
701
+ else
702
+ offset = 0
703
+ end
704
+ if (list = running_script_list(limit: limit, offset: offset, scope: options[:scope]))
705
+ if options[:verbose]
706
+ pp list
707
+ else
708
+ printf("%-5s %-20s %-30s %-22s %-10s\n", "ID", "User", "Filename", "Start Time", "State")
709
+ list.each do |hash|
710
+ printf("%-5s %-20s %-30s %-22s %-10s\n", hash['name'], hash['user_full_name'], hash['filename'], hash['start_time'], hash['state'])
711
+ end
712
+ end
713
+ ret_code = 0
714
+ end
715
+ return ret_code
716
+ rescue => e
717
+ puts "Error getting script status for #{args[1]}: #{e.message}"
718
+ puts "Have you called 'script init'?"
719
+ puts e.backtrace
720
+ return ERROR_CODE
721
+ end
722
+
723
+ def cli_script_status(args, options)
724
+ ret_code = ERROR_CODE
725
+ require 'openc3/script'
726
+ if (hash = script_get(args[1], scope: options[:scope]))
727
+ if options[:verbose]
728
+ pp hash
729
+ else
730
+ printf("%-5s %-20s %-30s %-22s %-10s\n", "ID", "User", "Filename", "Start Time", "State")
731
+ printf("%-5s %-20s %-30s %-22s %-10s\n", hash['name'], hash['user_full_name'], hash['filename'], hash['start_time'], hash['state'])
732
+ end
733
+ ret_code = 0
734
+ end
735
+ return ret_code
736
+ rescue => e
737
+ puts "Error getting script status for #{args[1]}: #{e.message}"
738
+ puts "Have you called 'script init'?"
739
+ puts e.backtrace
740
+ return ERROR_CODE
741
+ end
742
+
743
+ def cli_script_stop(args, options)
744
+ require 'openc3/script'
745
+ running_script_stop(args[1], scope: options[:scope])
696
746
  return 0
747
+ rescue => e
748
+ puts "Error stopping script #{args[1]}: #{e.message}"
749
+ puts "Have you called 'script init'?"
750
+ puts e.backtrace
751
+ return ERROR_CODE
697
752
  end
698
753
 
699
- ## cli_script(args) turns an ARGV of [spawn|run] <--wait 123...> <--disconnect> SCRIPT <scope> <ENV_A=1 ENV_B=2 ...>
700
- # into function calls and tidied parameters to remote-control a script via RunningScriptWebSocketApi
701
754
  def cli_script(args=[])
755
+ options = {scope: 'DEFAULT', disconnect: false, wait: 0, verbose: false}
756
+ option_parser = OptionParser.new do |opts|
757
+ opts.banner = "Usage: script --scope SCOPE [init | list | spawn | run]\n" +
758
+ " init Initialize running scripts (Enterprise Only)\n" +
759
+ " list List scripts in the specified scope\n" +
760
+ " spawn SCRIPT [ENV=VALUE] Spawn SCRIPT in the specified scope with optional env vars and return script ID\n" +
761
+ " run SCRIPT [ENV=VALUE] Run SCRIPT in the specified scope with optional env vars and print script output\n" +
762
+ " running [LIMIT] [OFFSET] Get a list of all running scripts (limit 100 by default). Use LIMIT and OFFSET to get large batches.\n" +
763
+ " status SCRIPT_ID Get status for the running script given by SCRIPT_ID\n" +
764
+ " stop SCRIPT_ID Stop the running script given by SCRIPT_ID\n"
765
+ opts.on("-h", "--help", "Show this message") do
766
+ puts opts
767
+ exit
768
+ end
769
+ opts.on("--scope SCOPE", "Run with specified scope (default = DEFAULT)") do |arg|
770
+ options[:scope] = arg
771
+ end
772
+ opts.on("-d", "--disconnect", "Run a script in disconnect mode (default = false)") do |arg|
773
+ options[:disconnect] = arg
774
+ end
775
+ opts.on("-w SECONDS", "--wait SECONDS", "*run only* - wait for the specified number of seconds before aborting script monitoring") do |arg|
776
+ options[:wait] = Integer(arg)
777
+ end
778
+ opts.on("-v", "--verbose", "*status only* - output ALL status information") do |arg|
779
+ options[:verbose] = arg
780
+ end
781
+ end
782
+
783
+ begin
784
+ option_parser.parse!(args)
785
+ rescue
786
+ abort(option_parser.to_s)
787
+ end
788
+
702
789
  ret_code = ERROR_CODE
703
790
  check_environment()
704
791
  # Double check for the OPENC3_API_PASSWORD because it is absolutely required
@@ -706,38 +793,30 @@ def cli_script(args=[])
706
793
  if ENV['OPENC3_API_PASSWORD'].nil? or ENV['OPENC3_API_PASSWORD'].empty?
707
794
  abort "OPENC3_API_PASSWORD environment variable is required for cli script"
708
795
  end
709
- command = args.shift
710
- # pull out the disconnect flag
711
- discon = args.delete('--disconnect')
712
- discon = (discon.is_a? String) ? true : false
713
- environ = {}
714
- args.each do |arg|
715
- name, value = arg.split('=')
716
- if name and value
717
- # add env[k]=v ; pull out "k=v"
718
- environ[name] = value
719
- args.delete(arg)
720
- end
721
- end
796
+
797
+ # The script command is the first parameter and it is required
798
+ command = args[0]
799
+ abort(option_parser.to_s) unless command
800
+
722
801
  case command
723
- # for list
724
- # args[] should now be ["/<path>", "<scope>"]
725
- # or ["/<path>"]
726
- # or ["<scope>"]
727
- # or []
802
+ when 'init'
803
+ ret_code = cli_script_init()
728
804
  when 'list'
729
- ret_code = cli_script_list(args)
730
- # for spawn or run
731
- # args[] should now be ["--wait 100", "script_name", "<scope>"]
732
- # or ["--wait 100", "script_name"]
733
- # or ["script_name", "<scope>"]
734
- # or ["script_name"]
805
+ ret_code = cli_script_list(args, options)
735
806
  when 'spawn'
736
- ret_code = cli_script_spawn(discon, environ, args)
807
+ abort(option_parser.to_s) unless args[1] # script file is required
808
+ ret_code = cli_script_spawn(args, options)
737
809
  when 'run'
738
- ret_code = cli_script_run(discon, environ, args)
739
- when 'init'
740
- ret_code = cli_script_init()
810
+ abort(option_parser.to_s) unless args[1] # script file is required
811
+ ret_code = cli_script_run(args, options)
812
+ when 'running'
813
+ ret_code = cli_script_running(args, options)
814
+ when 'status'
815
+ abort(option_parser.to_s) unless args[1] # script ID is required
816
+ ret_code = cli_script_status(args, options)
817
+ when 'stop'
818
+ abort(option_parser.to_s) unless args[1] # script ID is required
819
+ ret_code = cli_script_stop(args, options)
741
820
  else
742
821
  abort 'openc3cli internal error: parsing arguments'
743
822
  end
@@ -754,13 +833,7 @@ if not ARGV[0].nil? # argument(s) given
754
833
  IRB.start
755
834
 
756
835
  when 'script'
757
- case ARGV[1]
758
- when 'list', 'run', 'spawn', 'init'
759
- cli_script(ARGV[1..-1])
760
- else
761
- # invalid actions, misplaced and malformed leading options and 'help' come here
762
- abort("cli script <action> must be one of #{CLI_SCRIPT_ACTIONS}, not [#{ARGV[1]}]")
763
- end
836
+ cli_script(ARGV[1..-1])
764
837
 
765
838
  when 'rake'
766
839
  if File.exist?('Rakefile')
@@ -790,6 +863,8 @@ if not ARGV[0].nil? # argument(s) given
790
863
  cli_pkg_uninstall(ARGV[1], scope: ARGV[2])
791
864
 
792
865
  when 'generate'
866
+ # To test against a local copy call this file from the root cosmos directory like this:
867
+ # ruby -Iopenc3/lib openc3/bin/openc3cli generate ...
793
868
  OpenC3::CliGenerator.generate(ARGV[1..-1])
794
869
 
795
870
  when 'rubysloc'
@@ -1,17 +1,17 @@
1
1
  ---
2
- - name: Target name
2
+ - name: Target Name
3
3
  required: true
4
4
  description: The target name
5
5
  values: .+
6
- - name: Packet name
6
+ - name: Packet Name
7
7
  required: true
8
8
  description: The packet name
9
9
  values: .+
10
- - name: Item name
10
+ - name: Item Name
11
11
  required: true
12
12
  description: The item name
13
13
  values: .+
14
- - name: Value type
14
+ - name: Value Type
15
15
  required: false
16
16
  description: The type of the value to display. Default is CONVERTED.
17
17
  values: <%= %w(RAW CONVERTED) %>
@@ -118,6 +118,7 @@ PROCESSOR_CONVERSION:
118
118
  description: |
119
119
  This command reads a value from a processor. The value is read from the
120
120
  processor's available values. The processor must be defined in the target's configuration.
121
+ See the [Processor](/docs/configuration/processors) documentation for more information.
121
122
  parameters:
122
123
  - name: Processor Name
123
124
  required: true
@@ -132,7 +133,7 @@ PROCESSOR_CONVERSION:
132
133
  ITEM TEMP1HIGH 0 0 DERIVED "High-water mark for TEMP1"
133
134
  READ_CONVERSION processor_conversion.rb TEMP1WATER HIGH_WATER
134
135
  python_example: |
135
- PROCESSOR TEMP1WATER watermark_processor.rb TEMP1
136
+ PROCESSOR TEMP1WATER openc3/conversions/watermark_processor.py TEMP1
136
137
  ITEM TEMP1HIGH 0 0 DERIVED "High-water mark for TEMP1"
137
138
  READ_CONVERSION openc3/conversions/processor_conversion.py TEMP1WATER HIGH_WATER
138
139
  RECEIVED_COUNT_CONVERSION:
@@ -162,6 +162,7 @@ LIMITS:
162
162
  values: .+
163
163
  LIMITS_RESPONSE:
164
164
  summary: Defines a response class that is called when the limits state of the current item changes
165
+ description: See the [Limits Response](/docs/configuration/limits-response) documentation for more information.
165
166
  ruby_example: LIMITS_RESPONSE example_limits_response.rb 10
166
167
  python_example: LIMITS_RESPONSE example_limits_response.py 10
167
168
  parameters:
@@ -88,4 +88,17 @@ WIDGET:
88
88
  - name: Regex
89
89
  required: false
90
90
  description: Regex to match against filenames. If match, then no ERB processing
91
- values: .+
91
+ values: .+
92
+ SCRIPT_ENGINE:
93
+ summary: Define a script engine to add language support to Script Runner
94
+ example: SCRIPT_ENGINE .print print_script_engine.py
95
+ description: Defines a script engine to add language support to Script Runner
96
+ parameters:
97
+ - name: Extension
98
+ description: Extension that will use this script engine
99
+ required: true
100
+ values: .+
101
+ - name: Script Engine Filename
102
+ description: Filename that implements the script engine. Should be in top level lib folder in plugin.
103
+ required: true
104
+ values: .+
@@ -0,0 +1,51 @@
1
+ ---
2
+ WATERMARK_PROCESSOR:
3
+ summary: Calculates high and low values for a given item
4
+ description: |
5
+ Stores high and low values for a given item as HIGH_WATER and LOW_WATER.
6
+ Values are retrieved using a [ProcessorConversion](/docs/configuration/conversions#processor_conversion).
7
+ parameters:
8
+ - name: Item Name
9
+ description: The item name to calculate high and low values for
10
+ required: true
11
+ values: .+
12
+ - name: Value Type
13
+ required: false
14
+ description: The type of the value to display. Default is CONVERTED.
15
+ values: <%= %w(RAW CONVERTED) %>
16
+ ruby_example: |
17
+ PROCESSOR TEMP1WATER watermark_processor.rb TEMP1
18
+ ITEM TEMP1HIGH 0 0 DERIVED "High-water mark for TEMP1"
19
+ READ_CONVERSION processor_conversion.rb TEMP1WATER HIGH_WATER
20
+ python_example: |
21
+ PROCESSOR TEMP1WATER openc3/conversions/watermark_processor.py TEMP1
22
+ ITEM TEMP1HIGH 0 0 DERIVED "High-water mark for TEMP1"
23
+ READ_CONVERSION openc3/conversions/processor_conversion.py TEMP1WATER HIGH_WATER
24
+ STATISTICS_PROCESSOR:
25
+ summary: Calculates statistics for a given item
26
+ description: |
27
+ This processor calculates statistics for a given item as MIN, MAX, MEAN, and STDDEV
28
+ over a specified number of samples. Values are retrieved using a [ProcessorConversion](/docs/configuration/conversions#processor_conversion).
29
+ parameters:
30
+ - name: Item Name
31
+ description: The item name to calculate statistics for
32
+ required: true
33
+ values: .+
34
+ - name: Samples to Average
35
+ required: true
36
+ description: The number of samples to average for statistics
37
+ values: .*
38
+ - name: Value Type
39
+ required: false
40
+ description: The type of the value to display. Default is CONVERTED.
41
+ values: <%= %w(RAW CONVERTED) %>
42
+ ruby_example: PROCESSOR TEMP1STAT statistics_processor.rb TEMP1 100
43
+ python_example: PROCESSOR TEMP1STAT openc3/processors/statistics_processor.rb TEMP1 100
44
+ ruby_example: |
45
+ PROCESSOR TEMP1STAT statistics_processor.rb TEMP1 100
46
+ ITEM TEMP1STDDEV 0 0 DERIVED "Stddev of most recent 100 samples for TEMP1"
47
+ READ_CONVERSION processor_conversion.rb TEMP1STAT STDDEV FLOAT 64
48
+ python_example: |
49
+ PROCESSOR TEMP1STAT openc3/conversions/statistics_processor.py TEMP1 100
50
+ ITEM TEMP1STDDEV 0 0 DERIVED "Stddev of most recent 100 samples for TEMP1"
51
+ READ_CONVERSION openc3/conversions/processor_conversion.py TEMP1STAT STDDEV FLOAT 64
@@ -135,6 +135,7 @@ META:
135
135
  values: .*
136
136
  PROCESSOR:
137
137
  summary: Defines a processor class that executes code every time a packet is received
138
+ description: See the [Processor](/docs/configuration/processors) documentation for more information.
138
139
  ruby_example: PROCESSOR TEMP1HIGH watermark_processor.rb TEMP1
139
140
  python_example: PROCESSOR TEMP1HIGH watermark_processor.py TEMP1
140
141
  parameters:
@@ -114,8 +114,8 @@ module OpenC3
114
114
  #
115
115
  # @param args [String|Array<String>] See the description for calling style
116
116
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
117
- def set_tlm(*args, type: :CONVERTED, manual: false, scope: $openc3_scope, token: $openc3_token)
118
- target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, scope: scope)
117
+ def set_tlm(*args, type: :CONVERTED, manual: false, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
118
+ target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, cache_timeout: cache_timeout, scope: scope)
119
119
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
120
120
  CvtModel.set_item(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
121
121
  end
@@ -528,7 +528,7 @@ module OpenC3
528
528
  return [target_name, packet_name, item_name]
529
529
  end
530
530
 
531
- def _set_tlm_process_args(args, method_name, scope: $openc3_scope, token: $openc3_token)
531
+ def _set_tlm_process_args(args, method_name, cache_timeout: nil, scope: $openc3_scope, token: $openc3_token)
532
532
  case args.length
533
533
  when 1
534
534
  target_name, packet_name, item_name, value = extract_fields_from_set_tlm_text(args[0])
@@ -544,8 +544,13 @@ module OpenC3
544
544
  target_name = target_name.upcase
545
545
  packet_name = packet_name.upcase
546
546
  item_name = item_name.upcase
547
- # Determine if this item exists, it will raise appropriate errors if not
548
- TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
547
+
548
+ if packet_name == 'LATEST'
549
+ packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope)
550
+ else
551
+ # Determine if this item exists, it will raise appropriate errors if not
552
+ TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
553
+ end
549
554
 
550
555
  return [target_name, packet_name, item_name, value]
551
556
  end
@@ -128,15 +128,21 @@ module OpenC3
128
128
  end
129
129
  if msg_hash['raw']
130
130
  if @interface.connected?
131
- @logger.info "#{@interface.name}: Write raw"
132
- # A raw interface write results in an UNKNOWN packet
133
- command = System.commands.packet('UNKNOWN', 'UNKNOWN')
134
- command.received_count = TargetModel.increment_command_count('UNKNOWN', 'UNKNOWN', 1, scope: @scope)
135
- command = command.clone
136
- command.buffer = msg_hash['raw']
137
- command.received_time = Time.now
138
- CommandTopic.write_packet(command, scope: @scope)
139
- @interface.write_raw(msg_hash['raw'])
131
+ begin
132
+ @logger.info "#{@interface.name}: Write raw"
133
+ # A raw interface write results in an UNKNOWN packet
134
+ command = System.commands.packet('UNKNOWN', 'UNKNOWN')
135
+ command.received_count = TargetModel.increment_command_count('UNKNOWN', 'UNKNOWN', 1, scope: @scope)
136
+ command = command.clone
137
+ command.buffer = msg_hash['raw']
138
+ command.received_time = Time.now
139
+ CommandTopic.write_packet(command, scope: @scope)
140
+ @logger.info("write_raw sent #{msg_hash['raw'].length} bytes to #{@interface.name}", scope: @scope)
141
+ @interface.write_raw(msg_hash['raw'])
142
+ rescue => e
143
+ @logger.error "#{@interface.name}: write_raw: #{e.formatted}"
144
+ next e.message
145
+ end
140
146
  next 'SUCCESS'
141
147
  else
142
148
  next "Interface not connected: #{@interface.name}"
@@ -31,6 +31,7 @@ require 'openc3/models/gem_model'
31
31
  require 'openc3/models/target_model'
32
32
  require 'openc3/models/interface_model'
33
33
  require 'openc3/models/router_model'
34
+ require 'openc3/models/script_engine_model'
34
35
  require 'openc3/models/tool_model'
35
36
  require 'openc3/models/widget_model'
36
37
  require 'openc3/models/microservice_model'
@@ -249,7 +250,7 @@ module OpenC3
249
250
  case keyword
250
251
  when 'VARIABLE', 'NEEDS_DEPENDENCIES'
251
252
  # Ignore during phase 2
252
- when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
253
+ when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET', 'SCRIPT_ENGINE'
253
254
  begin
254
255
  if current_model
255
256
  current_model.create unless validate_only
@@ -260,7 +261,7 @@ module OpenC3
260
261
  # Otherwise we're stuck constantly iterating on the last model
261
262
  ensure
262
263
  current_model = nil
263
- current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
264
+ current_model = OpenC3.const_get((keyword.split('_').collect(&:capitalize).join + 'Model').intern).handle_config(parser,
264
265
  keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
265
266
  end
266
267
  else
@@ -339,7 +340,7 @@ module OpenC3
339
340
  sleep 15 if microservice_count > 0 # Cycle time 5s times 2 plus 5s wait for soft stop and then hard stop
340
341
  # Remove all the other models now that the processes have stopped
341
342
  # Save TargetModel for last as it has the most to cleanup
342
- [InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel].each do |model|
343
+ [InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel, ScriptEngineModel].each do |model|
343
344
  model.find_all_by_plugin(plugin: @name, scope: @scope).each do |_name, model_instance|
344
345
  begin
345
346
  model_instance.destroy
@@ -369,7 +370,7 @@ module OpenC3
369
370
  ensure
370
371
  # Double check everything is gone
371
372
  found = []
372
- [MicroserviceModel, InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel].each do |model|
373
+ [MicroserviceModel, InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel, ScriptEngineModel].each do |model|
373
374
  model.find_all_by_plugin(plugin: @name, scope: @scope).each do |_name, model_instance|
374
375
  found << model_instance
375
376
  end