
org.refcodes.serial.PacketInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-serial Show documentation
Show all versions of refcodes-serial Show documentation
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