Class: OpenC3::Interface

Inherits:
Object show all
Includes:
Api
Defined in:
lib/openc3/interfaces/interface.rb

Overview

Defines all the attributes and methods common to all interface classes used by OpenC3.

Constant Summary

Constants included from Api

Api::DELAY_METRICS, Api::DURATION_METRICS, Api::SUBSCRIPTION_DELIMITER, Api::SUM_METRICS

Constants included from ApiShared

ApiShared::DEFAULT_TLM_POLLING_RATE

Constants included from Extract

Extract::SCANNING_REGULAR_EXPRESSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Api

#_cmd_implementation, #_extract_target_command_names, #_extract_target_command_parameter_names, #_extract_target_packet_item_names, #_extract_target_packet_names, #_get_and_set_cmd, #_get_item, #_limits_group, #_set_tlm_process_args, #_tlm_process_args, #_validate_tlm_type, #build_cmd, #cmd, #cmd_no_checks, #cmd_no_hazardous_check, #cmd_no_range_check, #cmd_raw, #cmd_raw_no_checks, #cmd_raw_no_hazardous_check, #cmd_raw_no_range_check, #config_tool_names, #connect_interface, #connect_router, #delete_config, #disable_cmd, #disable_limits, #disable_limits_group, #disconnect_interface, #disconnect_router, #enable_cmd, #enable_limits, #enable_limits_group, #get_all_cmd_names, #get_all_cmds, #get_all_interface_info, #get_all_router_info, #get_all_settings, #get_all_tlm, #get_all_tlm_item_names, #get_all_tlm_names, #get_cmd, #get_cmd_buffer, #get_cmd_cnt, #get_cmd_cnts, #get_cmd_hazardous, #get_cmd_time, #get_cmd_value, #get_interface, #get_interface_names, #get_item, #get_limits, #get_limits_events, #get_limits_groups, #get_limits_set, #get_limits_sets, #get_metrics, #get_out_of_limits, #get_overall_limits_state, #get_overrides, #get_packet_derived_items, #get_packets, #get_param, #get_router, #get_router_names, #get_setting, #get_settings, #get_target, #get_target_interfaces, #get_target_names, #get_tlm, #get_tlm_available, #get_tlm_buffer, #get_tlm_cnt, #get_tlm_cnts, #get_tlm_packet, #get_tlm_values, #inject_tlm, #interface_details, #interface_protocol_cmd, #interface_target_disable, #interface_target_enable, #limits_enabled?, #list_configs, #list_settings, #load_config, #map_target_to_interface, #map_target_to_router, #normalize_tlm, #offline_access_needed, #override_tlm, #router_cmd, #router_details, #router_protocol_cmd, #router_target_disable, #router_target_enable, #save_config, #send_raw, #set_limits, #set_limits_set, #set_offline_access, #set_setting, #set_tlm, #start_raw_logging_interface, #start_raw_logging_router, #stash_all, #stash_delete, #stash_get, #stash_keys, #stash_set, #stop_raw_logging_interface, #stop_raw_logging_router, #subscribe_packets, #tlm, #tlm_formatted, #tlm_raw, #tlm_variable, #tlm_with_units, #unmap_target_from_interface, #unmap_target_from_router, #update_news, #update_plugin_store

Methods included from CmdLog

#_build_cmd_output_string

Constructor Details

#initializeInterface

Initialize default attribute values



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/openc3/interfaces/interface.rb', line 149

def initialize
  @name = self.class.to_s.split("::")[-1] # Remove namespacing if present
  @state = 'DISCONNECTED'
  @target_names = []
  @cmd_target_names = []
  @tlm_target_names = []
  @cmd_target_enabled = {}
  @tlm_target_enabled = {}
  @connect_on_startup = true
  @auto_reconnect = true
  @reconnect_delay = 5.0
  @disable_disconnect = false
  @stream_log_pair = nil
  @routers = []
  @read_count = 0
  @write_count = 0
  @bytes_read = 0
  @bytes_written = 0
  @num_clients = 0
  @read_queue_size = 0
  @write_queue_size = 0
  @write_mutex = Mutex.new
  @read_allowed = true
  @write_allowed = true
  @write_raw_allowed = true
  @options = {}
  @read_protocols = []
  @write_protocols = []
  @protocol_info = []
  @save_raw_data = true
  @read_raw_data = ''
  @written_raw_data = ''
  @read_raw_data_time = nil
  @written_raw_data_time = nil
  @config_params = []
  @interfaces = []
  @secrets = Secrets.getClient
  @scheduler = nil
end

Instance Attribute Details

#auto_reconnectBoolean

Returns Flag indicating if the interface should automatically reconnect after losing connection.

Returns:

  • (Boolean)

    Flag indicating if the interface should automatically reconnect after losing connection



68
69
70
# File 'lib/openc3/interfaces/interface.rb', line 68

def auto_reconnect
  @auto_reconnect
end

#bytes_readInteger

Returns The number of bytes read from this interface.

Returns:

  • (Integer)

    The number of bytes read from this interface



91
92
93
# File 'lib/openc3/interfaces/interface.rb', line 91

def bytes_read
  @bytes_read
end

#bytes_writtenInteger

Returns The number of bytes written to this interface.

Returns:

  • (Integer)

    The number of bytes written to this interface



94
95
96
# File 'lib/openc3/interfaces/interface.rb', line 94

def bytes_written
  @bytes_written
end

#cmd_target_enabledHash<String, Boolean>

Disabled cmd targets will ignore all commands sent to the interface microservice for that target

Returns:

  • (Hash<String, Boolean>)

    Hash of enabled state for each cmd_target



56
57
58
# File 'lib/openc3/interfaces/interface.rb', line 56

def cmd_target_enabled
  @cmd_target_enabled
end

#cmd_target_namesArray<String>

Returns Array of cmd target names associated with this interface.

Returns:

  • (Array<String>)

    Array of cmd target names associated with this interface



49
50
51
# File 'lib/openc3/interfaces/interface.rb', line 49

def cmd_target_names
  @cmd_target_names
end

#config_paramsArray

Returns Config params from the INTERFACE config line.

Returns:

  • (Array)

    Config params from the INTERFACE config line



136
137
138
# File 'lib/openc3/interfaces/interface.rb', line 136

def config_params
  @config_params
end

#connect_on_startupBoolean

Returns Flag indicating if the interface should be connected to on startup.

Returns:

  • (Boolean)

    Flag indicating if the interface should be connected to on startup



64
65
66
# File 'lib/openc3/interfaces/interface.rb', line 64

def connect_on_startup
  @connect_on_startup
end

#disable_disconnectBoolean

Returns Flag indicating if the user is allowed to disconnect this interface.

Returns:

  • (Boolean)

    Flag indicating if the user is allowed to disconnect this interface



75
76
77
# File 'lib/openc3/interfaces/interface.rb', line 75

def disable_disconnect
  @disable_disconnect
end

#interfacesArray<Interface>

Returns Array of interfaces to route packets to (when used as a BridgeRouter).

Returns:

  • (Array<Interface>)

    Array of interfaces to route packets to (when used as a BridgeRouter)



140
141
142
# File 'lib/openc3/interfaces/interface.rb', line 140

def interfaces
  @interfaces
end

#nameString

Returns Name of the interface.

Returns:

  • (String)

    Name of the interface



40
41
42
# File 'lib/openc3/interfaces/interface.rb', line 40

def name
  @name
end

#num_clientsInteger

Returns The number of active clients (when used as a Router).

Returns:

  • (Integer)

    The number of active clients (when used as a Router)



98
99
100
# File 'lib/openc3/interfaces/interface.rb', line 98

def num_clients
  @num_clients
end

#optionsHash<option name, option values>

Returns Hash of options supplied to interface/router.

Returns:

  • (Hash<option name, option values>)

    Hash of options supplied to interface/router



109
110
111
# File 'lib/openc3/interfaces/interface.rb', line 109

def options
  @options
end

#protocol_infoArray<[Protocol Class, Protocol Args, Protocol kind (:READ, :WRITE, :READ_WRITE)>] Info to recreate protocols

Returns Array<[Protocol Class, Protocol Args, Protocol kind (:READ, :WRITE, :READ_WRITE)>] Info to recreate protocols.

Returns:

  • (Array<[Protocol Class, Protocol Args, Protocol kind (:READ, :WRITE, :READ_WRITE)>] Info to recreate protocols)

    Array<[Protocol Class, Protocol Args, Protocol kind (:READ, :WRITE, :READ_WRITE)>] Info to recreate protocols



118
119
120
# File 'lib/openc3/interfaces/interface.rb', line 118

def protocol_info
  @protocol_info
end

#read_countInteger

Returns The number of packets read from this interface.

Returns:

  • (Integer)

    The number of packets read from this interface



85
86
87
# File 'lib/openc3/interfaces/interface.rb', line 85

def read_count
  @read_count
end

#read_protocolsArray<Protocol>

Returns Array of protocols for reading.

Returns:



112
113
114
# File 'lib/openc3/interfaces/interface.rb', line 112

def read_protocols
  @read_protocols
end

#read_queue_sizeInteger

Returns The number of packets in the read queue (when used as a Router).

Returns:

  • (Integer)

    The number of packets in the read queue (when used as a Router)



102
103
104
# File 'lib/openc3/interfaces/interface.rb', line 102

def read_queue_size
  @read_queue_size
end

#read_raw_dataString

Returns Most recently read raw data.

Returns:

  • (String)

    Most recently read raw data



124
125
126
# File 'lib/openc3/interfaces/interface.rb', line 124

def read_raw_data
  @read_raw_data
end

#read_raw_data_timeTime

Returns Most recent read raw data time.

Returns:

  • (Time)

    Most recent read raw data time



130
131
132
# File 'lib/openc3/interfaces/interface.rb', line 130

def read_raw_data_time
  @read_raw_data_time
end

#reconnect_delayInteger[ Delay between reconnect attempts

Returns Integer[ Delay between reconnect attempts.

Returns:

  • (Integer[ Delay between reconnect attempts)

    Integer[ Delay between reconnect attempts



71
72
73
# File 'lib/openc3/interfaces/interface.rb', line 71

def reconnect_delay
  @reconnect_delay
end

#routersArray<Routers>

Returns Array of routers that receive packets read from the interface (used by Bridge).

Returns:

  • (Array<Routers>)

    Array of routers that receive packets read from the interface (used by Bridge)



82
83
84
# File 'lib/openc3/interfaces/interface.rb', line 82

def routers
  @routers
end

#save_raw_dataBoolean

Returns Whether to save raw data in the interface and protocols.

Returns:

  • (Boolean)

    Whether to save raw data in the interface and protocols



121
122
123
# File 'lib/openc3/interfaces/interface.rb', line 121

def save_raw_data
  @save_raw_data
end

#schedulerScheduler

Returns Scheduler used for periodic commanding.

Returns:

  • (Scheduler)

    Scheduler used for periodic commanding



146
147
148
# File 'lib/openc3/interfaces/interface.rb', line 146

def scheduler
  @scheduler
end

#secretsSecrets

Returns Interface secrets manager class.

Returns:

  • (Secrets)

    Interface secrets manager class



143
144
145
# File 'lib/openc3/interfaces/interface.rb', line 143

def secrets
  @secrets
end

#stateString

Returns State of the interface: CONNECTED, ATTEMPTING, DISCONNECTED.

Returns:

  • (String)

    State of the interface: CONNECTED, ATTEMPTING, DISCONNECTED



43
44
45
# File 'lib/openc3/interfaces/interface.rb', line 43

def state
  @state
end

#stream_log_pairStreamLogPair

Returns StreamLogPair instance or nil.

Returns:



78
79
80
# File 'lib/openc3/interfaces/interface.rb', line 78

def stream_log_pair
  @stream_log_pair
end

#target_namesArray<String>

Returns Array of target names associated with this interface.

Returns:

  • (Array<String>)

    Array of target names associated with this interface



46
47
48
# File 'lib/openc3/interfaces/interface.rb', line 46

def target_names
  @target_names
end

#tlm_target_enabledHash<String, Boolean>

Disabled tlm targets will not output telemetry from the interface microservice for that target

Returns:

  • (Hash<String, Boolean>)

    Array of tlm target names associated with this interface



60
61
62
# File 'lib/openc3/interfaces/interface.rb', line 60

def tlm_target_enabled
  @tlm_target_enabled
end

#tlm_target_namesArray<String>

Returns Array of tlm target names associated with this interface.

Returns:

  • (Array<String>)

    Array of tlm target names associated with this interface



52
53
54
# File 'lib/openc3/interfaces/interface.rb', line 52

def tlm_target_names
  @tlm_target_names
end

#write_countInteger

Returns The number of packets written to this interface.

Returns:

  • (Integer)

    The number of packets written to this interface



88
89
90
# File 'lib/openc3/interfaces/interface.rb', line 88

def write_count
  @write_count
end

#write_protocolsArray<Protocol>

Returns Array of protocols for writing.

Returns:



115
116
117
# File 'lib/openc3/interfaces/interface.rb', line 115

def write_protocols
  @write_protocols
end

#write_queue_sizeInteger

Returns The number of packets in the write queue (when used as a Router).

Returns:

  • (Integer)

    The number of packets in the write queue (when used as a Router)



106
107
108
# File 'lib/openc3/interfaces/interface.rb', line 106

def write_queue_size
  @write_queue_size
end

#written_raw_dataString

Returns Most recently written raw data.

Returns:

  • (String)

    Most recently written raw data



127
128
129
# File 'lib/openc3/interfaces/interface.rb', line 127

def written_raw_data
  @written_raw_data
end

#written_raw_data_timeTime

Returns Most recent written raw data time.

Returns:

  • (Time)

    Most recent written raw data time



133
134
135
# File 'lib/openc3/interfaces/interface.rb', line 133

def written_raw_data_time
  @written_raw_data_time
end

Instance Method Details

#_writeObject

Wrap all writes in a mutex and handle errors



426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/openc3/interfaces/interface.rb', line 426

def _write
  if @write_mutex.owned?
    yield
  else
    @write_mutex.synchronize { yield }
  end
rescue WriteRejectError => e
  Logger.error("#{@name}: Write rejected by interface: #{e.message}")
  raise e
rescue Exception => e
  Logger.error("#{@name}: Error writing to interface")
  disconnect()
  raise e
end

#add_protocol(protocol_class, protocol_args, read_write) ⇒ Object



598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
# File 'lib/openc3/interfaces/interface.rb', line 598

def add_protocol(protocol_class, protocol_args, read_write)
  protocol_args = protocol_args.clone
  protocol = protocol_class.new(*protocol_args)
  case read_write
  when :READ
    @read_protocols << protocol
  when :WRITE
    @write_protocols.unshift(protocol)
  when :READ_WRITE, :PARAMS
    @read_protocols << protocol
    @write_protocols.unshift(protocol)
  else
    raise "Unknown protocol descriptor: #{read_write}. Must be :READ, :WRITE, or :READ_WRITE."
  end
  @protocol_info << [protocol_class, protocol_args, read_write]
  protocol.interface = self
  return protocol
end

#as_json(*_a) ⇒ Object



441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/openc3/interfaces/interface.rb', line 441

def as_json(*_a)
  config = {}
  config['name'] = @name
  config['state'] = @state
  config['clients'] = self.num_clients
  config['txsize'] = @write_queue_size
  config['rxsize'] = @read_queue_size
  config['txbytes'] = @bytes_written
  config['rxbytes'] = @bytes_read
  config['txcnt'] = @write_count
  config['rxcnt'] = @read_count
  config
end

#connectObject

Connects the interface to its target(s). Must be implemented by a subclass.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/openc3/interfaces/interface.rb', line 197

def connect
  (@read_protocols | @write_protocols).each { |protocol| protocol.connect_reset }

  periodic_cmds = @options['PERIODIC_CMD']
  if periodic_cmds
    if not @scheduler
      @scheduler = Rufus::Scheduler.new

      periodic_cmds.each do |log_dont_log, period, cmd_string|
        log_dont_log.upcase!
        period = "#{period.to_f}s"
        @scheduler.every period do
          if connected?()
            begin
              if log_dont_log == 'DONT_LOG'
                cmd(cmd_string, log_message: false)
              else
                cmd(cmd_string)
              end
            rescue Exception => e
              Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{e.formatted}")
            end
          end
        end
      end
    else
      @scheduler.resume
    end
  end
end

#connected?Boolean

Indicates if the interface is connected to its target(s) or not. Must be implemented by a subclass.

Returns:

  • (Boolean)


245
246
247
# File 'lib/openc3/interfaces/interface.rb', line 245

def connected?
  raise "connected? not defined by Interface"
end

#connection_stringObject

Should be implemented by subclass to return human readable connection string which will be placed in log messages when connecting and during connection failures



191
192
193
# File 'lib/openc3/interfaces/interface.rb', line 191

def connection_string
  return @name
end

#convert_data_to_packet(data, extra = nil) ⇒ Packet

Called to convert the read data into a OpenC3 Packet object

Parameters:

  • data (String)

    Raw packet data

Returns:

  • (Packet)

    OpenC3 Packet with buffer filled with data



552
553
554
555
556
# File 'lib/openc3/interfaces/interface.rb', line 552

def convert_data_to_packet(data, extra = nil)
  packet = Packet.new(nil, nil, :BIG_ENDIAN, nil, data)
  packet.extra = extra
  return packet
end

#convert_packet_to_data(packet) ⇒ Object

Called to convert a packet into the data to send

Parameters:

  • packet (Packet)

    Packet to extract data from

Returns:

  • data



562
563
564
# File 'lib/openc3/interfaces/interface.rb', line 562

def convert_packet_to_data(packet)
  return packet.buffer(true), packet.extra # Copy buffer so logged command isn't modified
end

#copy_to(other_interface) ⇒ Object

Copy settings from this interface to another interface. All instance variables are copied except for num_clients, read_queue_size, and write_queue_size since these are all specific to the operation of the interface rather than its instantiation.

Parameters:

  • other_interface (Interface)

    The other interface to copy to



493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/openc3/interfaces/interface.rb', line 493

def copy_to(other_interface)
  other_interface.name = self.name.clone
  other_interface.target_names = self.target_names.clone
  other_interface.cmd_target_names = self.cmd_target_names.clone
  other_interface.tlm_target_names = self.tlm_target_names.clone
  other_interface.cmd_target_enabled = self.cmd_target_enabled.clone
  other_interface.tlm_target_enabled = self.tlm_target_enabled.clone
  other_interface.connect_on_startup = self.connect_on_startup
  other_interface.auto_reconnect = self.auto_reconnect
  other_interface.reconnect_delay = self.reconnect_delay
  other_interface.disable_disconnect = self.disable_disconnect
  other_interface.routers = self.routers.clone
  other_interface.read_count = self.read_count
  other_interface.write_count = self.write_count
  other_interface.bytes_read = self.bytes_read
  other_interface.bytes_written = self.bytes_written
  other_interface.stream_log_pair = self.stream_log_pair.clone if @stream_log_pair
  # num_clients is per interface so don't copy
  # read_queue_size is the number of packets in the queue so don't copy
  # write_queue_size is the number of packets in the queue so don't copy
  self.options.each do |option_name, option_values|
    if option_values and Array === option_values[0]
      # Properly Handle option that supports multiple instances
      option_values.each do |ovs|
        other_interface.set_option(option_name, ovs)
      end
    else
      other_interface.set_option(option_name, option_values)
    end
  end
  other_interface.protocol_info = []
  self.protocol_info.each do |protocol_class, protocol_args, read_write|
    unless read_write == :PARAMS
      other_interface.add_protocol(protocol_class, protocol_args, read_write)
    end
  end
end

#detailsObject



674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
# File 'lib/openc3/interfaces/interface.rb', line 674

def details
  result = as_json()
  result['cmd_target_names'] = @cmd_target_names
  result['tlm_target_names'] = @tlm_target_names
  result['cmd_target_enabled'] = @cmd_target_enabled
  result['tlm_target_enabled'] = @tlm_target_enabled
  result['connect_on_startup'] = @connect_on_startup
  result['auto_reconnect'] = @auto_reconnect
  result['reconnect_delay'] = @reconnect_delay
  result['disable_disconnect'] = @disable_disconnect
  result['read_allowed'] = @read_allowed
  result['write_allowed'] = @write_allowed
  result['write_raw_allowed'] = @write_raw_allowed
  result['read_raw_data'] = @read_raw_data
  result['written_raw_data'] = @written_raw_data
  if @read_raw_data_time
    result['read_raw_data_time'] = @read_raw_data_time.iso8601
  else
    result['read_raw_data_time'] = nil
  end
  if @written_raw_data_time
    result['written_raw_data_time'] = @written_raw_data_time.iso8601
  else
    result['written_raw_data_time'] = nil
  end

  if @stream_log_pair and (@stream_log_pair.write_log.logging_enabled or @stream_log_pair.read_log.logging_enabled)
    result['stream_log'] = true
  else
    result['stream_log'] = false
  end

  result['options'] = @options
  result['read_protocols'] = []
  @read_protocols.each do |read_protocol|
    result['read_protocols'] << read_protocol.read_details
  end
  result['write_protocols'] = []
  @write_protocols.each do |write_protocol|
    result['write_protocols'] << write_protocol.write_details
  end

  return result
end

#disconnectObject

Disconnects the interface from its target(s). Must be implemented by a subclass.



251
252
253
254
255
256
257
258
# File 'lib/openc3/interfaces/interface.rb', line 251

def disconnect
  periodic_cmds = @options['PERIODIC_CMD']
  if periodic_cmds and @scheduler
    @scheduler.pause
  end

  (@read_protocols | @write_protocols).each { |protocol| protocol.disconnect_reset }
end

#interface_cmd(cmd_name, *_cmd_args) ⇒ Object



617
618
619
620
621
622
623
624
625
626
# File 'lib/openc3/interfaces/interface.rb', line 617

def interface_cmd(cmd_name, *_cmd_args)
  if cmd_name == 'clear_counters'
    @write_queue_size = 0
    @read_queue_size = 0
    @bytes_written = 0
    @bytes_read = 0
    @write_count = 0
    @read_count = 0
  end
end

#post_connectObject

Called immediately after the interface is connected. By default this method will run any commands specified by the CONNECT_CMD option



230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/openc3/interfaces/interface.rb', line 230

def post_connect
  connect_cmds = @options['CONNECT_CMD']
  if connect_cmds
    connect_cmds.each do |log_dont_log, cmd_string|
      if log_dont_log.upcase == 'DONT_LOG'
        cmd(cmd_string, log_message: false)
      else
        cmd(cmd_string)
      end
    end
  end
end

#protocol_cmd(cmd_name, *cmd_args, read_write: :READ_WRITE, index: -1)) ⇒ Object



628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
# File 'lib/openc3/interfaces/interface.rb', line 628

def protocol_cmd(cmd_name, *cmd_args, read_write: :READ_WRITE, index: -1)
  read_write = read_write.to_s.upcase.intern
  raise "Unknown protocol descriptor: #{read_write}. Must be :READ, :WRITE, or :READ_WRITE." unless [:READ, :WRITE, :READ_WRITE].include?(read_write)
  handled = false

  if index >= 0 or read_write == :READ_WRITE
    # Reconstruct full list of protocols in correct order
    protocols = []
    read_protocols = @read_protocols
    write_protocols = @write_protocols.reverse
    read_index = 0
    write_index = 0
    @protocol_info.each do |_protocol_class, _protocol_args, protocol_read_write|
      case protocol_read_write
      when :READ
        protocols << read_protocols[read_index]
        read_index += 1
      when :WRITE
        protocols << write_protocols[write_index]
        write_index += 1
      when :READ_WRITE, :PARAMS
        protocols << read_protocols[read_index]
        read_index += 1
        write_index += 1
      end
    end

    protocols.each_with_index do |protocol, protocol_index|
      # If index is given that is all that matters
      result = protocol.protocol_cmd(cmd_name, *cmd_args) if index == protocol_index or index == -1
      handled = true if result
    end
  elsif read_write == :READ # and index == -1
    @read_protocols.each do |protocol|
      result = protocol.protocol_cmd(cmd_name, *cmd_args)
      handled = true if result
    end
  else # read_write == :WRITE and index == -1
    @write_protocols.each do |protocol|
      result = protocol.protocol_cmd(cmd_name, *cmd_args)
      handled = true if result
    end
  end
  return handled
end

#readPacket

Retrieves the next packet from the interface.

Returns:

  • (Packet)

    Packet constructed from the data. Packet will be unidentified (nil target and packet names)



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/openc3/interfaces/interface.rb', line 271

def read
  raise "Interface not connected for read: #{@name}" unless connected?
  raise "Interface not readable: #{@name}" unless read_allowed?

  first = true
  loop do
    # Protocols may have cached data for a packet, so initially just inject a blank string
    # Otherwise we can hold off outputting other packets where all the data has already
    # been received
    extra = nil
    blank_test = false
    if !first or @read_protocols.length <= 0
      # Read data for a packet
      data, extra = read_interface()
      unless data
        Logger.info("#{@name}: read_interface requested disconnect")
        return nil
      end
    else
      data = ''
      first = false
      blank_test = true
    end

    @read_protocols.each do |protocol|
      # Extra check is for backwards compatibility
      protocol.read_protocol_input_base(data, extra) unless blank_test
      if extra
        data, extra = protocol.read_data(data, extra)
      else
        data, extra = protocol.read_data(data)
      end
      protocol.read_protocol_output_base(data, extra) unless blank_test
      if data == :DISCONNECT
        Logger.info("#{@name}: Protocol #{protocol.class} read_data requested disconnect")
        return nil
      end
      break if data == :STOP
      if blank_test
        # This means the blank test returned something so we can log
        protocol.read_protocol_input_base('', nil)
        protocol.read_protocol_output_base(data, extra)
      end
    end
    next if data == :STOP

    # Extra check is for backwards compatibility
    if extra
      packet = convert_data_to_packet(data, extra)
    else
      packet = convert_data_to_packet(data)
    end

    # Potentially modify packet
    @read_protocols.each do |protocol|
      packet = protocol.read_packet(packet)
      if packet == :DISCONNECT
        Logger.info("#{@name}: Protocol #{protocol.class} read_packet requested disconnect")
        return nil
      end
      break if packet == :STOP
    end
    next if packet == :STOP

    # Return packet
    @read_count += 1
    Logger.warn("#{@name}: Interface unexpectedly requested disconnect") unless packet
    return packet
  end
rescue Exception => e
  Logger.error("#{@name}: Error reading from interface")
  disconnect()
  raise e
end

#read_allowed?Boolean

Returns Whether reading is allowed.

Returns:

  • (Boolean)

    Whether reading is allowed



456
457
458
# File 'lib/openc3/interfaces/interface.rb', line 456

def read_allowed?
  @read_allowed
end

#read_interfaceObject



260
261
262
# File 'lib/openc3/interfaces/interface.rb', line 260

def read_interface
  raise "read_interface not defined by Interface"
end

#read_interface_base(data, _extra = nil) ⇒ String

Called to read data and manipulate it until enough data is returned. The definition of ‘enough data’ changes depending on the protocol used which is why this method exists. This method is also used to perform operations on the data before it can be interpreted as packet data such as decryption. After this method is called the post_read_data method is called. Subclasses must implement this method.

Returns:

  • (String)

    Raw packet data



574
575
576
577
578
579
580
581
# File 'lib/openc3/interfaces/interface.rb', line 574

def read_interface_base(data, _extra = nil)
  if @save_raw_data
    @read_raw_data_time = Time.now
    @read_raw_data = data.clone
  end
  @bytes_read += data.length
  @stream_log_pair.read_log.write(data) if @stream_log_pair
end

#set_option(option_name, option_values) ⇒ Object

Set an interface or router specific option

Parameters:

  • option_name

    name of the option

  • option_values

    array of option values



534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/openc3/interfaces/interface.rb', line 534

def set_option(option_name, option_values)
  option_name_upcase = option_name.upcase

  # CONNECT_CMD and PERIODIC_CMD are special because there could be more than 1
  # so we store them in an array for processing during connect()
  if option_name_upcase == 'PERIODIC_CMD' or option_name_upcase == 'CONNECT_CMD'
    # OPTION PERIODIC_CMD LOG/DONT_LOG 1.0 "INST COLLECT with TYPE NORMAL"
    @options[option_name_upcase] ||= []
    @options[option_name_upcase] << option_values.clone
  else
    @options[option_name_upcase] = option_values.clone
  end
end

#start_raw_loggingObject

Start raw logging for this interface



471
472
473
474
# File 'lib/openc3/interfaces/interface.rb', line 471

def start_raw_logging
  @stream_log_pair = StreamLogPair.new(@name) unless @stream_log_pair
  @stream_log_pair.start
end

#stop_raw_loggingObject

Stop raw logging for this interface



477
478
479
# File 'lib/openc3/interfaces/interface.rb', line 477

def stop_raw_logging
  @stream_log_pair.stop if @stream_log_pair
end

#write(packet) ⇒ Object

Method to send a packet on the interface.

Parameters:

  • packet (Packet)

    The Packet to send out the interface



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/openc3/interfaces/interface.rb', line 348

def write(packet)
  raise "Interface not connected for write: #{@name}" unless connected?
  raise "Interface not writable: #{@name}" unless write_allowed?

  _write do
    @write_count += 1

    # Potentially modify packet
    @write_protocols.each do |protocol|
      packet = protocol.write_packet(packet)
      if packet == :DISCONNECT
        Logger.info("#{@name}: Protocol #{protocol.class} write_packet requested disconnect")
        disconnect()
        return
      end
      return if packet == :STOP
    end

    data, extra = convert_packet_to_data(packet)

    # Potentially modify packet data
    @write_protocols.each do |protocol|
      # Extra check is for backwards compatibility
      protocol.write_protocol_input_base(data, extra)
      if extra
        data, extra = protocol.write_data(data, extra)
      else
        data, extra = protocol.write_data(data)
      end
      protocol.write_protocol_output_base(data, extra)
      if data == :DISCONNECT
        Logger.info("#{@name}: Protocol #{protocol.class} write_data requested disconnect")
        disconnect()
        return
      end
      return if data == :STOP
    end

    # Actually write out data if not handled by protocol
    # Extra check is for backwards compatibility
    if extra
      write_interface(data, extra)
    else
      write_interface(data)
    end

    # Potentially block and wait for response
    @write_protocols.each do |protocol|
      if extra
        packet, data, extra = protocol.post_write_interface(packet, data, extra)
      else
        packet, data, extra = protocol.post_write_interface(packet, data)
      end
      if packet == :DISCONNECT
        Logger.info("#{@name}: Protocol #{protocol.class} post_write_packet requested disconnect")
        disconnect()
        return
      end
      return if packet == :STOP
    end
  end

  return nil
end

#write_allowed?Boolean

Returns Whether writing is allowed.

Returns:

  • (Boolean)

    Whether writing is allowed



461
462
463
# File 'lib/openc3/interfaces/interface.rb', line 461

def write_allowed?
  @write_allowed
end

#write_interface(_data, _extra = nil) ⇒ Object



264
265
266
# File 'lib/openc3/interfaces/interface.rb', line 264

def write_interface(_data, _extra = nil)
  raise "write_interface not defined by Interface"
end

#write_interface_base(data, _extra = nil) ⇒ String

Called to write data to the underlying interface. Subclasses must implement this method and call super to count the raw bytes and allow raw logging.

Parameters:

  • data (String)

    Raw packet data

Returns:

  • (String)

    The exact data written



589
590
591
592
593
594
595
596
# File 'lib/openc3/interfaces/interface.rb', line 589

def write_interface_base(data, _extra = nil)
  if @save_raw_data
    @written_raw_data_time = Time.now
    @written_raw_data = data.clone
  end
  @bytes_written += data.length
  @stream_log_pair.write_log.write(data) if @stream_log_pair
end

#write_raw(data, extra = nil) ⇒ Object

Writes preformatted data onto the interface. Malformed data may cause problems.

Parameters:

  • data (String)

    The raw data to send out the interface



416
417
418
419
420
421
422
423
# File 'lib/openc3/interfaces/interface.rb', line 416

def write_raw(data, extra = nil)
  raise "Interface not connected for write_raw: #{@name}" unless connected?
  raise "Interface not write-rawable: #{@name}" unless write_raw_allowed?

  _write do
    write_interface(data, extra)
  end
end

#write_raw_allowed?Boolean

Returns Whether writing raw data over the interface is allowed.

Returns:

  • (Boolean)

    Whether writing raw data over the interface is allowed



466
467
468
# File 'lib/openc3/interfaces/interface.rb', line 466

def write_raw_allowed?
  @write_raw_allowed
end