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
# 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)
        target = System.targets[target_name]
        unique_id_mode = target.tlm_unique_id_mode if target
      else
        target_packets = System.commands.packets(target_name)
        target = System.targets[target_name]
        unique_id_mode = target.cmd_unique_id_mode if target
      end
    rescue RuntimeError
      # No commands/telemetry for this target
      next
    end

    if unique_id_mode
      target_packets.each do |_packet_name, packet|
        if packet.identify?(@data[@discard_leading_bytes..-1])
          identified_packet = packet
          break
        end
      end
    else
      # Do a hash lookup to quickly identify the packet
      if target_packets.length > 0
        packet = target_packets.first[1]
        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

    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



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

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



166
167
168
169
170
# File 'lib/openc3/interfaces/protocols/fixed_protocol.rb', line 166

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

  identify_and_finish_packet()
end

#write_detailsObject



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

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