All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.serial.PacketInputStream Maven / Gradle / Ivy

Go to download

Artifact providing generic (byte) serialization functionality including a TTY-/COM-Port implementation of the serial framework as well as a (local) loopback port.

The newest version!
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.serial;

import java.io.IOException;
import java.io.InputStream;

import org.refcodes.exception.UnhandledEnumBugException;
import org.refcodes.mixin.BlockSizeAccessor;
import org.refcodes.mixin.ConcatenateMode;
import org.refcodes.mixin.InputStreamAccessor.InputStreamBuilder;
import org.refcodes.mixin.PacketSizeAccessor;
import org.refcodes.numerical.ChecksumValidationMode;
import org.refcodes.numerical.ChecksumValidationModeAccessor.ChecksumValidationModeBuilder;
import org.refcodes.numerical.CrcAlgorithm;
import org.refcodes.numerical.CrcAlgorithmAccessor.CrcAlgorithmBuilder;
import org.refcodes.numerical.CrcChecksumConcatenateModeAccessor.CrcChecksumConcatenateModeBuilder;
import org.refcodes.numerical.Endianess;
import org.refcodes.numerical.EndianessAccessor;
import org.refcodes.serial.SegmentPackager.DummySegmentPackager;

/**
 * The {@link PacketInputStream} wraps an {@link InputStream} and chunks any
 * data to be written into packets with a sequence number and a block of data.
 * An according {@link PacketInputStream} then reverts the packetised data
 * stream while performing sequence number validation. A {@link SegmentPackager}
 * can be used to add functionality such as CRC checksum support.
 */
public class PacketInputStream extends InputStream implements PacketLengthWidthAccessor, EndianessAccessor, BlockSizeAccessor, SequenceNumberAccessor, SequenceNumberWidthAccessor, SequenceNumberInitValueAccessor, SequenceNumberConcatenateModeAccessor, PacketSizeAccessor, PacketSegmentPackagerAccessor, PacketMagicBytesAccessor {

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private Sequence _blockSequence;
	private int _blockSize;
	private Endianess _endianess;
	private boolean _isClosed;
	private int _packetLength;
	private byte[] _packetMagicBytes;
	private SegmentPackager _packetSegmentPackager;
	private ConcatenateMode _sequenceNumberConcatenateMode;
	private int _sequenceNumberWidth;
	private int _packetLengthWidth;
	protected int _blockOffset;
	protected InputStream _inputStream;
	protected Segment _packetSegment;
	protected int _sequenceNumber;
	protected int _sequenceNumberInitValue;
	protected NumberSegment _sequenceNumberSegment;
	protected AllocSectionDecoratorSegment _allocSegment;
	protected BoundedSequenceDecorator _boundedSequence;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Creates builder to build {@link PacketInputStream}.
	 * 
	 * @return created builder
	 */
	public static Builder builder() {
		return new Builder();
	}

	private PacketInputStream( Builder aBuilder ) {
		this( aBuilder.inputStream, aBuilder.blockSize, aBuilder.truncateLengthWidth, aBuilder.packetMagicBytes, aBuilder.sequenceNumberInitValue, aBuilder.sequenceNumberWidth, aBuilder.sequenceNumberConcatenateMode, aBuilder.toPacketSegmentPackager(), aBuilder.endianess );
	}

	// -------------------------------------------------------------------------

	/**
	 * Constructs an according {@link PacketInputStream} instance wrapping the
	 * given {@link InputStream}. The configuration attributes are taken from
	 * the {@link TransmissionMetrics} configuration object, though only those
	 * attributes are supported which are also supported by the other
	 * constructors!
	 *
	 * @param aInputStream The {@link InputStream} to be wrapped.
	 * @param aTransmissionMetrics The {@link TransmissionMetrics} to be used
	 *        for configuring this instance.
	 */
	public PacketInputStream( InputStream aInputStream, TransmissionMetrics aTransmissionMetrics ) {
		this( aInputStream, aTransmissionMetrics.getBlockSize(), aTransmissionMetrics.getPacketLengthWidth(), aTransmissionMetrics.getPacketMagicBytes(), aTransmissionMetrics.getSequenceNumberInitValue(), aTransmissionMetrics.getSequenceNumberWidth(), aTransmissionMetrics.getSequenceNumberConcatenateMode(), aTransmissionMetrics.toPacketSegmentPackager(), aTransmissionMetrics.getEndianess() );
	}

	// -------------------------------------------------------------------------

	/**
	 * Constructs an according {@link PacketInputStream} instance wrapping the
	 * given {@link InputStream}.
	 *
	 * @param aInputStream The {@link InputStream} to be wrapped.
	 * @param aBlockSize The block size of a data block for each packet.
	 * @param aPacketLengthWidth The width (bytes) for declaring the (max)
	 *        length of a package.
	 * @param aPacketMagicBytes The magic bytes identifying a packet and
	 *        distinguishing a packet from a last package.
	 * @param aSequenceNumberInitValue The initial sequence number from where to
	 *        start counting the blocks.
	 * @param aSequenceNumberWidth The width (in bytes) to be used for sequence
	 *        number values.
	 * @param aSequenceNumberConcatenateMode The mode of concatenation to use
	 *        when creating a {@link Sequence} from this {@link Transmission}
	 *        and the decorated {@link Transmission}.
	 * @param aPacketSegmentPackager An (optional) {@link SegmentPackager} used
	 *        to modify a packet's data e.g. with a CRC checksum.
	 * @param aEndianess The {@link Endianess} to use for integer (double)
	 *        numbers and the like.
	 */
	public PacketInputStream( InputStream aInputStream, int aBlockSize, int aPacketLengthWidth, byte[] aPacketMagicBytes, int aSequenceNumberInitValue, int aSequenceNumberWidth, ConcatenateMode aSequenceNumberConcatenateMode, SegmentPackager aPacketSegmentPackager, Endianess aEndianess ) {
		_inputStream = aInputStream;
		_packetMagicBytes = aPacketMagicBytes;
		_sequenceNumberConcatenateMode = aSequenceNumberConcatenateMode;
		_sequenceNumberInitValue = aSequenceNumberInitValue;
		_sequenceNumberWidth = aSequenceNumberWidth;
		_sequenceNumber = aSequenceNumberInitValue != -1 ? aSequenceNumberInitValue : 0;
		_blockSize = aBlockSize;
		_packetLengthWidth = aPacketLengthWidth;
		_endianess = aEndianess;
		_packetSegmentPackager = aPacketSegmentPackager != null ? aPacketSegmentPackager : new DummySegmentPackager();
		_blockSequence = new ByteArraySequence( aBlockSize );
		_boundedSequence = new BoundedSequenceDecorator( _blockSequence, aBlockSize );
		_sequenceNumberSegment = new NumberSegment( aSequenceNumberWidth, (long) aSequenceNumberInitValue, aEndianess );
		_blockOffset = aBlockSize; // Set offset to the "end" for a new block to be created
		final AllocSectionDecoratorSegment theAllocSegment = new AllocSectionDecoratorSegment<>( new SequenceSection( _boundedSequence ), aPacketLengthWidth, aEndianess );
		_allocSegment = theAllocSegment;
		// Package segment |-->
		final Segment thePacketSegment;
		final MagicBytesSegment theMagicBytes = new MagicBytesSegment( aPacketMagicBytes );
		switch ( _sequenceNumberConcatenateMode ) {
		case APPEND -> thePacketSegment = new SegmentComposite( theMagicBytes, theAllocSegment, _sequenceNumberSegment );
		case PREPEND -> thePacketSegment = new SegmentComposite( theMagicBytes, _sequenceNumberSegment, theAllocSegment );
		default -> throw new UnhandledEnumBugException( _sequenceNumberConcatenateMode );
		}
		// Package segment <--|
		_packetSegment = _packetSegmentPackager.toPackaged( thePacketSegment );
		_packetLength = _packetSegment.getLength();

	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int available() throws IOException {
		if ( _isClosed ) {
			throw new IOException( "The stream has already been closed!" );
		}
		final int theAvaialbel = super.available();
		final int theBlocks = theAvaialbel % _packetLength;
		final int theBlocksSize = theBlocks * _blockSize;
		int theLastBlockSize = ( theAvaialbel - ( theBlocks * _packetLength ) );
		final int thePackageOverhead = _packetSegment.getLength() - _blockSize - _sequenceNumberWidth;
		if ( theLastBlockSize >= _sequenceNumberWidth + thePackageOverhead ) {
			theLastBlockSize = theLastBlockSize - _sequenceNumberWidth - thePackageOverhead;
		}
		return theBlocksSize + theLastBlockSize;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void close() throws IOException {
		_isClosed = true;
		super.close();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getBlockSize() {
		return _blockSize;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Endianess getEndianess() {
		return _endianess;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte[] getPacketMagicBytes() {
		return _packetMagicBytes;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public SegmentPackager getPacketSegmentPackager() {
		return _packetSegmentPackager;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getPacketSize() {
		return _packetLength;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSequenceNumber() {
		return _sequenceNumber;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ConcatenateMode getSequenceNumberConcatenateMode() {
		return _sequenceNumberConcatenateMode;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSequenceNumberInitValue() {
		return _sequenceNumberInitValue;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSequenceNumberWidth() {
		return _sequenceNumberWidth;
	}

	/**
	 * {@inheritDoc}
	 */
	//	@Override
	//	public int read( byte[] b, int off, int len ) throws IOException {
	//		return super.read( b, off, len );
	//	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getPacketLengthWidth() {
		return _packetLengthWidth;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void mark( int readlimit ) {
		throw new UnsupportedOperationException( "A stream of type <" + getClass().getName() + "> does not support this operation!" );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean markSupported() {
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int read() throws IOException {
		if ( _isClosed ) {
			return -1;
		}
		if ( _blockOffset == _blockSize ) {
			final int theAvailable = _inputStream.available();
			try {
				doReceivePacket();
				_blockOffset = 0;
			}
			catch ( IOException e ) {
				if ( theAvailable == 0 ) {
					return -1;
				}
				throw e;
			}
		}
		return _blockOffset >= _allocSegment.getAllocLength() ? -1 : _blockSequence.getByteAt( _blockOffset++ ) & 0xFF;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void reset() throws IOException {
		throw new UnsupportedOperationException( "A stream of type <" + getClass().getName() + "> does not support this operation!" );
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Do receive packet.
	 *
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	protected void doReceivePacket() throws IOException {
		_packetSegment.receiveFrom( _inputStream );
		_sequenceNumber = _sequenceNumberSegment.getPayload().intValue();
		// Transmission post-processing |-->
		_blockOffset = 0;
		// Transmission post-processing <--|
	}

	// /////////////////////////////////////////////////////////////////////////
	// HOOKS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * To packet segment packager.
	 *
	 * @param aCrcAlgorithm the crc algorithm
	 * @param aCrcChecksumConcatenateMode the crc checksum concatenate mode
	 * @param aChecksumValidationMode the checksum validation mode
	 * @param aEndianess the endianess
	 * 
	 * @return the segment packager
	 */
	static SegmentPackager toPacketSegmentPackager( CrcAlgorithm aCrcAlgorithm, ConcatenateMode aCrcChecksumConcatenateMode, ChecksumValidationMode aChecksumValidationMode, Endianess aEndianess ) {
		if ( aCrcAlgorithm != null || aChecksumValidationMode != null || aCrcChecksumConcatenateMode != null ) {
			final CrcAlgorithm theCrcAlgorithm = aCrcAlgorithm != null ? aCrcAlgorithm : TransmissionMetrics.DEFAULT_CRC_ALGORITHM;
			final ChecksumValidationMode theCrcChecksumValidationMode = aChecksumValidationMode != null ? aChecksumValidationMode : TransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE;
			final ConcatenateMode theCrcChecksumConcatenateMode = aCrcChecksumConcatenateMode != null ? aCrcChecksumConcatenateMode : TransmissionMetrics.DEFAULT_CRC_CHECKSUM_CONCATENATE_MODE;
			final Endianess theEndianess = aEndianess != null ? aEndianess : TransmissionMetrics.DEFAULT_ENDIANESS;
			return new CrcSegmentPackager( theCrcAlgorithm, theCrcChecksumConcatenateMode, theCrcChecksumValidationMode, theEndianess );
		}
		return new DummySegmentPackager();
	}

	// /////////////////////////////////////////////////////////////////////////
	// INNER CLASSES:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Builder to build {@link PacketInputStream} instances.
	 */
	public static class Builder implements SequenceNumberInitValueBuilder, SequenceNumberWidthBuilder, BlockSizeBuilder, InputStreamBuilder, EndianessBuilder, SequenceNumberConcatenateModeBuilder, PacketSegmentPackagerBuilder, CrcAlgorithmBuilder, ChecksumValidationModeBuilder, CrcChecksumConcatenateModeBuilder, PacketLengthWidthBuilder, PacketMagicBytesBuilder {

		int blockSize = TransmissionMetrics.DEFAULT_BLOCK_SIZE;
		CrcAlgorithm crcAlgorithm = null;
		ConcatenateMode crcChecksumConcatenateMode = null;
		ChecksumValidationMode crcChecksumValidationMode = null;
		Endianess endianess = TransmissionMetrics.DEFAULT_ENDIANESS;
		InputStream inputStream = null;
		byte[] packetMagicBytes = TransmissionMetrics.DEFAULT_PACKET_MAGIC_BYTES;
		SegmentPackager packetSegmentPackager = null;
		ConcatenateMode sequenceNumberConcatenateMode = TransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_CONCATENATE_MODE;
		int sequenceNumberInitValue = TransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE;
		int sequenceNumberWidth = TransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH;
		int truncateLengthWidth = TransmissionMetrics.DEFAULT_LENGTH_WIDTH;

		/**
		 * Instantiates a new builder.
		 */
		Builder() {}

		/**
		 * Returns the {@link PacketInputStream} instance build according to the
		 * {@link Builder} configuration.
		 *
		 * @return The accordingly configured {@link PacketInputStream}.
		 */
		public PacketInputStream build() {
			return new PacketInputStream( this );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withBlockSize( int aBlockSize ) {
			blockSize = aBlockSize;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withCrcAlgorithm( CrcAlgorithm aCrcAlgorithm ) {
			crcAlgorithm = aCrcAlgorithm;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withCrcChecksumConcatenateMode( ConcatenateMode aCrcChecksumConcatenateMode ) {
			crcChecksumConcatenateMode = aCrcChecksumConcatenateMode;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withChecksumValidationMode( ChecksumValidationMode aChecksumValidationMode ) {
			crcChecksumValidationMode = aChecksumValidationMode;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withEndianess( Endianess aEndianess ) {
			endianess = aEndianess;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withInputStream( InputStream aInputStream ) {
			inputStream = aInputStream;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withPacketMagicBytes( byte[] aPacketMagicBytes ) {
			packetMagicBytes = aPacketMagicBytes;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withPacketSegmentPackager( SegmentPackager aPacketSegmentPackager ) {
			packetSegmentPackager = aPacketSegmentPackager;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withSequenceNumberConcatenateMode( ConcatenateMode aSequenceNumberConcatenateMode ) {
			sequenceNumberConcatenateMode = aSequenceNumberConcatenateMode;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withSequenceNumberInitValue( int aSequenceNumberInitValue ) {
			sequenceNumberInitValue = aSequenceNumberInitValue;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withSequenceNumberWidth( int aSequenceNumberWidth ) {
			sequenceNumberWidth = aSequenceNumberWidth;
			return this;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Builder withPacketLengthWidth( int aPacketLengthWidth ) {
			truncateLengthWidth = aPacketLengthWidth;
			return this;
		}

		/**
		 * Inferences the packet {@link SegmentPackager}. In case one is
		 * available as of {@link #getPacketSegmentPackager()}, then that is
		 * returned. Else CRC settings are evaluated and if possible sufficient
		 * CRC settings are available, a {@link CrcSegmentPackager} is returned.
		 * If there are no sufficient CRC settings, then a
		 * {@link DummySegmentPackager} is returned.
		 * 
		 * @return An interferenced {@link SegmentPackager} as of the instance's
		 *         properties.
		 */
		SegmentPackager toPacketSegmentPackager() {
			if ( packetSegmentPackager != null ) {
				return packetSegmentPackager;
			}
			return PacketOutputStream.toPacketSegmentPackager( crcAlgorithm, crcChecksumConcatenateMode, crcChecksumValidationMode, endianess );
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy