Class: OpenC3::LengthProtocol

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

Overview

Protocol which delineates packets using a length field at a fixed location in each packet.

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

Methods inherited from Protocol

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

Constructor Details

#initialize(length_bit_offset = 0, length_bit_size = 16, length_value_offset = 0, length_bytes_per_count = 1, length_endianness = 'BIG_ENDIAN', discard_leading_bytes = 0, sync_pattern = nil, max_length = nil, fill_length_and_sync_pattern = false, allow_empty_data = nil) ⇒ LengthProtocol

Returns a new instance of LengthProtocol.

Parameters:

  • length_bit_offset (Integer) (defaults to: 0)

    The bit offset of the length field

  • length_bit_size (Integer) (defaults to: 16)

    The size in bits of the length field

  • length_value_offset (Integer) (defaults to: 0)

    The offset to apply to the length value once it has been read from the packet. The value in the length field itself plus the length value offset MUST equal the total bytes including any discarded bytes. For example: if your length field really means "length - 1" this value should be 1.

  • length_bytes_per_count (Integer) (defaults to: 1)

    The number of bytes per each length field 'count'. This is used if the units of the length field is something other than bytes, for example words.

  • length_endianness (String) (defaults to: 'BIG_ENDIAN')

    The endianness of the length field. Must be either BIG_ENDIAN or LITTLE_ENDIAN.

  • max_length (Integer) (defaults to: nil)

    The maximum allowed value of the length field

  • fill_length_and_sync_pattern (Boolean) (defaults to: false)

    Fill the length field and sync pattern when writing packets

  • 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.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 49

def initialize(
  length_bit_offset = 0,
  length_bit_size = 16,
  length_value_offset = 0,
  length_bytes_per_count = 1,
  length_endianness = 'BIG_ENDIAN',
  discard_leading_bytes = 0,
  sync_pattern = nil,
  max_length = nil,
  fill_length_and_sync_pattern = false,
  allow_empty_data = nil
)
  super(discard_leading_bytes, sync_pattern, fill_length_and_sync_pattern, allow_empty_data)

  # Save length field attributes
  @length_bit_offset = Integer(length_bit_offset)
  @length_bit_size = Integer(length_bit_size)
  @length_value_offset = Integer(length_value_offset)
  @length_bytes_per_count = Integer(length_bytes_per_count)

  # Save endianness
  if length_endianness.to_s.upcase == 'LITTLE_ENDIAN'
    @length_endianness = :LITTLE_ENDIAN
  else
    @length_endianness = :BIG_ENDIAN
  end

  # Derive number of bytes required to contain entire length field
  if @length_endianness == :BIG_ENDIAN or ((@length_bit_offset % 8) == 0)
    length_bits_needed = @length_bit_offset + @length_bit_size
    length_bits_needed += 8 if (length_bits_needed % 8) != 0
    @length_bytes_needed = ((length_bits_needed - 1) / 8) + 1
  else
    @length_bytes_needed = (length_bit_offset / 8) + 1
  end

  # Save max length setting
  @max_length = ConfigParser.handle_nil(max_length)
  @max_length = Integer(@max_length) if @max_length
end

Instance Method Details

#calculate_length(buffer_length) ⇒ Object

protected



154
155
156
157
158
159
160
161
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 154

def calculate_length(buffer_length)
  length = (buffer_length / @length_bytes_per_count) - @length_value_offset
  if @max_length && length > @max_length
    raise "Calculated length #{length} larger than max_length #{@max_length}"
  end

  length
end

#read_detailsObject



140
141
142
143
144
145
146
147
148
149
150
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 140

def read_details
  result = super()
  result['length_bit_offset'] = @length_bit_offset
  result['length_bit_size'] = @length_bit_size
  result['length_value_offset'] = @length_value_offset
  result['length_bytes_per_count'] = @length_bytes_per_count
  result['length_endianness'] = @length_endianness
  result['length_bytes_needed'] = @length_bytes_needed
  result['max_length'] = @max_length
  return result
end

#reduce_to_single_packetObject



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
188
189
190
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 163

def reduce_to_single_packet
  # Make sure we have at least enough data to reach the length field
  return :STOP if @data.length < @length_bytes_needed

  # Determine the packet's length
  length = BinaryAccessor.read(@length_bit_offset,
                               @length_bit_size,
                               :UINT,
                               @data,
                               @length_endianness)
  raise "Length value received larger than max_length: #{length} > #{@max_length}" if @max_length and length > @max_length

  packet_length = (length * @length_bytes_per_count) + @length_value_offset
  # Ensure the calculated packet length is long enough to support the location of the length field
  # without overlap into the next packet
  if (packet_length * 8) < (@length_bit_offset + @length_bit_size)
    raise "Calculated packet length of #{packet_length * 8} bits < (offset:#{@length_bit_offset} + size:#{@length_bit_size})"
  end

  # Make sure we have enough data for the packet
  return :STOP if @data.length < packet_length

  # Reduce to packet data and setup current_data for next packet
  packet_data = @data[0..(packet_length - 1)]
  @data.replace(@data[packet_length..-1])

  return packet_data, @extra
end

#write_data(data, extra = nil) ⇒ String

Called to perform modifications on write data before making it into a packet

Parameters:

  • data (String)

    Raw packet data

Returns:

  • (String)

    Potentially modified packet data



115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 115

def write_data(data, extra = nil)
  data, extra = super(data, extra)
  if @fill_fields
    # If the start of the length field is before what we discard, then the
    # length field is outside the packet
    if @length_bit_offset < (@discard_leading_bytes * 8)
      BinaryAccessor.write(calculate_length(data.length), @length_bit_offset, @length_bit_size, :UINT,
                           data, @length_endianness, :ERROR)
    end
  end
  return data, extra
end

#write_detailsObject



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 128

def write_details
  result = super()
  result['length_bit_offset'] = @length_bit_offset
  result['length_bit_size'] = @length_bit_size
  result['length_value_offset'] = @length_value_offset
  result['length_bytes_per_count'] = @length_bytes_per_count
  result['length_endianness'] = @length_endianness
  result['length_bytes_needed'] = @length_bytes_needed
  result['max_length'] = @max_length
  return result
end

#write_packet(packet) ⇒ Packet

Called to perform modifications on a command packet before it is send

Parameters:

  • packet (Packet)

    Original packet

Returns:

  • (Packet)

    Potentially modified packet



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/openc3/interfaces/protocols/length_protocol.rb', line 94

def write_packet(packet)
  if @fill_fields
    # If the start of the length field is past what we discard, then the
    # length field is inside the packet
    if @length_bit_offset >= (@discard_leading_bytes * 8)
      length = calculate_length(packet.buffer(false).length + @discard_leading_bytes)
      # Subtract off the discarded bytes since they haven't been added yet
      # Adding bytes happens in the write_data method
      offset = @length_bit_offset - (@discard_leading_bytes * 8)
      # Directly write the packet buffer and fill in the length
      BinaryAccessor.write(length, offset, @length_bit_size, :UINT,
                           packet.buffer(false), @length_endianness, :ERROR)
    end
  end
  return super(packet) # Allow burst_protocol to set the sync if needed
end