/* * This file is protected by Copyright. Please refer to the COPYRIGHT file * distributed with this source distribution. * * This file is part of REDHAWK bulkioInterfaces. * * REDHAWK bulkioInterfaces is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * REDHAWK bulkioInterfaces is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package bulkio; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.log4j.Logger; import BULKIO.PrecisionUTCTime; import BULKIO.StreamSRI; public abstract class OutDataPort<E extends BULKIO.updateSRIOperations,A> extends OutPortBase<E> { /** * Size of a single element */ protected final int sizeof; protected OutDataPort(String portName, Logger logger, ConnectionEventListener connectionListener, int size) { super(portName, logger, connectionListener); this.sizeof = size; } /** * Connects a port to receive data from this port. */ public void connectPort(final org.omg.CORBA.Object connection, final String connectionId) throws CF.PortPackage.InvalidPort, CF.PortPackage.OccupiedPort { if (logger != null) { logger.trace("bulkio.OutPort connectPort ENTER (port=" + name +")"); } synchronized (this.updatingPortsLock) { final E port; try { port = this.narrow(connection); } catch (final Exception ex) { if (logger != null) { logger.error("bulkio.OutPort CONNECT PORT: " + name + " PORT NARROW FAILED"); } throw new CF.PortPackage.InvalidPort((short)1, "Invalid port for connection '" + connectionId + "'"); } this.outConnections.put(connectionId, port); this.active = true; this.stats.put(connectionId, new linkStatistics(this.name, this.sizeof)); if (logger != null) { logger.debug("bulkio.OutPort CONNECT PORT: " + name + " CONNECTION '" + connectionId + "'"); } } if (callback != null) { callback.connect(connectionId); } if (logger != null) { logger.trace("bulkio.OutPort connectPort EXIT (port=" + name +")"); } } /** * Breaks a connection. */ public void disconnectPort(String connectionId) { if (logger != null) { logger.trace("bulkio.OutPort disconnectPort ENTER (port=" + name +")"); } synchronized (this.updatingPortsLock) { final E port = this.outConnections.remove(connectionId); if (port != null) { // Create an empty data packet with an invalid timestamp to // send with the end-of-stream final A data = emptyArray(); final BULKIO.PrecisionUTCTime tstamp = bulkio.time.utils.notSet(); for (Map.Entry<String, SriMapStruct> entry: this.currentSRIs.entrySet()) { final String streamID = entry.getKey(); final SriMapStruct sriMap = entry.getValue(); if (sriMap.connections.contains(connectionId)) { try { sendPacket(port, data, tstamp, true, streamID); } catch(Exception e) { if (logger != null) { logger.error("Call to pushPacket failed on port " + name + " connection " + connectionId); } } } } } this.stats.remove(connectionId); this.active = (this.outConnections.size() != 0); // Remove connectionId from any sets in the currentSRIs.connections values for (Map.Entry<String,SriMapStruct> entry : this.currentSRIs.entrySet()) { entry.getValue().connections.remove(connectionId); } if (logger != null) { logger.trace("bulkio.OutPort DISCONNECT PORT:" + name + " CONNECTION '" + connectionId + "'"); for(Map.Entry<String,SriMapStruct> entry: this.currentSRIs.entrySet()) { logger.trace("bulkio.OutPort updated currentSRIs key=" + entry.getKey() + ", value.sri=" + entry.getValue().sri + ", value.connections=" + entry.getValue().connections); } } } if (callback != null) { callback.disconnect(connectionId); } if (logger != null) { logger.trace("bulkio.OutPort disconnectPort EXIT (port=" + name +")"); } } /** * Sends an array of samples. */ protected void pushPacket(A data, PrecisionUTCTime time, boolean endOfStream, String streamID) { if (logger != null) { logger.trace("bulkio.OutPort pushPacket ENTER (port=" + this.name +")"); } synchronized(this.updatingPortsLock) { if (!this.currentSRIs.containsKey(streamID)) { StreamSRI header = bulkio.sri.utils.create(); header.streamID = streamID; this.pushSRI(header); } pushPacketData(data, time, endOfStream, streamID); } if (logger != null) { logger.trace("bulkio.OutPort pushPacket EXIT (port=" + this.name +")"); } } protected void pushPacketData(A data, PrecisionUTCTime time, boolean endOfStream, String streamID) { pushSinglePacket(data, time, endOfStream, streamID); } /** * Sends out SRI describing the data payload. * * H: structure of type BULKIO.StreamSRI with the SRI for this stream * hversion * xstart: start time of the stream * xdelta: delta between two samples * xunits: unit types from Platinum specification * subsize: 0 if the data is one-dimensional * ystart * ydelta * yunits: unit types from Platinum specification * mode: 0-scalar, 1-complex * streamID: stream identifier * sequence<CF::DataType> keywords: unconstrained sequence of key-value pairs for additional description */ public void pushSRI(StreamSRI header) { if (logger != null) { logger.trace("bulkio.OutPort pushSRI ENTER (port=" + name +")"); } // Header cannot be null if (header == null) { if (logger != null) { logger.trace("bulkio.OutPort pushSRI EXIT (port=" + name +")"); } return; } // Null streamID is a usage error if (header.streamID == null) { throw new NullPointerException("SRI streamID cannot be null"); } // Header cannot have null keywords if (header.keywords == null) { header.keywords = new CF.DataType[0]; } synchronized (this.updatingPortsLock) { this.currentSRIs.put(header.streamID, new SriMapStruct(header)); if (this.active) { for (Entry<String,E> entry : this.outConnections.entrySet()) { final String connectionID = entry.getKey(); // Check filter for route if (!isStreamRoutedToConnection(header.streamID, connectionID)) { continue; } final E port = entry.getValue(); try { port.pushSRI(header); // Update entry in currentSRIs this.currentSRIs.get(header.streamID).connections.add(connectionID); this.updateStats(connectionID); } catch (Exception e) { if ( this.reportConnectionErrors(connectionID)) { if (this.logger != null) { logger.error("Call to pushSRI failed on port " + name + " connection " + connectionID); } } } } } } if (logger != null) { logger.trace("bulkio.OutPort pushSRI EXIT (port=" + name +")"); } } protected void pushSinglePacket(A data, PrecisionUTCTime time, boolean endOfStream, String streamID) { final int length = arraySize(data); SriMapStruct sriStruct = this.currentSRIs.get(streamID); if (this.active) { for (Entry<String,E> entry : this.outConnections.entrySet()) { final String connectionID = entry.getKey(); // Check filter for route if (!isStreamRoutedToConnection(streamID, connectionID)) { continue; } final E port = entry.getValue(); try { // If SRI for given streamID has not been pushed to this connection, push it if (!sriStruct.connections.contains(connectionID)) { port.pushSRI(sriStruct.sri); sriStruct.connections.add(connectionID); } this.sendPacket(port, data, time, endOfStream, streamID); this.stats.get(connectionID).update(length, (float)0.0, endOfStream, streamID, false); } catch (Exception e) { if ( this.reportConnectionErrors(connectionID)) { if ( this.logger != null ) { logger.error("Call to pushPacket failed on port " + name + " connection " + connectionID); } } } } } if (endOfStream) { if (this.currentSRIs.containsKey(streamID)) { this.currentSRIs.remove(streamID); } } return; } protected abstract E narrow(org.omg.CORBA.Object obj); protected abstract void sendPacket(E port, A data, PrecisionUTCTime time, boolean endOfStream, String streamID); protected abstract int arraySize(A array); protected abstract A emptyArray(); }