
org.refcodes.serial.StopAndWaitPacketOutputStream 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.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.refcodes.controlflow.RetryCounter;
import org.refcodes.data.IoTimeout;
import org.refcodes.io.SkipAvailableInputStream;
import org.refcodes.io.TimeoutInputStream;
import org.refcodes.mixin.ConcatenateMode;
import org.refcodes.numerical.ChecksumValidationMode;
import org.refcodes.numerical.CrcAlgorithm;
import org.refcodes.numerical.Endianess;
import org.refcodes.serial.InputReturnStreamAccessor.InputReturnStreamBuilder;
import org.refcodes.serial.SegmentPackager.DummySegmentPackager;
/**
* The {@link StopAndWaitPacketOutputStream} wraps an {@link OutputStream} and
* chunks any data to be written into packets with a sequence number, a block of
* data and a CRC checksum. An according {@link StopAndWaitPacketInputStream}
* then reverts the packetised data stream while performing CRC checksum
* validation as well as sequence number validation.
*/
public class StopAndWaitPacketOutputStream extends PacketOutputStream implements AcknowledgeRetryNumberAccessor, AcknowledgeTimeoutMillisAccessor, AcknowledgeMagicBytesAccessor, AcknowledgeSegmentPackagerAccessor {
// /////////////////////////////////////////////////////////////////////////
// STATCIS:
// /////////////////////////////////////////////////////////////////////////
private static final Logger LOGGER = Logger.getLogger( StopAndWaitPacketInputStream.class.getName() );
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private static final boolean IS_DEBUG = false;
private byte[] _acknowledgeMagicBytes;
private MagicBytesSegment _acknowledgeMagicBytesSegment;
private int _acknowledgeRetryNumber;
private Segment _acknowledgeSegment;
private SegmentPackager _acknowledgeSegmentPackager;
private NumberSegment _acknowledgeSequenceNumberSegment;
private long _acknowledgeTimeoutInMs;
private InputStream _returnStream;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Creates builder to build {@link StopAndWaitPacketOutputStream}.
*
* @return created builder
*/
public static Builder builder() {
return new Builder();
}
private StopAndWaitPacketOutputStream( Builder aBuilder ) {
this( aBuilder.outputStream, 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 StopAndWaitPacketOutputStream} 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 aOutputStream The {@link OutputStream} to be wrapped.
* @param aReturnStream The {@link InputStream} for establishing a return
* channel (handshake with the communication partner).
* @param aTransmissionMetrics The {@link TransmissionMetrics} to be used
* for configuring this instance.
*/
public StopAndWaitPacketOutputStream( OutputStream aOutputStream, InputStream aReturnStream, TransmissionMetrics aTransmissionMetrics ) {
this( aOutputStream, 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 StopAndWaitPacketOutputStream} instance
* wrapping the given {@link OutputStream}.
*
* @param aOutputStream The {@link OutputStream} to be wrapped.
* @param aBlockSize The block size of a data block for each packet.
* @param aPacketLengthWidth The width (bytes) for the number of bytes to be
* truncated from the last block.
* @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 InputStream} 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 aAckRetryNumber The number of retries waiting for an ACK from the
* return channel.
* @param aAckTimeoutInMs 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 StopAndWaitPacketOutputStream( OutputStream aOutputStream, int aBlockSize, int aPacketLengthWidth, byte[] aPacketMagicBytes, int aSequenceNumberInitValue, int aSequenceNumberWidth, ConcatenateMode aSequenceNumberConcatenateMode, SegmentPackager aPacketSegmentPackager, InputStream aReturnStream, byte[] aAcknowledgeMagicBytes, int aAckRetryNumber, long aAckTimeoutInMs, SegmentPackager aAckSegmentPackager, Endianess aEndianess ) {
super( aOutputStream, aBlockSize, aPacketLengthWidth, aPacketMagicBytes, aSequenceNumberInitValue, aSequenceNumberWidth, aSequenceNumberConcatenateMode, aPacketSegmentPackager, aEndianess );
_returnStream = aReturnStream;
_acknowledgeMagicBytes = aAcknowledgeMagicBytes;
_acknowledgeRetryNumber = aAckRetryNumber;
_acknowledgeTimeoutInMs = aAckTimeoutInMs;
_acknowledgeSegmentPackager = aAckSegmentPackager != null ? aAckSegmentPackager : new DummySegmentPackager();
_acknowledgeSegment = _acknowledgeSegmentPackager.toPackaged( new SegmentComposite<>( _acknowledgeMagicBytesSegment = new MagicBytesSegment( _acknowledgeMagicBytes.length ), _acknowledgeSequenceNumberSegment = new NumberSegment( _sequenceNumberWidth, _endianess ) ) );
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public byte[] getAcknowledgeMagicBytes() {
return _acknowledgeMagicBytes;
}
/**
* {@inheritDoc}
*/
@Override
public int getAcknowledgeRetryNumber() {
return _acknowledgeRetryNumber;
}
/**
* {@inheritDoc}
*/
@Override
public SegmentPackager getAcknowledgeSegmentPackager() {
return _acknowledgeSegmentPackager;
}
/**
* {@inheritDoc}
*/
@Override
public long getAcknowledgeTimeoutMillis() {
return _acknowledgeTimeoutInMs;
}
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
protected void doTransmitPacket() throws IOException {
if ( _returnStream == null ) {
super.doTransmitPacket();
}
else {
if ( _blockOffset != 0 ) {
// Sequence number |-->
_sequenceNumberSegment.setPayload( Long.valueOf( _sequenceNumber ) );
// Sequence number <--|
byte[] eAck;
int eSequenceNumber;
@SuppressWarnings("resource")
final // Do not close me after done!
SkipAvailableInputStream theSkipReturnStream = new SkipAvailableInputStream( _returnStream, _acknowledgeTimeoutInMs );
Exception eException = null;
final RetryCounter theRetries = new RetryCounter( _acknowledgeRetryNumber, _acknowledgeTimeoutInMs );
final TimeoutInputStream theTimeoutReturnStream = SerialUtility.createTimeoutInputStream( _returnStream, _acknowledgeTimeoutInMs );
while ( theRetries.nextRetry() ) {
eException = null;
try {
_packetSegment.transmitTo( _outputStream );
// Ack |-->
_acknowledgeSegment.receiveFrom( theTimeoutReturnStream );
eAck = _acknowledgeMagicBytesSegment.getPayload();
eSequenceNumber = _acknowledgeSequenceNumberSegment.getPayload().intValue();
if ( Arrays.equals( eAck, _acknowledgeMagicBytes ) && eSequenceNumber == _sequenceNumber ) {
if ( IS_DEBUG ) {
LOGGER.log( Level.INFO, getClass().getSimpleName() + ": OK = " + _acknowledgeSegment );
}
// Transmission post-processing |-->
_sequenceNumber++;
_blockSequence.clear();
_blockOffset = 0;
// Transmission post-processing <--|
return;
}
else {
if ( IS_DEBUG ) {
LOGGER.log( Level.WARNING, getClass().getSimpleName() + ": FAIL = " + _acknowledgeSegment );
}
}
// Ack <--|
}
catch ( Exception e ) {
eException = e;
}
if ( theRetries.hasNextRetry() ) {
try {
theSkipReturnStream.skipAvailableWithin( IoTimeout.toTimeoutSleepLoopTimeInMs( _acknowledgeTimeoutInMs ) );
}
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 final class Builder extends PacketOutputStream.Builder implements AcknowledgeMagicBytesBuilder, AcknowledgeTimeoutMillisBuilder, AcknowledgeRetryNumberBuilder, InputReturnStreamBuilder, 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;
InputStream returnStream = null;
private Builder() {}
/**
* Returns the {@link StopAndWaitPacketInputStream} instance build
* according to the {@link Builder} configuration.
*
* @return The accordingly configured
* {@link StopAndWaitPacketInputStream}.
*/
@Override
public StopAndWaitPacketOutputStream build() {
return new StopAndWaitPacketOutputStream( 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 aAckTimeoutInMs ) {
acknowledgeTimeoutInMs = aAckTimeoutInMs;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withReturnStream( InputStream 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();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy