Class: OpenC3::BurstProtocol
- Defined in:
- lib/openc3/interfaces/protocols/burst_protocol.rb,
ext/openc3/ext/burst_protocol/burst_protocol.c
Overview
Reads all data available on the interface and creates a packet with that data.
Direct Known Subclasses
FixedProtocol, LengthProtocol, PreidentifiedProtocol, TerminatedProtocol
Constant Summary collapse
- DEFAULT_MAX_BUFFER_SIZE =
Maximum number of bytes which will be buffered while waiting for a complete packet. This bounds the memory a peer can cause the interface to allocate by declaring an enormous packet length (LENGTH, PREIDENTIFIED) or by never sending a terminator (TERMINATED). Deliberately much larger than any realistic packet so it does not affect existing configurations. Override with the OPENC3_PROTOCOL_MAX_BUFFER_SIZE environment variable.
100_000_000
Instance Attribute Summary
Attributes inherited from Protocol
#allow_empty_data, #extra, #interface
Instance Method Summary collapse
-
#check_buffer_size ⇒ Object
Raises if the accumulation buffer has grown past @max_buffer_size.
-
#handle_sync_pattern ⇒ Boolean
Control code (nil, :STOP).
-
#initialize(discard_leading_bytes = 0, sync_pattern = nil, fill_fields = false, allow_empty_data = nil) ⇒ BurstProtocol
constructor
A new instance of BurstProtocol.
- #log_discard(length, found) ⇒ Object
-
#read_data(*args) ⇒ String|nil
Reads from the interface.
- #read_details ⇒ Object
- #reduce_to_single_packet ⇒ Object
- #reset ⇒ Object
-
#write_data(data, extra = nil) ⇒ String
Called to perform modifications on write data before sending it to the interface.
- #write_details ⇒ Object
-
#write_packet(packet) ⇒ Packet
Called to perform modifications on a command packet before it is sent.
Methods inherited from Protocol
#connect_reset, #disconnect_reset, #post_write_interface, #protocol_cmd, #read_packet, #read_protocol_input_base, #read_protocol_output_base, #write_protocol_input_base, #write_protocol_output_base
Constructor Details
#initialize(discard_leading_bytes = 0, sync_pattern = nil, fill_fields = false, allow_empty_data = nil) ⇒ BurstProtocol
Returns a new instance of BurstProtocol.
42 43 44 45 46 47 48 49 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 42 def initialize(discard_leading_bytes = 0, sync_pattern = nil, fill_fields = false, allow_empty_data = nil) super(allow_empty_data) # Calls reset() @discard_leading_bytes = discard_leading_bytes.to_i @sync_pattern = ConfigParser.handle_nil(sync_pattern) @sync_pattern = @sync_pattern.hex_to_byte_string if @sync_pattern @fill_fields = ConfigParser.handle_true_false(fill_fields) @max_buffer_size = Integer(ENV.fetch('OPENC3_PROTOCOL_MAX_BUFFER_SIZE', DEFAULT_MAX_BUFFER_SIZE)) end |
Instance Method Details
#check_buffer_size ⇒ Object
Raises if the accumulation buffer has grown past @max_buffer_size. Called from read_data by both the Ruby and the C implementations. Raising causes Interface#read to log the error and disconnect, which drops the offending peer and resets this protocol.
209 210 211 212 213 214 215 216 217 218 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 209 def check_buffer_size length = @data.length return if length <= @max_buffer_size # Release the buffer and clear any partially reduced state now rather than # waiting for the interface to disconnect and reset() us reset() raise "#{@interface ? @interface.name : ""}: Protocol buffer of #{length} bytes exceeds maximum of #{@max_buffer_size} bytes. " \ "Increase OPENC3_PROTOCOL_MAX_BUFFER_SIZE." end |
#handle_sync_pattern ⇒ Boolean
Returns control code (nil, :STOP).
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 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 119 def handle_sync_pattern if @sync_pattern and @sync_state == :SEARCHING loop do # Make sure we have some data to look for a sync word in return :STOP if @data.length < @sync_pattern.length # Find the beginning of the sync pattern sync_index = @data.index(@sync_pattern.getbyte(0).chr) if sync_index # Make sure we have enough data for the whole sync pattern past this index return :STOP if @data.length < (sync_index + @sync_pattern.length) # Check for the rest of the sync pattern found = true index = sync_index @sync_pattern.each_byte do |byte| if @data.getbyte(index) != byte found = false break end index += 1 end if found if sync_index != 0 log_discard(sync_index, true) # Delete Data Before Sync Pattern @data.replace(@data[sync_index..-1]) end @sync_state = :FOUND return nil else # not found log_discard(sync_index + 1, false) # Delete Data Before and including first character of suspected sync Pattern @data.replace(@data[(sync_index + 1)..-1]) next end # if found else # sync_index = nil log_discard(@data.length, false) @data.replace('') return :STOP end # unless sync_index.nil? end # end loop end # if @sync_pattern nil end |
#log_discard(length, found) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 220 def log_discard(length, found) Logger.error("#{@interface ? @interface.name : ""}: Sync #{'not ' unless found}found. Discarding #{length} bytes of data.") if @data.length >= 0 Logger.error(sprintf("Starting: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", @data.length >= 1 ? @data.getbyte(0) : 0, @data.length >= 2 ? @data.getbyte(1) : 0, @data.length >= 3 ? @data.getbyte(2) : 0, @data.length >= 4 ? @data.getbyte(3) : 0, @data.length >= 5 ? @data.getbyte(4) : 0, @data.length >= 6 ? @data.getbyte(5) : 0)) end end |
#read_data(*args) ⇒ String|nil
Reads from the interface. It can look for a sync pattern before creating a Packet. It can discard a set number of bytes at the beginning before creating the Packet.
Note: On the first call to this from any interface read(), data will contain a blank string. Blank string is an opportunity for protocols to return any queued up packets. If they have no queued up packets, they should pass the blank string down to chained protocols giving them the same opportunity.
70 71 72 73 74 75 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 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 70 def read_data(data, extra = nil) @data << data check_buffer_size() @extra = extra unless (data.length == 0 and extra.nil?) # Maintain extra from last read read_data while true control = handle_sync_pattern() return control if control and data.length > 0 # Only return here if not blank string test # Reduce the data to a single packet packet_data, extra = reduce_to_single_packet() if packet_data == :RESYNC @sync_state = :SEARCHING next if data.length > 0 # Only immediately resync if not blank string test end # Potentially allow blank string to be sent to other protocols if no packet is ready in this one if Symbol === packet_data if (data.length <= 0) and packet_data != :DISCONNECT # On blank string test, return blank string (unless we had a packet or need disconnect) # The base class handles the special case of returning STOP if on the last protocol in the # chain return super(data, extra) else return packet_data, extra # Return any control code if not on blank string test end end @sync_state = :SEARCHING # Discard leading bytes if necessary packet_data.replace(packet_data[@discard_leading_bytes..-1]) if @discard_leading_bytes > 0 return packet_data, extra end end |
#read_details ⇒ Object
242 243 244 245 246 247 248 249 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 242 def read_details result = super() result['discard_leading_bytes'] = @discard_leading_bytes result['sync_pattern'] = @sync_pattern.inspect result['fill_fields'] = @fill_fields result['max_buffer_size'] = @max_buffer_size return result end |
#reduce_to_single_packet ⇒ Object
269 270 271 272 273 274 275 276 277 278 279 |
# File 'ext/openc3/ext/burst_protocol/burst_protocol.c', line 269 def reduce_to_single_packet if @data.length <= 0 # Need some data return :STOP end # Reduce to packet data and clear data for next packet packet_data = @data.clone @data.replace('') return packet_data, @extra end |
#reset ⇒ Object
51 52 53 54 55 56 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 51 def reset super() @data = '' @data.force_encoding('ASCII-8BIT') @sync_state = :SEARCHING end |
#write_data(data, extra = nil) ⇒ String
Called to perform modifications on write data before sending it to the interface
190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 190 def write_data(data, extra = nil) # If we're filling the sync pattern and discarding the leading bytes # during a read then we need to put them back during a write. # If we're discarding the bytes then by definition they can't be part # of the packet so we just modify the data. if @fill_fields && @discard_leading_bytes > 0 data = ("\x00" * @discard_leading_bytes) << data if @sync_pattern BinaryAccessor.write(@sync_pattern, 0, @sync_pattern.length * 8, :BLOCK, data, :BIG_ENDIAN, :ERROR) end end return super(data, extra) end |
#write_details ⇒ Object
233 234 235 236 237 238 239 240 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 233 def write_details result = super() result['discard_leading_bytes'] = @discard_leading_bytes result['sync_pattern'] = @sync_pattern.inspect result['fill_fields'] = @fill_fields result['max_buffer_size'] = @max_buffer_size return result end |
#write_packet(packet) ⇒ Packet
Called to perform modifications on a command packet before it is sent
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/openc3/interfaces/protocols/burst_protocol.rb', line 174 def write_packet(packet) # If we're filling the sync pattern and the sync pattern is part of the # packet (since we're not discarding any leading bytes) then we have to # fill the sync pattern in the actual packet so do it here. if @fill_fields && @sync_pattern && @discard_leading_bytes == 0 # Directly write the packet buffer and fill in the sync pattern BinaryAccessor.write(@sync_pattern, 0, @sync_pattern.length * 8, :BLOCK, packet.buffer(false), :BIG_ENDIAN, :ERROR) end packet end |