openc3 5.2.0 → 5.4.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.
- checksums.yaml +4 -4
 - data/bin/openc3cli +108 -105
 - data/data/config/interface_modifiers.yaml +22 -4
 - data/data/config/item_modifiers.yaml +4 -2
 - data/data/config/microservice.yaml +18 -0
 - data/data/config/table_manager.yaml +2 -2
 - data/data/config/tool.yaml +1 -1
 - data/ext/openc3/ext/config_parser/config_parser.c +17 -2
 - data/lib/openc3/api/api.rb +1 -0
 - data/lib/openc3/api/interface_api.rb +12 -0
 - data/lib/openc3/api/metrics_api.rb +97 -0
 - data/lib/openc3/api/router_api.rb +14 -2
 - data/lib/openc3/api/target_api.rb +24 -3
 - data/lib/openc3/api/tlm_api.rb +5 -4
 - data/lib/openc3/config/config_parser.rb +29 -4
 - data/lib/openc3/core_ext/time.rb +6 -1
 - data/lib/openc3/interfaces/interface.rb +27 -26
 - data/lib/openc3/interfaces/mqtt_interface.rb +240 -0
 - data/lib/openc3/interfaces/protocols/override_protocol.rb +2 -61
 - data/lib/openc3/interfaces/protocols/protocol.rb +6 -1
 - data/lib/openc3/interfaces/simulated_target_interface.rb +1 -3
 - data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -11
 - data/lib/openc3/interfaces.rb +2 -3
 - data/lib/openc3/logs/buffered_packet_log_reader.rb +2 -2
 - data/lib/openc3/microservices/cleanup_microservice.rb +17 -1
 - data/lib/openc3/microservices/decom_microservice.rb +12 -9
 - data/lib/openc3/microservices/interface_microservice.rb +93 -9
 - data/lib/openc3/microservices/log_microservice.rb +11 -5
 - data/lib/openc3/microservices/microservice.rb +10 -9
 - data/lib/openc3/microservices/periodic_microservice.rb +7 -0
 - data/lib/openc3/microservices/reaction_microservice.rb +0 -33
 - data/lib/openc3/microservices/reducer_microservice.rb +14 -10
 - data/lib/openc3/microservices/text_log_microservice.rb +12 -3
 - data/lib/openc3/microservices/timeline_microservice.rb +0 -6
 - data/lib/openc3/microservices/trigger_group_microservice.rb +0 -20
 - data/lib/openc3/models/cvt_model.rb +103 -47
 - data/lib/openc3/models/interface_model.rb +23 -0
 - data/lib/openc3/models/metric_model.rb +53 -6
 - data/lib/openc3/models/microservice_model.rb +15 -1
 - data/lib/openc3/models/model.rb +1 -1
 - data/lib/openc3/models/plugin_model.rb +6 -1
 - data/lib/openc3/models/secret_model.rb +53 -0
 - data/lib/openc3/models/target_model.rb +2 -2
 - data/lib/openc3/models/tool_model.rb +17 -8
 - data/lib/openc3/operators/microservice_operator.rb +25 -0
 - data/lib/openc3/operators/operator.rb +5 -1
 - data/lib/openc3/packets/packet.rb +21 -7
 - data/lib/openc3/packets/packet_item.rb +3 -2
 - data/lib/openc3/script/api_shared.rb +18 -2
 - data/lib/openc3/script/script.rb +8 -0
 - data/lib/openc3/script/script_runner.rb +1 -2
 - data/lib/openc3/script/storage.rb +2 -1
 - data/lib/openc3/script/suite.rb +15 -11
 - data/lib/openc3/system/system.rb +6 -3
 - data/lib/openc3/topics/interface_topic.rb +17 -1
 - data/lib/openc3/topics/router_topic.rb +17 -1
 - data/lib/openc3/utilities/aws_bucket.rb +20 -3
 - data/lib/openc3/utilities/bucket.rb +1 -1
 - data/lib/openc3/utilities/bucket_file_cache.rb +1 -1
 - data/lib/openc3/utilities/bucket_utilities.rb +1 -1
 - data/lib/openc3/utilities/local_mode.rb +1 -0
 - data/lib/openc3/utilities/metric.rb +77 -101
 - data/lib/openc3/utilities/redis_secrets.rb +46 -0
 - data/lib/openc3/utilities/s3_autoload.rb +19 -9
 - data/lib/openc3/utilities/secrets.rb +63 -0
 - data/lib/openc3/utilities/target_file.rb +3 -1
 - data/lib/openc3/version.rb +5 -5
 - data/templates/plugin-template/LICENSE.txt +7 -0
 - data/templates/plugin-template/README.md +4 -3
 - data/templates/plugin-template/plugin.gemspec +4 -4
 - metadata +22 -3
 - data/data/config/_interfaces.yaml.err +0 -1017
 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: ascii-8bit
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Copyright 2022 OpenC3, Inc.
         
     | 
| 
      
 4 
     | 
    
         
            +
            # All Rights Reserved.
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            # This program is free software; you can modify and/or redistribute it
         
     | 
| 
      
 7 
     | 
    
         
            +
            # under the terms of the GNU Affero General Public License
         
     | 
| 
      
 8 
     | 
    
         
            +
            # as published by the Free Software Foundation; version 3 with
         
     | 
| 
      
 9 
     | 
    
         
            +
            # attribution addendums as found in the LICENSE.txt
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            # This program is distributed in the hope that it will be useful,
         
     | 
| 
      
 12 
     | 
    
         
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         
     | 
| 
      
 13 
     | 
    
         
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
         
     | 
| 
      
 14 
     | 
    
         
            +
            # GNU Affero General Public License for more details.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            # This file may also be used under the terms of a commercial license
         
     | 
| 
      
 17 
     | 
    
         
            +
            # if purchased from OpenC3, Inc.
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            require 'openc3/models/model'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            module OpenC3
         
     | 
| 
      
 22 
     | 
    
         
            +
              class SecretModel < Model
         
     | 
| 
      
 23 
     | 
    
         
            +
                PRIMARY_KEY = 'openc3__secrets'
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # NOTE: The following three class methods are used by the ModelController
         
     | 
| 
      
 26 
     | 
    
         
            +
                # and are reimplemented to enable various Model class methods to work
         
     | 
| 
      
 27 
     | 
    
         
            +
                def self.get(name:, scope:)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  super("#{scope}__#{PRIMARY_KEY}", name: name)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def self.names(scope:)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  super("#{scope}__#{PRIMARY_KEY}")
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def self.all(scope:)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  super("#{scope}__#{PRIMARY_KEY}")
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
                # END NOTE
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def initialize(name:, value:, scope:)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  super("#{scope}__#{PRIMARY_KEY}", name: name, scope: scope)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @value = value
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                # @return [Hash] JSON encoding of this model
         
     | 
| 
      
 46 
     | 
    
         
            +
                def as_json(*a)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  {
         
     | 
| 
      
 48 
     | 
    
         
            +
                    'name' => @name,
         
     | 
| 
      
 49 
     | 
    
         
            +
                    'value' => @value.as_json(*a),
         
     | 
| 
      
 50 
     | 
    
         
            +
                  }
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -101,7 +101,7 @@ module OpenC3 
     | 
|
| 
       101 
101 
     | 
    
         
             
                      targets[target_name]['modified'] = true if targets[target_name]
         
     | 
| 
       102 
102 
     | 
    
         
             
                    end
         
     | 
| 
       103 
103 
     | 
    
         
             
                  else
         
     | 
| 
       104 
     | 
    
         
            -
                    modified_targets = Bucket.getClient(). 
     | 
| 
      
 104 
     | 
    
         
            +
                    modified_targets = Bucket.getClient().list_files(bucket: ENV['OPENC3_CONFIG_BUCKET'], path: "DEFAULT/targets_modified/", only_directories: true)
         
     | 
| 
       105 
105 
     | 
    
         
             
                    modified_targets.each do |target_name|
         
     | 
| 
       106 
106 
     | 
    
         
             
                      # A target could have been deleted without removing the modified files
         
     | 
| 
       107 
107 
     | 
    
         
             
                      # Thus we have to check for the existance of the target_name key
         
     | 
| 
         @@ -313,7 +313,7 @@ module OpenC3 
     | 
|
| 
       313 
313 
     | 
    
         
             
                  reduced_day_log_retain_time: nil,
         
     | 
| 
       314 
314 
     | 
    
         
             
                  cleanup_poll_time: 900,
         
     | 
| 
       315 
315 
     | 
    
         
             
                  needs_dependencies: false,
         
     | 
| 
       316 
     | 
    
         
            -
                  target_microservices: {},
         
     | 
| 
      
 316 
     | 
    
         
            +
                  target_microservices: {'REDUCER' => [[]]},
         
     | 
| 
       317 
317 
     | 
    
         
             
                  scope:
         
     | 
| 
       318 
318 
     | 
    
         
             
                )
         
     | 
| 
       319 
319 
     | 
    
         
             
                  super("#{scope}__#{PRIMARY_KEY}", name: name, plugin: plugin, updated_at: updated_at,
         
     | 
| 
         @@ -93,8 +93,15 @@ module OpenC3 
     | 
|
| 
       93 
93 
     | 
    
         
             
                # The ToolsTab.vue calls the ToolsController which uses this method to reorder the tools
         
     | 
| 
       94 
94 
     | 
    
         
             
                # Position is index in the list starting with 0 = first
         
     | 
| 
       95 
95 
     | 
    
         
             
                def self.set_position(name:, position:, scope:)
         
     | 
| 
       96 
     | 
    
         
            -
                   
     | 
| 
       97 
     | 
    
         
            -
                   
     | 
| 
      
 96 
     | 
    
         
            +
                  moving = from_json(get(name: name, scope: scope), scope: scope)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  old_pos = moving.position
         
     | 
| 
      
 98 
     | 
    
         
            +
                  new_pos = Integer(position)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  direction = :down
         
     | 
| 
      
 100 
     | 
    
         
            +
                  if (old_pos == new_pos)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    return # we're not doing anything
         
     | 
| 
      
 102 
     | 
    
         
            +
                  elsif (new_pos > old_pos)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    direction = :up
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
       98 
105 
     | 
    
         | 
| 
       99 
106 
     | 
    
         
             
                  # Go through all the tools and reorder
         
     | 
| 
       100 
107 
     | 
    
         
             
                  all(scope: scope).each do |_tool_name, tool|
         
     | 
| 
         @@ -102,12 +109,14 @@ module OpenC3 
     | 
|
| 
       102 
109 
     | 
    
         
             
                    # Update the requested model to the new position
         
     | 
| 
       103 
110 
     | 
    
         
             
                    if tool_model.name == name
         
     | 
| 
       104 
111 
     | 
    
         
             
                      tool_model.position = position
         
     | 
| 
       105 
     | 
    
         
            -
                     
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
                       
     | 
| 
      
 112 
     | 
    
         
            +
                    elsif direction == :down
         
     | 
| 
      
 113 
     | 
    
         
            +
                      if tool_model.position >= new_pos && tool_model.position < old_pos
         
     | 
| 
      
 114 
     | 
    
         
            +
                        tool_model.position += 1
         
     | 
| 
      
 115 
     | 
    
         
            +
                      end
         
     | 
| 
      
 116 
     | 
    
         
            +
                    else # up
         
     | 
| 
      
 117 
     | 
    
         
            +
                      if tool_model.position > old_pos && tool_model.position <= new_pos
         
     | 
| 
      
 118 
     | 
    
         
            +
                        tool_model.position -= 1
         
     | 
| 
      
 119 
     | 
    
         
            +
                      end
         
     | 
| 
       111 
120 
     | 
    
         
             
                    end
         
     | 
| 
       112 
121 
     | 
    
         
             
                    tool_model.update
         
     | 
| 
       113 
122 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -23,8 +23,10 @@ 
     | 
|
| 
       23 
23 
     | 
    
         
             
            require 'openc3'
         
     | 
| 
       24 
24 
     | 
    
         
             
            require 'openc3/models/microservice_model'
         
     | 
| 
       25 
25 
     | 
    
         
             
            require 'openc3/operators/operator'
         
     | 
| 
      
 26 
     | 
    
         
            +
            require 'openc3/utilities/secrets'
         
     | 
| 
       26 
27 
     | 
    
         
             
            require 'redis'
         
     | 
| 
       27 
28 
     | 
    
         
             
            require 'open3'
         
     | 
| 
      
 29 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
       28 
30 
     | 
    
         | 
| 
       29 
31 
     | 
    
         
             
            module OpenC3
         
     | 
| 
       30 
32 
     | 
    
         
             
              # Creates new OperatorProcess objects based on querying the Redis key value store.
         
     | 
| 
         @@ -34,6 +36,7 @@ module OpenC3 
     | 
|
| 
       34 
36 
     | 
    
         
             
                  Logger.microservice_name = "MicroserviceOperator"
         
     | 
| 
       35 
37 
     | 
    
         
             
                  super
         
     | 
| 
       36 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                  @secrets = Secrets.getClient
         
     | 
| 
       37 
40 
     | 
    
         
             
                  @microservices = {}
         
     | 
| 
       38 
41 
     | 
    
         
             
                  @previous_microservices = {}
         
     | 
| 
       39 
42 
     | 
    
         
             
                  @new_microservices = {}
         
     | 
| 
         @@ -53,6 +56,28 @@ module OpenC3 
     | 
|
| 
       53 
56 
     | 
    
         
             
                  env['OPENC3_MICROSERVICE_NAME'] = microservice_name
         
     | 
| 
       54 
57 
     | 
    
         
             
                  container = microservice_config["container"]
         
     | 
| 
       55 
58 
     | 
    
         
             
                  scope = microservice_name.split("__")[0]
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # Setup secrets for microservice
         
     | 
| 
      
 61 
     | 
    
         
            +
                  secrets = microservice_config["secrets"]
         
     | 
| 
      
 62 
     | 
    
         
            +
                  if secrets
         
     | 
| 
      
 63 
     | 
    
         
            +
                    secrets.each do |type, secret_name, env_name_or_path|
         
     | 
| 
      
 64 
     | 
    
         
            +
                      secret_value = @secrets.get(secret_name, scope: scope)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      if secret_value
         
     | 
| 
      
 66 
     | 
    
         
            +
                        case type
         
     | 
| 
      
 67 
     | 
    
         
            +
                        when 'ENV'
         
     | 
| 
      
 68 
     | 
    
         
            +
                          env[env_name_or_path] = secret_value
         
     | 
| 
      
 69 
     | 
    
         
            +
                        when 'FILE'
         
     | 
| 
      
 70 
     | 
    
         
            +
                          FileUtils.mkdir_p(File.dirname(env_name_or_path))
         
     | 
| 
      
 71 
     | 
    
         
            +
                          File.open(env_name_or_path, 'wb') do |file|
         
     | 
| 
      
 72 
     | 
    
         
            +
                            file.write(secret_value)
         
     | 
| 
      
 73 
     | 
    
         
            +
                          end
         
     | 
| 
      
 74 
     | 
    
         
            +
                        end
         
     | 
| 
      
 75 
     | 
    
         
            +
                      else
         
     | 
| 
      
 76 
     | 
    
         
            +
                        Logger.error("Microservice #{microservice_name} references unknown secret: #{secret_name}")
         
     | 
| 
      
 77 
     | 
    
         
            +
                      end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
       56 
81 
     | 
    
         
             
                  return process_definition, work_dir, env, scope, container
         
     | 
| 
       57 
82 
     | 
    
         
             
                end
         
     | 
| 
       58 
83 
     | 
    
         | 
| 
         @@ -160,7 +160,11 @@ module OpenC3 
     | 
|
| 
       160 
160 
     | 
    
         
             
                def soft_stop
         
     | 
| 
       161 
161 
     | 
    
         
             
                  Thread.new do
         
     | 
| 
       162 
162 
     | 
    
         
             
                    Logger.info("Soft shutting down process: #{cmd_line()}", scope: @scope)
         
     | 
| 
       163 
     | 
    
         
            -
                     
     | 
| 
      
 163 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 164 
     | 
    
         
            +
                      Process.kill("SIGINT", @process.pid) if @process # Signal the process to stop
         
     | 
| 
      
 165 
     | 
    
         
            +
                    rescue Errno::ESRCH
         
     | 
| 
      
 166 
     | 
    
         
            +
                      # Process already gone
         
     | 
| 
      
 167 
     | 
    
         
            +
                    end
         
     | 
| 
       164 
168 
     | 
    
         
             
                  end
         
     | 
| 
       165 
169 
     | 
    
         
             
                end
         
     | 
| 
       166 
170 
     | 
    
         | 
| 
         @@ -33,7 +33,7 @@ module OpenC3 
     | 
|
| 
       33 
33 
     | 
    
         
             
              # as managing PacketItem's limit states.
         
     | 
| 
       34 
34 
     | 
    
         
             
              class Packet < Structure
         
     | 
| 
       35 
35 
     | 
    
         
             
                RESERVED_ITEM_NAMES = ['PACKET_TIMESECONDS'.freeze, 'PACKET_TIMEFORMATTED'.freeze, 'RECEIVED_TIMESECONDS'.freeze, 'RECEIVED_TIMEFORMATTED'.freeze, 'RECEIVED_COUNT'.freeze]
         
     | 
| 
       36 
     | 
    
         
            -
                 
     | 
| 
      
 36 
     | 
    
         
            +
                ANY_STATE = 'ANY'
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                # @return [String] Name of the target this packet is associated with
         
     | 
| 
       39 
39 
     | 
    
         
             
                attr_reader :target_name
         
     | 
| 
         @@ -627,8 +627,8 @@ module OpenC3 
     | 
|
| 
       627 
627 
     | 
    
         
             
                        value = value.map do |val, index|
         
     | 
| 
       628 
628 
     | 
    
         
             
                          if item.states.key(val)
         
     | 
| 
       629 
629 
     | 
    
         
             
                            item.states.key(val)
         
     | 
| 
       630 
     | 
    
         
            -
                          elsif item.states.values.include?( 
     | 
| 
       631 
     | 
    
         
            -
                            item.states.key( 
     | 
| 
      
 630 
     | 
    
         
            +
                          elsif item.states.values.include?(ANY_STATE)
         
     | 
| 
      
 631 
     | 
    
         
            +
                            item.states.key(ANY_STATE)
         
     | 
| 
       632 
632 
     | 
    
         
             
                          else
         
     | 
| 
       633 
633 
     | 
    
         
             
                            apply_format_string_and_units(item, val, value_type)
         
     | 
| 
       634 
634 
     | 
    
         
             
                          end
         
     | 
| 
         @@ -637,8 +637,8 @@ module OpenC3 
     | 
|
| 
       637 
637 
     | 
    
         
             
                        state_value = item.states.key(value)
         
     | 
| 
       638 
638 
     | 
    
         
             
                        if state_value
         
     | 
| 
       639 
639 
     | 
    
         
             
                          value = state_value
         
     | 
| 
       640 
     | 
    
         
            -
                        elsif item.states.values.include?( 
     | 
| 
       641 
     | 
    
         
            -
                          value = item.states.key( 
     | 
| 
      
 640 
     | 
    
         
            +
                        elsif item.states.values.include?(ANY_STATE)
         
     | 
| 
      
 641 
     | 
    
         
            +
                          value = item.states.key(ANY_STATE)
         
     | 
| 
       642 
642 
     | 
    
         
             
                        else
         
     | 
| 
       643 
643 
     | 
    
         
             
                          value = apply_format_string_and_units(item, value, value_type)
         
     | 
| 
       644 
644 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -653,7 +653,14 @@ module OpenC3 
     | 
|
| 
       653 
653 
     | 
    
         
             
                      end
         
     | 
| 
       654 
654 
     | 
    
         
             
                    end
         
     | 
| 
       655 
655 
     | 
    
         
             
                  else
         
     | 
| 
       656 
     | 
    
         
            -
                     
     | 
| 
      
 656 
     | 
    
         
            +
                    # Trim a potentially long string (like if they accidentally pass buffer as value_type)
         
     | 
| 
      
 657 
     | 
    
         
            +
                    if value_type.to_s.length > 10
         
     | 
| 
      
 658 
     | 
    
         
            +
                      value_type = value_type.to_s[0...10]
         
     | 
| 
      
 659 
     | 
    
         
            +
                      # Ensure we're not trying to output binary
         
     | 
| 
      
 660 
     | 
    
         
            +
                      value_type = value_type.simple_formatted unless value_type.is_printable?
         
     | 
| 
      
 661 
     | 
    
         
            +
                      value_type += '...'
         
     | 
| 
      
 662 
     | 
    
         
            +
                    end
         
     | 
| 
      
 663 
     | 
    
         
            +
                    raise ArgumentError, "Unknown value type '#{value_type}', must be :RAW, :CONVERTED, :FORMATTED, or :WITH_UNITS"
         
     | 
| 
       657 
664 
     | 
    
         
             
                  end
         
     | 
| 
       658 
665 
     | 
    
         
             
                  return value
         
     | 
| 
       659 
666 
     | 
    
         
             
                end
         
     | 
| 
         @@ -716,7 +723,14 @@ module OpenC3 
     | 
|
| 
       716 
723 
     | 
    
         
             
                  when :FORMATTED, :WITH_UNITS
         
     | 
| 
       717 
724 
     | 
    
         
             
                    raise ArgumentError, "Invalid value type on write: #{value_type}"
         
     | 
| 
       718 
725 
     | 
    
         
             
                  else
         
     | 
| 
       719 
     | 
    
         
            -
                     
     | 
| 
      
 726 
     | 
    
         
            +
                    # Trim potentially long string (like if they accidentally pass buffer as value_type)
         
     | 
| 
      
 727 
     | 
    
         
            +
                    if value_type.to_s.length > 10
         
     | 
| 
      
 728 
     | 
    
         
            +
                      value_type = value_type.to_s[0...10]
         
     | 
| 
      
 729 
     | 
    
         
            +
                      # Ensure we're not trying to output binary
         
     | 
| 
      
 730 
     | 
    
         
            +
                      value_type = value_type.simple_formatted unless value_type.is_printable?
         
     | 
| 
      
 731 
     | 
    
         
            +
                      value_type += '...'
         
     | 
| 
      
 732 
     | 
    
         
            +
                    end
         
     | 
| 
      
 733 
     | 
    
         
            +
                    raise ArgumentError, "Unknown value type '#{value_type}', must be :RAW, :CONVERTED, :FORMATTED, or :WITH_UNITS"
         
     | 
| 
       720 
734 
     | 
    
         
             
                  end
         
     | 
| 
       721 
735 
     | 
    
         
             
                  if @read_conversion_cache
         
     | 
| 
       722 
736 
     | 
    
         
             
                    synchronize() do
         
     | 
| 
         @@ -493,7 +493,7 @@ module OpenC3 
     | 
|
| 
       493 
493 
     | 
    
         
             
                    if self.limits.values
         
     | 
| 
       494 
494 
     | 
    
         
             
                      config['limits'] ||= {}
         
     | 
| 
       495 
495 
     | 
    
         
             
                      config['limits']['persistence_setting'] = self.limits.persistence_setting
         
     | 
| 
       496 
     | 
    
         
            -
                      config['limits'][' 
     | 
| 
      
 496 
     | 
    
         
            +
                      config['limits']['response'] = self.limits.response.to_s if self.limits.response
         
     | 
| 
       497 
497 
     | 
    
         
             
                      self.limits.values.each do |limits_set, limits_values|
         
     | 
| 
       498 
498 
     | 
    
         
             
                        limits = {}
         
     | 
| 
       499 
499 
     | 
    
         
             
                        limits['red_low'] =  limits_values[0]
         
     | 
| 
         @@ -557,9 +557,10 @@ module OpenC3 
     | 
|
| 
       557 
557 
     | 
    
         | 
| 
       558 
558 
     | 
    
         
             
                  item.limits = PacketItemLimits.new
         
     | 
| 
       559 
559 
     | 
    
         
             
                  if hash['limits']
         
     | 
| 
       560 
     | 
    
         
            -
                    # Delete  
     | 
| 
      
 560 
     | 
    
         
            +
                    # Delete the keys so the only ones left are limits sets
         
     | 
| 
       561 
561 
     | 
    
         
             
                    persistence_setting = hash['limits'].delete('persistence_setting')
         
     | 
| 
       562 
562 
     | 
    
         
             
                    item.limits.persistence_setting = persistence_setting if persistence_setting
         
     | 
| 
      
 563 
     | 
    
         
            +
                    hash['limits'].delete('response') # Can't round trip response
         
     | 
| 
       563 
564 
     | 
    
         
             
                    item.limits.enabled = true if hash['limits'].delete('enabled')
         
     | 
| 
       564 
565 
     | 
    
         
             
                    values = {}
         
     | 
| 
       565 
566 
     | 
    
         
             
                    hash['limits'].each do |set, items|
         
     | 
| 
         @@ -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 
     | 
    
         
             
            require 'openc3/script/extract'
         
     | 
| 
         @@ -500,7 +500,20 @@ module OpenC3 
     | 
|
| 
       500 
500 
     | 
    
         
             
                def load_utility(procedure_name)
         
     | 
| 
       501 
501 
     | 
    
         
             
                  return start(procedure_name)
         
     | 
| 
       502 
502 
     | 
    
         
             
                end
         
     | 
| 
       503 
     | 
    
         
            -
                 
     | 
| 
      
 503 
     | 
    
         
            +
                def require_utility(procedure_name)
         
     | 
| 
      
 504 
     | 
    
         
            +
                  @require_utility_cache ||= {}
         
     | 
| 
      
 505 
     | 
    
         
            +
                  if @require_utility_cache[procedure_name]
         
     | 
| 
      
 506 
     | 
    
         
            +
                    return false
         
     | 
| 
      
 507 
     | 
    
         
            +
                  else
         
     | 
| 
      
 508 
     | 
    
         
            +
                    @require_utility_cache[procedure_name] = true
         
     | 
| 
      
 509 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 510 
     | 
    
         
            +
                      return start(procedure_name)
         
     | 
| 
      
 511 
     | 
    
         
            +
                    rescue LoadError
         
     | 
| 
      
 512 
     | 
    
         
            +
                      @require_utility_cache[procedure_name] = false
         
     | 
| 
      
 513 
     | 
    
         
            +
                      raise # reraise the error
         
     | 
| 
      
 514 
     | 
    
         
            +
                    end
         
     | 
| 
      
 515 
     | 
    
         
            +
                  end
         
     | 
| 
      
 516 
     | 
    
         
            +
                end
         
     | 
| 
       504 
517 
     | 
    
         | 
| 
       505 
518 
     | 
    
         
             
                ###########################################################################
         
     | 
| 
       506 
519 
     | 
    
         
             
                # Private implementation details
         
     | 
| 
         @@ -714,6 +727,7 @@ module OpenC3 
     | 
|
| 
       714 
727 
     | 
    
         | 
| 
       715 
728 
     | 
    
         
             
                def _openc3_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate, exp_to_eval, scope: $openc3_scope, token: $openc3_token, &block)
         
     | 
| 
       716 
729 
     | 
    
         
             
                  end_time = Time.now.sys + timeout
         
     | 
| 
      
 730 
     | 
    
         
            +
                  raise "Invalid comparison to non-ascii value" unless exp_to_eval.is_printable?
         
     | 
| 
       717 
731 
     | 
    
         | 
| 
       718 
732 
     | 
    
         
             
                  while true
         
     | 
| 
       719 
733 
     | 
    
         
             
                    work_start = Time.now.sys
         
     | 
| 
         @@ -792,6 +806,7 @@ module OpenC3 
     | 
|
| 
       792 
806 
     | 
    
         
             
                # Wait on an expression to be true.
         
     | 
| 
       793 
807 
     | 
    
         
             
                def openc3_script_wait_implementation_expression(exp_to_eval, timeout, polling_rate, context, scope: $openc3_scope, token: $openc3_token)
         
     | 
| 
       794 
808 
     | 
    
         
             
                  end_time = Time.now.sys + timeout
         
     | 
| 
      
 809 
     | 
    
         
            +
                  raise "Invalid comparison to non-ascii value" unless exp_to_eval.is_printable?
         
     | 
| 
       795 
810 
     | 
    
         | 
| 
       796 
811 
     | 
    
         
             
                  while true
         
     | 
| 
       797 
812 
     | 
    
         
             
                    work_start = Time.now.sys
         
     | 
| 
         @@ -828,6 +843,7 @@ module OpenC3 
     | 
|
| 
       828 
843 
     | 
    
         
             
                end
         
     | 
| 
       829 
844 
     | 
    
         | 
| 
       830 
845 
     | 
    
         
             
                def check_eval(target_name, packet_name, item_name, comparison_to_eval, value, scope: $openc3_scope, token: $openc3_token)
         
     | 
| 
      
 846 
     | 
    
         
            +
                  raise "Invalid comparison to non-ascii value" unless comparison_to_eval.is_printable?
         
     | 
| 
       831 
847 
     | 
    
         
             
                  string = "value " + comparison_to_eval
         
     | 
| 
       832 
848 
     | 
    
         
             
                  check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}"
         
     | 
| 
       833 
849 
     | 
    
         
             
                  # Show user the check against a quoted string
         
     | 
    
        data/lib/openc3/script/script.rb
    CHANGED
    
    | 
         @@ -178,6 +178,14 @@ module OpenC3 
     | 
|
| 
       178 
178 
     | 
    
         
             
                  print "Details: #{details}\n" if details
         
     | 
| 
       179 
179 
     | 
    
         
             
                  gets.chomp
         
     | 
| 
       180 
180 
     | 
    
         
             
                end
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                def step_mode
         
     | 
| 
      
 183 
     | 
    
         
            +
                  # NOOP
         
     | 
| 
      
 184 
     | 
    
         
            +
                end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                def run_mode
         
     | 
| 
      
 187 
     | 
    
         
            +
                  # NOOP
         
     | 
| 
      
 188 
     | 
    
         
            +
                end
         
     | 
| 
       181 
189 
     | 
    
         
             
              end
         
     | 
| 
       182 
190 
     | 
    
         | 
| 
       183 
191 
     | 
    
         
             
              # Provides a proxy to the JsonDRbObject which communicates with the API server
         
     | 
| 
         @@ -13,7 +13,7 @@ 
     | 
|
| 
       13 
13 
     | 
    
         
             
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
         
     | 
| 
       14 
14 
     | 
    
         
             
            # GNU Affero General Public License for more details.
         
     | 
| 
       15 
15 
     | 
    
         
             
            #
         
     | 
| 
       16 
     | 
    
         
            -
            # This file may also be used under the terms of a commercial license 
     | 
| 
      
 16 
     | 
    
         
            +
            # This file may also be used under the terms of a commercial license
         
     | 
| 
       17 
17 
     | 
    
         
             
            # if purchased from OpenC3, Inc.
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            module OpenC3
         
     | 
| 
         @@ -236,6 +236,5 @@ module OpenC3 
     | 
|
| 
       236 
236 
     | 
    
         
             
                    return JSON.parse(response.body, :allow_nan => true, :create_additions => true)
         
     | 
| 
       237 
237 
     | 
    
         
             
                  end
         
     | 
| 
       238 
238 
     | 
    
         
             
                end
         
     | 
| 
       239 
     | 
    
         
            -
             
     | 
| 
       240 
239 
     | 
    
         
             
              end
         
     | 
| 
       241 
240 
     | 
    
         
             
            end
         
     | 
| 
         @@ -157,7 +157,8 @@ module OpenC3 
     | 
|
| 
       157 
157 
     | 
    
         
             
                    case ENV['OPENC3_CLOUD']
         
     | 
| 
       158 
158 
     | 
    
         
             
                    when 'local'
         
     | 
| 
       159 
159 
     | 
    
         
             
                      uri = URI.parse("http://openc3-minio:9000" + url)
         
     | 
| 
       160 
     | 
    
         
            -
                     
     | 
| 
      
 160 
     | 
    
         
            +
                    when 'aws'
         
     | 
| 
      
 161 
     | 
    
         
            +
                      uri = URI.parse("https://s3.#{ENV['AWS_REGION']}.amazonaws.com" + url)
         
     | 
| 
       161 
162 
     | 
    
         
             
                    when 'gcp'
         
     | 
| 
       162 
163 
     | 
    
         
             
                      uri = URI.parse("https://storage.googleapis.com" + url)
         
     | 
| 
       163 
164 
     | 
    
         
             
                    # when 'azure'
         
     | 
    
        data/lib/openc3/script/suite.rb
    CHANGED
    
    | 
         @@ -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 
     | 
    
         
             
            require 'openc3/script/exceptions'
         
     | 
| 
         @@ -37,14 +37,12 @@ module OpenC3 
     | 
|
| 
       37 
37 
     | 
    
         
             
                # START PUBLIC API
         
     | 
| 
       38 
38 
     | 
    
         
             
                ###########################################################################
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
                #  
     | 
| 
       41 
     | 
    
         
            -
                def initialize
         
     | 
| 
       42 
     | 
    
         
            -
                  @scripts = {}
         
     | 
| 
       43 
     | 
    
         
            -
                  @plans = []
         
     | 
| 
       44 
     | 
    
         
            -
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
                # Explictly avoid creating an initialize method which forces end users to call super()
         
     | 
| 
       45 
41 
     | 
    
         | 
| 
       46 
42 
     | 
    
         
             
                # Add a group to the suite
         
     | 
| 
       47 
43 
     | 
    
         
             
                def add_group(group_class)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @scripts ||= {}
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @plans ||= []
         
     | 
| 
       48 
46 
     | 
    
         
             
                  group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
         
     | 
| 
       49 
47 
     | 
    
         
             
                  @scripts[group_class] = group_class.new unless @scripts[group_class]
         
     | 
| 
       50 
48 
     | 
    
         
             
                  @plans << [:GROUP, group_class, nil]
         
     | 
| 
         @@ -52,6 +50,8 @@ module OpenC3 
     | 
|
| 
       52 
50 
     | 
    
         | 
| 
       53 
51 
     | 
    
         
             
                # Add a script to the suite
         
     | 
| 
       54 
52 
     | 
    
         
             
                def add_script(group_class, script)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @scripts ||= {}
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @plans ||= []
         
     | 
| 
       55 
55 
     | 
    
         
             
                  group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
         
     | 
| 
       56 
56 
     | 
    
         
             
                  @scripts[group_class] = group_class.new unless @scripts[group_class]
         
     | 
| 
       57 
57 
     | 
    
         
             
                  @plans << [:SCRIPT, group_class, script]
         
     | 
| 
         @@ -59,6 +59,8 @@ module OpenC3 
     | 
|
| 
       59 
59 
     | 
    
         | 
| 
       60 
60 
     | 
    
         
             
                # Add a group setup to the suite
         
     | 
| 
       61 
61 
     | 
    
         
             
                def add_group_setup(group_class)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @scripts ||= {}
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @plans ||= []
         
     | 
| 
       62 
64 
     | 
    
         
             
                  group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
         
     | 
| 
       63 
65 
     | 
    
         
             
                  @scripts[group_class] = group_class.new unless @scripts[group_class]
         
     | 
| 
       64 
66 
     | 
    
         
             
                  @plans << [:GROUP_SETUP, group_class, nil]
         
     | 
| 
         @@ -66,6 +68,8 @@ module OpenC3 
     | 
|
| 
       66 
68 
     | 
    
         | 
| 
       67 
69 
     | 
    
         
             
                # Add a group teardown to the suite
         
     | 
| 
       68 
70 
     | 
    
         
             
                def add_group_teardown(group_class)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @scripts ||= {}
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @plans ||= []
         
     | 
| 
       69 
73 
     | 
    
         
             
                  group_class = Object.const_get(group_class.to_s.intern) unless group_class.class == Class
         
     | 
| 
       70 
74 
     | 
    
         
             
                  @scripts[group_class] = group_class.new unless @scripts[group_class]
         
     | 
| 
       71 
75 
     | 
    
         
             
                  @plans << [:GROUP_TEARDOWN, group_class, nil]
         
     | 
| 
         @@ -269,11 +273,7 @@ module OpenC3 
     | 
|
| 
       269 
273 
     | 
    
         
             
                @@abort_on_exception = false
         
     | 
| 
       270 
274 
     | 
    
         
             
                @@current_result = nil
         
     | 
| 
       271 
275 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
                 
     | 
| 
       273 
     | 
    
         
            -
                  @output_io = StringIO.new('', 'r+')
         
     | 
| 
       274 
     | 
    
         
            -
                  $stdout = Stdout.instance
         
     | 
| 
       275 
     | 
    
         
            -
                  $stderr = Stderr.instance
         
     | 
| 
       276 
     | 
    
         
            -
                end
         
     | 
| 
      
 276 
     | 
    
         
            +
                # Explictly avoid creating an initialize method which forces end users to call super()
         
     | 
| 
       277 
277 
     | 
    
         | 
| 
       278 
278 
     | 
    
         
             
                def self.abort_on_exception
         
     | 
| 
       279 
279 
     | 
    
         
             
                  @@abort_on_exception
         
     | 
| 
         @@ -350,7 +350,11 @@ module OpenC3 
     | 
|
| 
       350 
350 
     | 
    
         | 
| 
       351 
351 
     | 
    
         
             
                  # Verify script method exists
         
     | 
| 
       352 
352 
     | 
    
         
             
                  if object.class.method_defined?(method_name)
         
     | 
| 
      
 353 
     | 
    
         
            +
                    @output_io ||= StringIO.new('', 'r+')
         
     | 
| 
       353 
354 
     | 
    
         
             
                    # Capture STDOUT and STDERR
         
     | 
| 
      
 355 
     | 
    
         
            +
                    # $stdout & $stderr must be set to change output
         
     | 
| 
      
 356 
     | 
    
         
            +
                    $stdout = Stdout.instance
         
     | 
| 
      
 357 
     | 
    
         
            +
                    $stderr = Stderr.instance
         
     | 
| 
       354 
358 
     | 
    
         
             
                    $stdout.add_stream(@output_io)
         
     | 
| 
       355 
359 
     | 
    
         
             
                    $stderr.add_stream(@output_io)
         
     | 
| 
       356 
360 
     | 
    
         | 
    
        data/lib/openc3/system/system.rb
    CHANGED
    
    | 
         @@ -131,11 +131,14 @@ module OpenC3 
     | 
|
| 
       131 
131 
     | 
    
         | 
| 
       132 
132 
     | 
    
         
             
                  target = Target.new(target_name, target_config_dir)
         
     | 
| 
       133 
133 
     | 
    
         
             
                  @targets[target.name] = target
         
     | 
| 
      
 134 
     | 
    
         
            +
                  errors = [] # Store all errors processing the cmd_tlm files
         
     | 
| 
       134 
135 
     | 
    
         
             
                  target.cmd_tlm_files.each do |cmd_tlm_file|
         
     | 
| 
       135 
136 
     | 
    
         
             
                    @packet_config.process_file(cmd_tlm_file, target.name)
         
     | 
| 
       136 
     | 
    
         
            -
                  rescue Exception =>  
     | 
| 
       137 
     | 
    
         
            -
                     
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
      
 137 
     | 
    
         
            +
                  rescue Exception => error
         
     | 
| 
      
 138 
     | 
    
         
            +
                    errors << "Error processing #{cmd_tlm_file}:\n#{error.message}"
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                  unless errors.empty?
         
     | 
| 
      
 141 
     | 
    
         
            +
                    raise errors.join("\n")
         
     | 
| 
       139 
142 
     | 
    
         
             
                  end
         
     | 
| 
       140 
143 
     | 
    
         
             
                end
         
     | 
| 
       141 
144 
     | 
    
         
             
              end
         
     | 
| 
         @@ -38,7 +38,7 @@ module OpenC3 
     | 
|
| 
       38 
38 
     | 
    
         
             
                def self.receive_commands(interface, scope:)
         
     | 
| 
       39 
39 
     | 
    
         
             
                  while true
         
     | 
| 
       40 
40 
     | 
    
         
             
                    Topic.read_topics(InterfaceTopic.topics(interface, scope: scope)) do |topic, msg_id, msg_hash, redis|
         
     | 
| 
       41 
     | 
    
         
            -
                      result = yield topic, msg_hash
         
     | 
| 
      
 41 
     | 
    
         
            +
                      result = yield topic, msg_id, msg_hash, redis
         
     | 
| 
       42 
42 
     | 
    
         
             
                      ack_topic = topic.split("__")
         
     | 
| 
       43 
43 
     | 
    
         
             
                      ack_topic[1] = 'ACK' + ack_topic[1]
         
     | 
| 
       44 
44 
     | 
    
         
             
                      ack_topic = ack_topic.join("__")
         
     | 
| 
         @@ -76,5 +76,21 @@ module OpenC3 
     | 
|
| 
       76 
76 
     | 
    
         
             
                  sleep 1 # Give some time for the interface to shutdown
         
     | 
| 
       77 
77 
     | 
    
         
             
                  InterfaceTopic.clear_topics(InterfaceTopic.topics(interface, scope: scope))
         
     | 
| 
       78 
78 
     | 
    
         
             
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def interface_cmd(interface_name, cmd_name, *cmd_params, scope:)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  data = {}
         
     | 
| 
      
 82 
     | 
    
         
            +
                  data['cmd_name'] = cmd_name
         
     | 
| 
      
 83 
     | 
    
         
            +
                  data['cmd_params'] = cmd_params
         
     | 
| 
      
 84 
     | 
    
         
            +
                  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'interface_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def protocol_cmd(interface_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  data = {}
         
     | 
| 
      
 89 
     | 
    
         
            +
                  data['cmd_name'] = cmd_name
         
     | 
| 
      
 90 
     | 
    
         
            +
                  data['cmd_params'] = cmd_params
         
     | 
| 
      
 91 
     | 
    
         
            +
                  data['read_write'] = read_write.to_s.upcase
         
     | 
| 
      
 92 
     | 
    
         
            +
                  data['index'] = index
         
     | 
| 
      
 93 
     | 
    
         
            +
                  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
       79 
95 
     | 
    
         
             
              end
         
     | 
| 
       80 
96 
     | 
    
         
             
            end
         
     | 
| 
         @@ -40,7 +40,7 @@ module OpenC3 
     | 
|
| 
       40 
40 
     | 
    
         
             
                def self.receive_telemetry(router, scope:)
         
     | 
| 
       41 
41 
     | 
    
         
             
                  while true
         
     | 
| 
       42 
42 
     | 
    
         
             
                    Topic.read_topics(RouterTopic.topics(router, scope: scope)) do |topic, msg_id, msg_hash, redis|
         
     | 
| 
       43 
     | 
    
         
            -
                      result = yield topic, msg_hash
         
     | 
| 
      
 43 
     | 
    
         
            +
                      result = yield topic, msg_id, msg_hash, redis
         
     | 
| 
       44 
44 
     | 
    
         
             
                      if /CMD}ROUTER/.match?(topic)
         
     | 
| 
       45 
45 
     | 
    
         
             
                        ack_topic = topic.split("__")
         
     | 
| 
       46 
46 
     | 
    
         
             
                        ack_topic[1] = 'ACK' + ack_topic[1]
         
     | 
| 
         @@ -88,5 +88,21 @@ module OpenC3 
     | 
|
| 
       88 
88 
     | 
    
         
             
                  sleep 1 # Give some time for the interface to shutdown
         
     | 
| 
       89 
89 
     | 
    
         
             
                  RouterTopic.clear_topics(RouterTopic.topics(router, scope: scope))
         
     | 
| 
       90 
90 
     | 
    
         
             
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                def router_cmd(router_name, cmd_name, *cmd_params, scope:)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  data = {}
         
     | 
| 
      
 94 
     | 
    
         
            +
                  data['cmd_name'] = cmd_name
         
     | 
| 
      
 95 
     | 
    
         
            +
                  data['cmd_params'] = cmd_params
         
     | 
| 
      
 96 
     | 
    
         
            +
                  Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def protocol_cmd(router_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  data = {}
         
     | 
| 
      
 101 
     | 
    
         
            +
                  data['cmd_name'] = cmd_name
         
     | 
| 
      
 102 
     | 
    
         
            +
                  data['cmd_params'] = cmd_params
         
     | 
| 
      
 103 
     | 
    
         
            +
                  data['read_write'] = read_write.to_s.upcase
         
     | 
| 
      
 104 
     | 
    
         
            +
                  data['index'] = index
         
     | 
| 
      
 105 
     | 
    
         
            +
                  Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
       91 
107 
     | 
    
         
             
              end
         
     | 
| 
       92 
108 
     | 
    
         
             
            end
         
     | 
| 
         @@ -121,15 +121,20 @@ module OpenC3 
     | 
|
| 
       121 
121 
     | 
    
         
             
                  result
         
     | 
| 
       122 
122 
     | 
    
         
             
                end
         
     | 
| 
       123 
123 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
                # Lists the  
     | 
| 
       125 
     | 
    
         
            -
                def  
     | 
| 
      
 124 
     | 
    
         
            +
                # Lists the files under a specified path
         
     | 
| 
      
 125 
     | 
    
         
            +
                def list_files(bucket:, path:, only_directories: false)
         
     | 
| 
       126 
126 
     | 
    
         
             
                  # Trailing slash is important in AWS S3 when listing files
         
     | 
| 
       127 
127 
     | 
    
         
             
                  # See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Types/ListObjectsV2Output.html#common_prefixes-instance_method
         
     | 
| 
       128 
128 
     | 
    
         
             
                  if path[-1] != '/'
         
     | 
| 
       129 
129 
     | 
    
         
             
                    path += '/'
         
     | 
| 
       130 
130 
     | 
    
         
             
                  end
         
     | 
| 
      
 131 
     | 
    
         
            +
                  # If we're searching for the root then kill the path or AWS will return nothing
         
     | 
| 
      
 132 
     | 
    
         
            +
                  path = nil if path == '/'
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
       131 
134 
     | 
    
         
             
                  token = nil
         
     | 
| 
       132 
135 
     | 
    
         
             
                  result = []
         
     | 
| 
      
 136 
     | 
    
         
            +
                  dirs = []
         
     | 
| 
      
 137 
     | 
    
         
            +
                  files = []
         
     | 
| 
       133 
138 
     | 
    
         
             
                  while true
         
     | 
| 
       134 
139 
     | 
    
         
             
                    resp = @client.list_objects_v2({
         
     | 
| 
       135 
140 
     | 
    
         
             
                      bucket: bucket,
         
     | 
| 
         @@ -141,7 +146,19 @@ module OpenC3 
     | 
|
| 
       141 
146 
     | 
    
         
             
                    resp.common_prefixes.each do |item|
         
     | 
| 
       142 
147 
     | 
    
         
             
                      # If path was DEFAULT/targets_modified/ then the
         
     | 
| 
       143 
148 
     | 
    
         
             
                      # results look like DEFAULT/targets_modified/INST/
         
     | 
| 
       144 
     | 
    
         
            -
                       
     | 
| 
      
 149 
     | 
    
         
            +
                      dirs << item.prefix.split('/')[-1]
         
     | 
| 
      
 150 
     | 
    
         
            +
                    end
         
     | 
| 
      
 151 
     | 
    
         
            +
                    if only_directories
         
     | 
| 
      
 152 
     | 
    
         
            +
                      result = dirs
         
     | 
| 
      
 153 
     | 
    
         
            +
                    else
         
     | 
| 
      
 154 
     | 
    
         
            +
                      resp.contents.each do |aws_item|
         
     | 
| 
      
 155 
     | 
    
         
            +
                        item = {}
         
     | 
| 
      
 156 
     | 
    
         
            +
                        item['name'] = aws_item.key.split('/')[-1]
         
     | 
| 
      
 157 
     | 
    
         
            +
                        item['modified'] = aws_item.last_modified
         
     | 
| 
      
 158 
     | 
    
         
            +
                        item['size'] = aws_item.size
         
     | 
| 
      
 159 
     | 
    
         
            +
                        files << item
         
     | 
| 
      
 160 
     | 
    
         
            +
                      end
         
     | 
| 
      
 161 
     | 
    
         
            +
                      result = [dirs, files]
         
     | 
| 
       145 
162 
     | 
    
         
             
                    end
         
     | 
| 
       146 
163 
     | 
    
         
             
                    break unless resp.is_truncated
         
     | 
| 
       147 
164 
     | 
    
         
             
                    token = resp.next_continuation_token
         
     | 
| 
         @@ -57,7 +57,7 @@ module OpenC3 
     | 
|
| 
       57 
57 
     | 
    
         
             
                  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
         
     | 
| 
       58 
58 
     | 
    
         
             
                end
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                def  
     | 
| 
      
 60 
     | 
    
         
            +
                def list_files(bucket:, path:, only_directories: false)
         
     | 
| 
       61 
61 
     | 
    
         
             
                  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
         
     | 
| 
       62 
62 
     | 
    
         
             
                end
         
     | 
| 
       63 
63 
     | 
    
         | 
| 
         @@ -73,7 +73,7 @@ class BucketFile 
     | 
|
| 
       73 
73 
     | 
    
         
             
                  local_path = "#{BucketFileCache.instance.cache_dir}/#{File.basename(@bucket_path)}"
         
     | 
| 
       74 
74 
     | 
    
         
             
                  unless File.exist?(local_path)
         
     | 
| 
       75 
75 
     | 
    
         
             
                    OpenC3::Logger.debug "Retrieving #{@bucket_path} from logs bucket"
         
     | 
| 
       76 
     | 
    
         
            -
                    client.get_object(bucket:  
     | 
| 
      
 76 
     | 
    
         
            +
                    client.get_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: @bucket_path, path: local_path)
         
     | 
| 
       77 
77 
     | 
    
         
             
                    if File.exist?(local_path)
         
     | 
| 
       78 
78 
     | 
    
         
             
                      basename = File.basename(local_path)
         
     | 
| 
       79 
79 
     | 
    
         
             
                      if uncompress and File.extname(basename) == ".gz"
         
     | 
| 
         @@ -46,7 +46,7 @@ module OpenC3 
     | 
|
| 
       46 
46 
     | 
    
         
             
                    return oldest_list
         
     | 
| 
       47 
47 
     | 
    
         
             
                  end
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                  directories = client. 
     | 
| 
      
 49 
     | 
    
         
            +
                  directories = client.list_files(bucket: bucket, path: prefix, only_directories: true)
         
     | 
| 
       50 
50 
     | 
    
         
             
                  filtered_directories = filter_directories_to_time_range(directories, start_time, end_time)
         
     | 
| 
       51 
51 
     | 
    
         
             
                  filtered_directories.each do |directory|
         
     | 
| 
       52 
52 
     | 
    
         
             
                    directory_files = client.list_objects(bucket: bucket, prefix: "#{prefix}/#{directory}", max_request: max_request, max_total: max_total)
         
     |