diff --git a/lib/cosmos/interfaces/protocols/burst_protocol.rb b/lib/cosmos/interfaces/protocols/burst_protocol.rb index 040a42f39..3c9aee569 100644 --- a/lib/cosmos/interfaces/protocols/burst_protocol.rb +++ b/lib/cosmos/interfaces/protocols/burst_protocol.rb @@ -23,7 +23,8 @@ class BurstProtocol < Protocol # that will be searched for in the raw data. Bytes encountered before # this pattern is found are discarded. # @param fill_fields [Boolean] Fill any required fields when writing packets - def initialize(discard_leading_bytes = 0, sync_pattern = nil, fill_fields = false, allow_empty_data = false) + # @param allow_empty_data [true/false/nil] See Protocol#initialize + def initialize(discard_leading_bytes = 0, sync_pattern = nil, fill_fields = false, allow_empty_data = nil) super(allow_empty_data) @discard_leading_bytes = discard_leading_bytes.to_i @sync_pattern = ConfigParser.handle_nil(sync_pattern) @@ -44,6 +45,8 @@ def reset # # @return [String|nil] Data for a packet consisting of the bytes read def read_data(data) + return super(data) if (data.length <= 0) + @data << data control = handle_sync_pattern() diff --git a/lib/cosmos/interfaces/protocols/crc_protocol.rb b/lib/cosmos/interfaces/protocols/crc_protocol.rb index 1e9817603..615b02fd8 100644 --- a/lib/cosmos/interfaces/protocols/crc_protocol.rb +++ b/lib/cosmos/interfaces/protocols/crc_protocol.rb @@ -21,7 +21,8 @@ class CrcProtocol < Protocol def initialize(write_item_name, strip_crc, bad_strategy, bit_offset, bit_size = 32, endianness = 'BIG_ENDIAN', - poly = nil, seed = nil, xor = nil, reflect = nil) + poly = nil, seed = nil, xor = nil, reflect = nil, allow_empty_data = nil) + super(allow_empty_data) @write_item_name = ConfigParser.handle_nil(write_item_name) @strip_crc = ConfigParser.handle_true_false(strip_crc) raise "Invalid strip CRC of '#{strip_crc}'. Must be TRUE or FALSE." unless !!@strip_crc == @strip_crc @@ -95,6 +96,8 @@ def initialize(write_item_name, strip_crc, bad_strategy, bit_offset, end def read_data(data) + return super(data) if (data.length <= 0) + crc = BinaryAccessor.read(@bit_offset, @bit_size, :UINT, data, @endianness) calculated_crc = @crc.calc(data[0...(@bit_offset / 8)]) if calculated_crc != crc diff --git a/lib/cosmos/interfaces/protocols/fixed_protocol.rb b/lib/cosmos/interfaces/protocols/fixed_protocol.rb index b2720656f..75255f529 100644 --- a/lib/cosmos/interfaces/protocols/fixed_protocol.rb +++ b/lib/cosmos/interfaces/protocols/fixed_protocol.rb @@ -25,15 +25,17 @@ class FixedProtocol < BurstProtocol # telemetry (true) or commands (false) # @param fill_fields (see BurstProtocol#initialize) # @param unknown_raise Whether to raise an exception on an unknown packet + # @param allow_empty_data [true/false/nil] See Protocol#initialize def initialize( min_id_size, discard_leading_bytes = 0, sync_pattern = nil, telemetry = true, fill_fields = false, - unknown_raise = false + unknown_raise = false, + allow_empty_data = nil ) - super(discard_leading_bytes, sync_pattern, fill_fields) + 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) diff --git a/lib/cosmos/interfaces/protocols/length_protocol.rb b/lib/cosmos/interfaces/protocols/length_protocol.rb index 00c72df34..30e2a4904 100644 --- a/lib/cosmos/interfaces/protocols/length_protocol.rb +++ b/lib/cosmos/interfaces/protocols/length_protocol.rb @@ -33,6 +33,7 @@ class LengthProtocol < BurstProtocol # @param max_length [Integer] The maximum allowed value of the length field # @param fill_length_and_sync_pattern [Boolean] Fill the length field and sync # pattern when writing packets + # @param allow_empty_data [true/false/nil] See Protocol#initialize def initialize( length_bit_offset = 0, length_bit_size = 16, @@ -42,9 +43,10 @@ def initialize( discard_leading_bytes = 0, sync_pattern = nil, max_length = nil, - fill_length_and_sync_pattern = false + fill_length_and_sync_pattern = false, + allow_empty_data = nil ) - super(discard_leading_bytes, sync_pattern, fill_length_and_sync_pattern) + 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) diff --git a/lib/cosmos/interfaces/protocols/override_protocol.rb b/lib/cosmos/interfaces/protocols/override_protocol.rb index a34844fd1..486a9d1ba 100644 --- a/lib/cosmos/interfaces/protocols/override_protocol.rb +++ b/lib/cosmos/interfaces/protocols/override_protocol.rb @@ -17,8 +17,8 @@ module Cosmos # methods. Clearing the override requires calling normalize_tlm. class OverrideProtocol < Protocol - # @param allow_empty_data [true/false] Whether STOP should be returned on empty data - def initialize(allow_empty_data = false) + # @param allow_empty_data [true/false/nil] See Protocol#initialize + def initialize(allow_empty_data = nil) super(allow_empty_data) end diff --git a/lib/cosmos/interfaces/protocols/preidentified_protocol.rb b/lib/cosmos/interfaces/protocols/preidentified_protocol.rb index bf5893ff6..f3daad09f 100644 --- a/lib/cosmos/interfaces/protocols/preidentified_protocol.rb +++ b/lib/cosmos/interfaces/protocols/preidentified_protocol.rb @@ -16,8 +16,9 @@ class PreidentifiedProtocol < BurstProtocol # @param sync_pattern (see BurstProtocol#initialize) # @param max_length [Integer] The maximum allowed value of the length field - def initialize(sync_pattern = nil, max_length = nil) - super(0, sync_pattern) + # @param allow_empty_data [true/false/nil] See Protocol#initialize + def initialize(sync_pattern = nil, max_length = nil, allow_empty_data = nil) + super(0, sync_pattern, false, allow_empty_data) @max_length = ConfigParser.handle_nil(max_length) @max_length = Integer(@max_length) if @max_length end diff --git a/lib/cosmos/interfaces/protocols/protocol.rb b/lib/cosmos/interfaces/protocols/protocol.rb index b1d7fb0e9..2aa6d839c 100644 --- a/lib/cosmos/interfaces/protocols/protocol.rb +++ b/lib/cosmos/interfaces/protocols/protocol.rb @@ -17,10 +17,12 @@ class Protocol attr_accessor :interface attr_accessor :allow_empty_data - # @param allow_empty_data [true/false] Whether STOP should be returned on empty data - def initialize(allow_empty_data = false) + # @param allow_empty_data [true/false/nil] Whether or not this protocol will allow an empty string + # to be passed down to later Protocols (instead of returning :STOP). Can be true, false, or nil, where + # nil is interpreted as true unless the Protocol is the last Protocol of the chain. + def initialize(allow_empty_data = nil) @interface = nil - @allow_empty_data = ConfigParser.handle_true_false(allow_empty_data) + @allow_empty_data = ConfigParser.handle_true_false_nil(allow_empty_data) reset() end @@ -37,7 +39,17 @@ def disconnect_reset # Ensure we have some data in case this is the only protocol def read_data(data) - return :STOP if (data.length <= 0) && !@allow_empty_data + if (data.length <= 0) + if @allow_empty_data.nil? + if @interface and @interface.read_protocols[-1] == self + # Last read interface in chain with auto @allow_empty_data + return :STOP + end + elsif !@allow_empty_data + # Don't @allow_empty_data means STOP + return :STOP + end + end data end diff --git a/lib/cosmos/interfaces/protocols/template_protocol.rb b/lib/cosmos/interfaces/protocols/template_protocol.rb index 53eb42480..7e1c4d135 100644 --- a/lib/cosmos/interfaces/protocols/template_protocol.rb +++ b/lib/cosmos/interfaces/protocols/template_protocol.rb @@ -36,6 +36,7 @@ class TemplateProtocol < TerminatedProtocol # for a response # @param raise_exceptions [String] Whether to raise exceptions when errors # occur in the protocol like unexpected responses or response timeouts. + # @param allow_empty_data [true/false/nil] See Protocol#initialize def initialize( write_termination_characters, read_termination_characters, @@ -48,7 +49,8 @@ def initialize( fill_fields = false, response_timeout = 5.0, response_polling_period = 0.02, - raise_exceptions = false + raise_exceptions = false, + allow_empty_data = nil ) super( write_termination_characters, @@ -56,7 +58,8 @@ def initialize( strip_read_termination, discard_leading_bytes, sync_pattern, - fill_fields) + fill_fields, + allow_empty_data) @response_template = nil @response_packet = nil @response_packets = [] @@ -93,6 +96,8 @@ def disconnect_reset end def read_data(data) + return super(data) if (data.length <= 0) + # Drop all data until the initial_read_delay is complete. # This gets rid of unused welcome messages, # prompts, and other junk on initial connections diff --git a/lib/cosmos/interfaces/protocols/terminated_protocol.rb b/lib/cosmos/interfaces/protocols/terminated_protocol.rb index 0368b375e..0c5d1a3d2 100644 --- a/lib/cosmos/interfaces/protocols/terminated_protocol.rb +++ b/lib/cosmos/interfaces/protocols/terminated_protocol.rb @@ -27,19 +27,21 @@ class TerminatedProtocol < BurstProtocol # @param discard_leading_bytes (see BurstProtocol#initialize) # @param sync_pattern (see BurstProtocol#initialize) # @param fill_fields (see BurstProtocol#initialize) + # @param allow_empty_data [true/false/nil] See Protocol#initialize def initialize( write_termination_characters, read_termination_characters, strip_read_termination = true, discard_leading_bytes = 0, sync_pattern = nil, - fill_fields = false + fill_fields = false, + allow_empty_data = nil ) @write_termination_characters = write_termination_characters.hex_to_byte_string @read_termination_characters = read_termination_characters.hex_to_byte_string @strip_read_termination = ConfigParser.handle_true_false(strip_read_termination) - super(discard_leading_bytes, sync_pattern, fill_fields) + super(discard_leading_bytes, sync_pattern, fill_fields, allow_empty_data) end def write_data(data) diff --git a/spec/interfaces/protocols/burst_protocol_spec.rb b/spec/interfaces/protocols/burst_protocol_spec.rb index a5eb56493..e5c01886c 100644 --- a/spec/interfaces/protocols/burst_protocol_spec.rb +++ b/spec/interfaces/protocols/burst_protocol_spec.rb @@ -197,6 +197,24 @@ def read pkt = @interface.read expect(pkt.length).to eql 3 # sync plus one byte end + + it "handle auto allow_empty_data correctly" do + @interface.add_protocol(BurstProtocol, [0, nil, false, nil], :READ_WRITE) + expect(@interface.read_protocols[0].read_data("")).to eql :STOP + expect(@interface.read_protocols[0].read_data("A")).to eql "A" + @interface.add_protocol(BurstProtocol, [0, nil, false, nil], :READ_WRITE) + expect(@interface.read_protocols[0].read_data("")).to eql "" + expect(@interface.read_protocols[1].read_data("")).to eql :STOP + expect(@interface.read_protocols[0].read_data("A")).to eql "A" + expect(@interface.read_protocols[1].read_data("A")).to eql "A" + @interface.add_protocol(BurstProtocol, [0, nil, false, nil], :READ_WRITE) + expect(@interface.read_protocols[0].read_data("")).to eql "" + expect(@interface.read_protocols[1].read_data("")).to eql "" + expect(@interface.read_protocols[2].read_data("")).to eql :STOP + expect(@interface.read_protocols[0].read_data("A")).to eql "A" + expect(@interface.read_protocols[1].read_data("A")).to eql "A" + expect(@interface.read_protocols[2].read_data("A")).to eql "A" + end end describe "write" do