Class: OpenC3::FixedProtocol

Inherits:
BurstProtocol show all
Defined in:
lib/openc3/interfaces/protocols/fixed_protocol.rb

Overview

Delineates packets by identifying them and then reading out their entire fixed length. Packets lengths can vary but they must all be fixed.

Instance Attribute Summary

Attributes inherited from Protocol

#allow_empty_data, #extra, #interface

Instance Method Summary collapse

Methods inherited from BurstProtocol

#handle_sync_pattern, #log_discard, #read_data, #reset, #write_data, #write_packet

Methods inherited from Protocol

#connect_reset, #disconnect_reset, #post_write_interface, #protocol_cmd, #read_data, #read_protocol_input_base, #read_protocol_output_base, #reset, #write_data, #write_packet, #write_protocol_input_base, #write_protocol_output_base

Constructor Details

#initialize(min_id_size, discard_leading_bytes = 0, sync_pattern = nil, telemetry = true, fill_fields = false, unknown_raise = false, allow_empty_data = nil) ⇒ FixedProtocol

Returns a new instance of FixedProtocol.

Parameters:

  • min_id_size (Integer)

    The minimum amount of data needed to identify a packet.

  • telemetry (Boolean) (defaults to: true)

    Whether the interface is returning telemetry (true) or commands (false)

  • unknown_raise (defaults to: false)

    Whether to raise an exception on an unknown packet

  • allow_empty_data (true/false/nil) (defaults to: nil)

    See Protocol#initialize

  • discard_leading_bytes (Integer) (defaults to: 0)

    The number of bytes to discard from the binary data after reading. Note that this is often used to remove a sync pattern from the final packet data.

  • sync_pattern (String) (defaults to: nil)

    String representing a hex number ("0x1234") that will be searched for in the raw data. Bytes encountered before this pattern is found are discarded.

  • fill_fields (Boolean) (defaults to: false)

    Fill any required fields when writing packets



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 40

def initialize(
  min_id_size,
  discard_leading_bytes = 0,
  sync_pattern = nil,
  telemetry = true,
  fill_fields = false,
  unknown_raise = false,
  allow_empty_data = nil
)
  super(discard_leading_bytes, sync_pattern, fill_fields, allow_empty_data)
  @min_id_size = Integer(min_id_size)
  @telemetry = telemetry
  @unknown_raise = ConfigParser.handle_true_false(unknown_raise)
  @received_time = nil
  @target_name = nil
  @packet_name = nil
end

Instance Method Details

#identify_and_finish_packetString|Symbol

Identifies an unknown buffer of data as a Packet. The raw data is returned but the packet that matched is recorded so it can be set in the read_packet callback.

Returns:

  • (String|Symbol)

    The identified packet data or :STOP if more data is required to build a packet



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 76

def identify_and_finish_packet
  packet_data = nil
  identified_packet = nil

  if @telemetry
    target_names = @interface.tlm_target_names
  else
    target_names = @interface.cmd_target_names
  end

  target_names.each do |target_name|
    target_packets = nil
    unique_id_mode = false
    begin
      if @telemetry
        target_packets = System.telemetry.packets(target_name)
        unique_id_mode = System.telemetry.tlm_unique_id_mode(target_name)
      else
        target_packets = System.commands.packets(target_name)
        unique_id_mode = System.commands.cmd_unique_id_mode(target_name)
      end
    rescue RuntimeError
      # No commands/telemetry for this target
      next
    end

    if unique_id_mode
      target_packets.each do |_packet_name, packet|
        if not packet.subpacket and packet.identify?(@data[@discard_leading_bytes..-1]) # identify? handles virtual
          identified_packet = packet
          break
        end
      end
    else
      # Do a hash lookup to quickly identify the packet
      if target_packets.length > 0
        packet = nil
        target_packets.each do |_packet_name, target_packet|
          next if target_packet.virtual
          next if target_packet.subpacket
          packet = target_packet
          break
        end
        if packet
          key = packet.read_id_values(@data[@discard_leading_bytes..-1])
          if @telemetry
            hash = System.telemetry.config.tlm_id_value_hash[target_name]
          else
            hash = System.commands.config.cmd_id_value_hash[target_name]
          end
          identified_packet = hash[key]
          identified_packet = hash['CATCHALL'.freeze] unless identified_packet
        end
      end
    end

    if identified_packet
      if identified_packet.defined_length + @discard_leading_bytes > @data.length
        # Check if need more data to finish packet
        return :STOP
      end

      # Set some variables so we can update the packet in
      # read_packet
      @received_time = Time.now.sys
      @target_name = identified_packet.target_name
      @packet_name = identified_packet.packet_name

      # Get the data from this packet
      # Previous implementation looked like the following:
      #   packet_data = @data.slice!(0, identified_packet.defined_length + @discard_leading_bytes)
      # But slice! is 6x slower at small packets (1024)
      # and 1000x slower at large packets (1Mb)
      # Run test/benchmarks/string_mod_benchmark.rb for details

      # Triple dot range because it's effectively a length calculation and we start with 0
      packet_data = @data[0...(identified_packet.defined_length + @discard_leading_bytes)]
      @data = @data[(identified_packet.defined_length + @discard_leading_bytes)..-1]
      break
    end
  end

  unless identified_packet
    raise "Unknown data received by FixedProtocol" if @unknown_raise

    # Unknown packet? Just return all the current data
    @received_time = nil
    @target_name = nil
    @packet_name = nil
    packet_data = @data.clone
    @data.replace('')
  end

  return packet_data, @extra
end

#read_detailsObject



186
187
188
189
190
191
192
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 186

def read_details
  result = super()
  result['min_id_size'] = @min_id_size
  result['telemetry'] = @telemetry
  result['unknown_raise'] = @unknown_raise
  return result
end

#read_packet(packet) ⇒ Object

Set the received_time, target_name and packet_name which we recorded when we identified this packet. The server will also do this but since we know the information here, we perform this optimization.



61
62
63
64
65
66
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 61

def read_packet(packet)
  packet.received_time = @received_time
  packet.target_name = @target_name
  packet.packet_name = @packet_name
  return packet
end

#reduce_to_single_packetObject



172
173
174
175
176
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 172

def reduce_to_single_packet
  return :STOP if @data.length < @min_id_size

  identify_and_finish_packet()
end

#write_detailsObject



178
179
180
181
182
183
184
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 178

def write_details
  result = super()
  result['min_id_size'] = @min_id_size
  result['telemetry'] = @telemetry
  result['unknown_raise'] = @unknown_raise
  return result
end