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

net.sf.jrtps.RTPSReader Maven / Gradle / Ivy

package net.sf.jrtps;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import net.sf.jrtps.builtin.WriterData;
import net.sf.jrtps.message.AckNack;
import net.sf.jrtps.message.Data;
import net.sf.jrtps.message.Heartbeat;
import net.sf.jrtps.message.Message;
import net.sf.jrtps.types.EntityId_t;
import net.sf.jrtps.types.GUID_t;
import net.sf.jrtps.types.GuidPrefix_t;
import net.sf.jrtps.types.SequenceNumberSet;
import net.sf.jrtps.types.Time_t;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RTPSReader implements RTPS Reader endpoint functionality.
 * RTPSReader does not store any data received. It only keeps track of data
 * entries sent by writers and propagates received data to SampleListeners registered.
 * 
 * @author mcr70
 * @see SampleListener
 */
public class RTPSReader extends Endpoint {
	private static final Logger logger = LoggerFactory.getLogger(RTPSReader.class);

	private HashSet matchedWriters = new HashSet<>();
	private HashMap writerProxies = new HashMap<>();

	private List> sampleListeners = new LinkedList>();

	private int ackNackCount = 0;
	private Marshaller marshaller;

	private List> pendingSamples = new LinkedList<>();

	public RTPSReader(GuidPrefix_t prefix, EntityId_t entityId, String topicName, Marshaller marshaller, Configuration configuration) {
		super(prefix, entityId, topicName, configuration);

		this.marshaller = marshaller;
	}


	/**
	 * Adds a SampleListener to this RTPSReader.
	 * 
	 * @param listener SampleListener to add.
	 */
	public void addListener(SampleListener listener) {
		logger.debug("Adding SampleListener {} for topic {}", listener, getTopicName());
		sampleListeners.add(listener);
	}

	/**
	 * Removes a SampleListener from this RTPSReader.
	 * 
	 * @param listener SampleListener to remove
	 */
	public void removeListener(SampleListener listener) {
		logger.debug("Removing SampleListener {} from topic {}", listener, getTopicName());
		sampleListeners.remove(listener);
	}

	/**
	 * Handle incoming Data message.
	 * 
	 * @param sourcePrefix GuidPrefix of the remote participant sending Data message 
	 * @param data
	 * @param timestamp
	 * @throws IOException
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	void createSample(GuidPrefix_t sourcePrefix, Data data, Time_t timestamp) throws IOException {

		GUID_t writerGuid = new GUID_t(sourcePrefix, data.getWriterId()); 

		WriterProxy wp = getWriterProxy(writerGuid);

		if (wp.acceptData(data.getWriterSequenceNumber())) {
			Object obj = marshaller.unmarshall(data.getDataEncapsulation());
			logger.debug("[{}] Got Data: {}, {}", getGuid().entityId, 
					obj.getClass().getSimpleName(), data.getWriterSequenceNumber());

			synchronized (pendingSamples) {
				pendingSamples.add(new Sample(obj, timestamp, data.getStatusInfo()));	
			}
		}
		else {
			logger.trace("[{}] Data was rejected: Data seq-num={}, proxy seq-num={}", getGuid().entityId, 
					data.getWriterSequenceNumber(), wp.getSeqNumMax());
		}
	}

	/**
	 * Handle incoming HeartBeat message.
	 * 
	 * @param senderGuidPrefix
	 * @param hb
	 */
	void onHeartbeat(GuidPrefix_t senderGuidPrefix, Heartbeat hb) {
		logger.debug("[{}] Got Heartbeat: {}-{}", getGuid().entityId, hb.getFirstSequenceNumber(), hb.getLastSequenceNumber());
		boolean doSend = false;
		if (!hb.finalFlag()) { // if the FinalFlag is not set, then the Reader must send an AckNack
			doSend = true;
		}
		else {
			WriterProxy wp = getWriterProxy(new GUID_t(senderGuidPrefix, hb.getWriterId()));
			if (wp.acceptHeartbeat(hb.getLastSequenceNumber())) {
				doSend = true;
			}
			else {
				logger.trace("Will no send AckNack, since my seq-num is {} and Heartbeat seq-num is {}", wp.getSeqNumMax(), hb.getLastSequenceNumber());
			}
		}

		if (doSend) {
			Message m = new Message(getGuid().prefix);
			//AckNack an = createAckNack(new GUID_t(senderGuidPrefix, hb.getWriterId()), hb.getFirstSequenceNumber().getAsLong(), hb.getLastSequenceNumber().getAsLong());
			AckNack an = createAckNack(new GUID_t(senderGuidPrefix, hb.getWriterId()));
			m.addSubMessage(an);
			logger.debug("[{}] Sending AckNack: {}", getGuid().entityId, an.getReaderSNState());
			sendMessage(m, senderGuidPrefix);
		}
	}


	private AckNack createAckNack(GUID_t writerGuid) {
		// This is a simple AckNack, that can be optimized if store
		// out-of-order data samples in a separate cache.

		WriterProxy wp = getWriterProxy(writerGuid);
		long seqNumFirst = wp.getSeqNumMax(); // Positively ACK all that we have..
		int[] bitmaps = new int[] {-1}; // Negatively ACK rest

		SequenceNumberSet snSet = new SequenceNumberSet(seqNumFirst+1, bitmaps);

		AckNack an = new AckNack(getGuid().entityId, writerGuid.entityId, snSet, ackNackCount++);

		return an;
	}

	private WriterProxy getWriterProxy(GUID_t writerGuid) {
		WriterProxy wp = writerProxies.get(writerGuid);;
		if (wp == null) {
			wp = new WriterProxy(writerGuid);
			writerProxies.put(writerGuid, wp);
		}

		return wp;
	}

	/**
	 * Get the BuiltinEndpointSet ID of this RTPSReader.
	 * 
	 * @return 0, if this RTPSReader is not builtin endpoint
	 */
	int endpointSetId() {
		return getGuid().entityId.getEndpointSetId();
	}

	public void close() {
		// TODO: No use for this
	}

	void addMatchedWriter(WriterData writerData) {
		matchedWriters.add(writerData);
	}
	void removeMatchedWriter(WriterData writerData) {
		matchedWriters.remove(writerData);
	}


	/**
	 * Releases pending samples.
	 */
	void releasePendingSamples() {
		// TODO: pending samples need to be handled differently.
		//       Maybe have a flag that tells if there is more pending samples
		//       at the end of the method.
		LinkedList> ll = new LinkedList<>();

		synchronized(pendingSamples) {
			ll.addAll(pendingSamples);
			pendingSamples.clear();
		}

		if (ll.size() > 0) {
			for (SampleListener sl: sampleListeners) {
				sl.onSamples(ll);
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy