
org.refcodes.serial.StopAndWaitPacketInputStream 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 java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.refcodes.controlflow.RetryCounter;
import org.refcodes.io.SkipAvailableInputStream;
import org.refcodes.mixin.ConcatenateMode;
import org.refcodes.numerical.ChecksumValidationMode;
import org.refcodes.numerical.CrcAlgorithm;
import org.refcodes.numerical.Endianess;
import org.refcodes.serial.OutputReturnStreamAccessor.OutputReturnStreamBuilder;
import org.refcodes.serial.SegmentPackager.DummySegmentPackager;
/**
* The {@link StopAndWaitPacketInputStream} 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 StopAndWaitPacketInputStream} 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 StopAndWaitPacketInputStream extends PacketInputStream implements AcknowledgeRetryNumberAccessor, AcknowledgeTimeoutMillisAccessor, AcknowledgeMagicBytesAccessor, AcknowledgeSegmentPackagerAccessor {
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
private static final Logger LOGGER = Logger.getLogger( StopAndWaitPacketInputStream.class.getName() );
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private static final boolean IS_DEBUG = false;
byte[] _acknowledgeMagicBytes;
int _acknowledgeRetryNumber;
Segment _acknowledgeSegment;
SegmentPackager _acknowledgeSegmentPackager;
NumberSegment _acknowledgeSequenceNumberSegment;
long _acknowledgeTimeoutInMs;
OutputStream _returnStream;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Creates builder to build {@link StopAndWaitPacketInputStream}.
*
* @return created builder
*/
public static Builder builder() {
return new Builder();
}
private StopAndWaitPacketInputStream( Builder aBuilder ) {
this( aBuilder.inputStream, aBuilder.blockSize, aBuilder.truncateLengthWidth, aBuilder.packetMagicBytes, aBuilder.sequenceNumberInitValue, aBuilder.sequenceNumberWidth, aBuilder.sequenceNumberConcatenateMode, aBuilder.toPacketSegmentPackager(), aBuilder.returnStream, aBuilder.acknowledgeMagicBytes, aBuilder.acknowledgeRetryNumber, aBuilder.acknowledgeTimeoutInMs, aBuilder.toAckSegmentPackager(), aBuilder.endianess );
}
// -------------------------------------------------------------------------
/**
* Constructs an according {@link StopAndWaitPacketInputStream} instance
* wrapping the given {@link OutputStream}. 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 aReturnStream The {@link OutputStream} for establishing a return
* channel (handshake with the communication partner).
* @param aTransmissionMetrics The {@link TransmissionMetrics} to be used
* for configuring this instance.
*/
public StopAndWaitPacketInputStream( InputStream aInputStream, OutputStream aReturnStream, TransmissionMetrics aTransmissionMetrics ) {
this( aInputStream, aTransmissionMetrics.getBlockSize(), aTransmissionMetrics.getPacketLengthWidth(), aTransmissionMetrics.getPacketMagicBytes(), aTransmissionMetrics.getSequenceNumberInitValue(), aTransmissionMetrics.getSequenceNumberWidth(), aTransmissionMetrics.getSequenceNumberConcatenateMode(), aTransmissionMetrics.toPacketSegmentPackager(), aReturnStream, aTransmissionMetrics.getAcknowledgeMagicBytes(), aTransmissionMetrics.getAcknowledgeRetryNumber(), aTransmissionMetrics.getAcknowledgeTimeoutMillis(), aTransmissionMetrics.toAckSegmentPackager(), aTransmissionMetrics.getEndianess() );
}
// -------------------------------------------------------------------------
/**
* Constructs an according {@link StopAndWaitPacketInputStream} instance
* wrapping the given {@link OutputStream}.
*
* @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 aReturnStream The {@link OutputStream} for establishing a return
* channel (handshake with the communication partner).
* @param aAcknowledgeMagicBytes The ACK character(s) to be used by the
* return channel to transmit an ACK (acknowledge) response after
* successful receiving a transmission.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aAckSegmentPackager An (optional) {@link SegmentPackager} used to
* modify a ACK response data e.g. with a CRC checksum.
* @param aEndianess The {@link Endianess} to use for integer (double)
* numbers and the like.
*/
public StopAndWaitPacketInputStream( InputStream aInputStream, int aBlockSize, int aPacketLengthWidth, byte[] aPacketMagicBytes, int aSequenceNumberInitValue, int aSequenceNumberWidth, ConcatenateMode aSequenceNumberConcatenateMode, SegmentPackager aPacketSegmentPackager, OutputStream aReturnStream, byte[] aAcknowledgeMagicBytes, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, SegmentPackager aAckSegmentPackager, Endianess aEndianess ) {
super( aInputStream, aBlockSize, aPacketLengthWidth, aPacketMagicBytes, aSequenceNumberInitValue, aSequenceNumberWidth, aSequenceNumberConcatenateMode, aPacketSegmentPackager, aEndianess );
_returnStream = aReturnStream;
_acknowledgeMagicBytes = aAcknowledgeMagicBytes;
_acknowledgeRetryNumber = aAcknowledgeRetryNumber;
_acknowledgeTimeoutInMs = aAcknowledgeTimeoutInMs;
_acknowledgeSegmentPackager = aAckSegmentPackager != null ? aAckSegmentPackager : new DummySegmentPackager();
_acknowledgeSegment = _acknowledgeSegmentPackager.toPackaged( new SegmentComposite<>( new MagicBytesSegment( _acknowledgeMagicBytes ), _acknowledgeSequenceNumberSegment = new NumberSegment( aSequenceNumberWidth, aEndianess ) ) );
}
// /////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public byte[] getAcknowledgeMagicBytes() {
return _acknowledgeMagicBytes;
}
/**
* {@inheritDoc}
*/
@Override
public int getAcknowledgeRetryNumber() {
return _acknowledgeRetryNumber;
}
/**
* {@inheritDoc}
*/
@Override
public SegmentPackager getAcknowledgeSegmentPackager() {
return _acknowledgeSegmentPackager;
}
// /////////////////////////////////////////////////////////////////////////
// HOOKS:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public long getAcknowledgeTimeoutMillis() {
return _acknowledgeTimeoutInMs;
}
/**
* {@inheritDoc}
*/
@Override
protected void doReceivePacket() throws IOException {
if ( _returnStream == null ) {
super.doReceivePacket();
return;
}
else {
final RetryCounter theRetries = new RetryCounter( _acknowledgeRetryNumber, _acknowledgeTimeoutInMs );
Exception eException = null;
long eSequenceNumber;
@SuppressWarnings("resource")
final // Do not close me after done!
SkipAvailableInputStream theSkipInputStream = new SkipAvailableInputStream( _inputStream, _acknowledgeTimeoutInMs );
while ( theRetries.nextRetry() ) {
eException = null;
try {
_packetSegment.receiveFrom( _inputStream );
eSequenceNumber = _sequenceNumberSegment.getPayload().intValue();
if ( ( _sequenceNumberInitValue == -1 && _sequenceNumber == 0 ) || eSequenceNumber == _sequenceNumber ) {
// Ack |-->
_acknowledgeSequenceNumberSegment.setPayload( eSequenceNumber );
_acknowledgeSegment.transmitTo( _returnStream );
if ( IS_DEBUG ) {
LOGGER.log( Level.INFO, getClass().getSimpleName() + ": OK = " + _acknowledgeSegment );
}
_sequenceNumber++;
// Ack <--|
// Transmission post-processing |-->
_blockOffset = 0;
// Transmission post-processing <--|
return;
}
else {
if ( IS_DEBUG ) {
LOGGER.log( Level.WARNING, getClass().getSimpleName() + ": FAIL = " + _acknowledgeSegment );
}
}
}
catch ( Exception e ) {
eException = e;
}
if ( theRetries.hasNextRetry() ) {
try {
// theSkipInputStream.skipAvailableFor( _acknowledgeTimeoutInMs );
theSkipInputStream.skipAvailableTillSilenceFor( _acknowledgeTimeoutInMs * 8 / 10 );
}
catch ( IOException ignore ) {}
}
}
if ( eException != null ) {
throw new FlowControlRetryException( _acknowledgeRetryNumber, _acknowledgeTimeoutInMs, "Aborting after <" + _acknowledgeRetryNumber + "> retries with a timeout for each retry of <" + _acknowledgeTimeoutInMs + "> milliseconds: " + eException.getMessage(), eException );
}
else {
throw new FlowControlRetryException( _acknowledgeRetryNumber, _acknowledgeTimeoutInMs, "Aborting after <" + _acknowledgeRetryNumber + "> retries with a timeout for each retry of <" + _acknowledgeTimeoutInMs + "> milliseconds." );
}
}
}
// /////////////////////////////////////////////////////////////////////////
// INNER CLASSES:
// /////////////////////////////////////////////////////////////////////////
/**
* Builder to build {@link StopAndWaitPacketInputStream} instances.
*/
public static class Builder extends PacketInputStream.Builder implements AcknowledgeMagicBytesBuilder, AcknowledgeTimeoutMillisBuilder, AcknowledgeRetryNumberBuilder, OutputReturnStreamBuilder, AcknowledgeSegmentPackagerBuilder {
byte[] acknowledgeMagicBytes = TransmissionMetrics.DEFAULT_ACKNOWLEDGE_MAGIC_BYTES;
int acknowledgeRetryNumber = TransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER;
SegmentPackager acknowledgeSegmentPackager = null;
long acknowledgeTimeoutInMs = TransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS;
OutputStream returnStream = null;
/**
* Instantiates a new builder.
*/
protected Builder() {}
/**
* Returns the {@link StopAndWaitPacketInputStream} instance build
* according to the {@link Builder} configuration.
*
* @return The accordingly configured
* {@link StopAndWaitPacketInputStream}.
*/
@Override
public StopAndWaitPacketInputStream build() {
return new StopAndWaitPacketInputStream( this );
}
/**
* {@inheritDoc}
*/
@Override
public Builder withAcknowledgeMagicBytes( byte[] aAcknowledgeMagicBytes ) {
acknowledgeMagicBytes = aAcknowledgeMagicBytes;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withAcknowledgeRetryNumber( int aAcknowledgeRetryNumber ) {
acknowledgeRetryNumber = aAcknowledgeRetryNumber;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withAcknowledgeSegmentPackager( SegmentPackager aAcknowledgeSegmentPackager ) {
acknowledgeSegmentPackager = aAcknowledgeSegmentPackager;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withAcknowledgeTimeoutMillis( long aAcknowledgeTimeoutInMs ) {
acknowledgeTimeoutInMs = aAcknowledgeTimeoutInMs;
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 withPacketSegmentPackager( SegmentPackager aPacketSegmentPackager ) {
packetSegmentPackager = aPacketSegmentPackager;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withReturnStream( OutputStream aReturnStream ) {
returnStream = aReturnStream;
return this;
}
/**
* Inferences the ACK {@link SegmentPackager}. In case one is available
* as of {@link #getAcknowledgeSegmentPackager()}, 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 toAckSegmentPackager() {
if ( acknowledgeSegmentPackager != null ) {
return acknowledgeSegmentPackager;
}
if ( crcAlgorithm != null || crcChecksumValidationMode != null ) {
final CrcAlgorithm theCrcAlgorithm = crcAlgorithm != null ? crcAlgorithm : TransmissionMetrics.DEFAULT_CRC_ALGORITHM;
final ChecksumValidationMode theCrcChecksumValidationMode = crcChecksumValidationMode != null ? crcChecksumValidationMode : TransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE;
final ConcatenateMode theCrcChecksumConcatenateMode = crcChecksumConcatenateMode != null ? crcChecksumConcatenateMode : TransmissionMetrics.DEFAULT_CRC_CHECKSUM_CONCATENATE_MODE;
final Endianess theEndianess = endianess != null ? endianess : TransmissionMetrics.DEFAULT_ENDIANESS;
return new CrcSegmentPackager( theCrcAlgorithm, theCrcChecksumConcatenateMode, theCrcChecksumValidationMode, theEndianess );
}
return new DummySegmentPackager();
}
/**
* 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.
*/
@Override
SegmentPackager toPacketSegmentPackager() {
if ( packetSegmentPackager != null ) {
return packetSegmentPackager;
}
if ( crcAlgorithm != null || crcChecksumValidationMode != null ) {
final CrcAlgorithm theCrcAlgorithm = crcAlgorithm != null ? crcAlgorithm : TransmissionMetrics.DEFAULT_CRC_ALGORITHM;
final ChecksumValidationMode theCrcChecksumValidationMode = crcChecksumValidationMode != null ? crcChecksumValidationMode : TransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE;
final ConcatenateMode theCrcChecksumConcatenateMode = crcChecksumConcatenateMode != null ? crcChecksumConcatenateMode : TransmissionMetrics.DEFAULT_CRC_CHECKSUM_CONCATENATE_MODE;
final Endianess theEndianess = endianess != null ? endianess : TransmissionMetrics.DEFAULT_ENDIANESS;
return new CrcSegmentPackager( theCrcAlgorithm, theCrcChecksumConcatenateMode, theCrcChecksumValidationMode, theEndianess );
}
return new DummySegmentPackager();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy