/*******************************************************************************
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* All rights reserved. This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package gov.redhawk.bulkio.util;
import gov.redhawk.sca.util.Debug;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import BULKIO.PortStatistics;
import BULKIO.PortUsageType;
import BULKIO.PrecisionUTCTime;
import BULKIO.ProvidesPortStatisticsProviderOperations;
import BULKIO.StreamSRI;
import BULKIO.updateSRIOperations;
import CF.DataType;
/**
*
*/
public abstract class AbstractBulkIOPort implements ProvidesPortStatisticsProviderOperations, updateSRIOperations {
private static final Debug TRACE_LOG = new Debug(BulkIOUtilActivator.getDefault(), AbstractBulkIOPort.class.getSimpleName());
private final Map<String, StreamSRI> streamSRIMap = Collections.synchronizedMap(new HashMap<String, StreamSRI>());
private StreamSRI currentSri;
private final PortStatistics stats = new PortStatistics();
private AtomicLong lastWrite = new AtomicLong(-1);
private AtomicLong lastUpdate = new AtomicLong(-1);
private AtomicInteger numElements = new AtomicInteger();
private AtomicInteger numCalls = new AtomicInteger();
private BulkIOType type;
{
stats.callsPerSecond = -1;
stats.elementsPerSecond = -1;
stats.timeSinceLastCall = -1;
stats.bitsPerSecond = -1;
stats.keywords = new DataType[0];
stats.portName = "port_" + System.getProperty("user.name", "user") + "_" + System.currentTimeMillis();
stats.streamIDs = new String[0];
}
protected AbstractBulkIOPort() {
}
/**
* @since 2.0
*/
protected AbstractBulkIOPort(BulkIOType type) {
this.type = type;
}
/**
* @since 2.0
*/
@NonNull
public BulkIOType getBulkIOType() {
return type;
}
/**
* @since 2.0
*/
public void setBulkIOType(@NonNull BulkIOType type) {
this.type = type;
}
/**
* Call this method to update the port statistics, should only need to be called from the statistics method itself
*/
private synchronized void updateStatitics() {
long currentTime = System.currentTimeMillis();
long oldLastUpdate = this.lastUpdate.getAndSet(currentTime);
if (oldLastUpdate == -1) {
return;
}
long delta = Math.max(1, currentTime - oldLastUpdate);
stats.callsPerSecond = this.numCalls.floatValue() / delta * 1000f; // SUPPRESS CHECKSTYLE MAGIC NUMBER
this.numCalls.set(0);
stats.elementsPerSecond = this.numElements.floatValue() / delta * 1000f; // SUPPRESS CHECKSTYLE MAGIC NUMBER
this.numElements.set(0);
if (this.lastWrite.get() > 0) {
stats.timeSinceLastCall = Math.max(0, currentTime - this.lastWrite.get());
} else {
stats.timeSinceLastCall = -1;
}
int bpa = (type != null) ? type.getBytePerAtom() : 1; // SUPPRESS CHECKSTYLE AvoidInline
stats.bitsPerSecond = bpa * stats.elementsPerSecond * 8; // SUPPRESS CHECKSTYLE MAGIC NUMBER
}
/**
* Call this method every time a push packet is received
* @param length Length of the push packet array.
* @return true if should process packet
*/
protected boolean pushPacket(int length, @Nullable final PrecisionUTCTime time, final boolean endOfStream, @Nullable final String streamID) {
if (endOfStream) {
// Process last packet sent
this.streamSRIMap.remove(streamID);
} else if (getSri(streamID) == null) {
return false;
}
this.lastUpdate.compareAndSet(-1, System.currentTimeMillis());
this.lastWrite.set(System.currentTimeMillis());
this.numElements.addAndGet(length);
this.numCalls.incrementAndGet();
return true;
}
@Override
@NonNull
public PortUsageType state() {
return PortUsageType.ACTIVE;
}
@NonNull
public PortStatistics getPortStatistics() {
return stats;
}
@Override
@NonNull
public PortStatistics statistics() {
updateStatitics();
return stats;
}
@Override
@NonNull
public StreamSRI[] activeSRIs() {
return this.streamSRIMap.values().toArray(new StreamSRI[this.streamSRIMap.size()]);
}
/**
* sub-class should override the {@link #handleStreamSRIChanged(String, StreamSRI, StreamSRI)} method.
* @since 2.0
*/
@Override
public final void pushSRI(@Nullable StreamSRI sri) {
if (AbstractBulkIOPort.TRACE_LOG.enabled) {
AbstractBulkIOPort.TRACE_LOG.enteringMethod(StreamSRIUtil.toString(sri));
}
if (sri != null) {
String streamId = sri.streamID;
if (streamId != null) {
StreamSRI oldSri = this.streamSRIMap.put(streamId, sri);
if (!StreamSRIUtil.equals(oldSri, sri)) {
handleStreamSRIChanged(streamId, oldSri, sri);
}
}
}
StreamSRI oldSri = this.currentSri;
this.currentSri = sri;
if (!StreamSRIUtil.equals(oldSri, sri)) {
handleStreamSRIChanged(oldSri, sri);
}
AbstractBulkIOPort.TRACE_LOG.exitingMethod(sri);
}
/**
* @since 2.0
*/
@Nullable
public StreamSRI getStreamSRI() {
return this.currentSri;
}
/** callback to notify that SRI has changed from previous SRI.
* @deprecated use {@link #handleStreamSRIChanged(String, StreamSRI, StreamSRI)}
*/
@Deprecated
protected void handleStreamSRIChanged(@Nullable StreamSRI oldSri, @Nullable StreamSRI newSri) {
}
/** callback to notify that SRI has changed for specified streamID (this is method that sub-classes should override).
* @since 2.0*/
protected void handleStreamSRIChanged(@NonNull String streamID, @Nullable StreamSRI oldSri, @NonNull StreamSRI newSri) {
}
/**
* @param streamID
* @return SRI for specified stream ID (null if it does not exist or has reached end-of-stream (EOS)).
*/
@Nullable
public StreamSRI getSri(@Nullable String streamID) {
return this.streamSRIMap.get(streamID);
}
}