package edu.colostate.vchill.proxy; import edu.colostate.vchill.ChillDefines; import edu.colostate.vchill.ControlMessage; import edu.colostate.vchill.ScaleManager; import edu.colostate.vchill.ViewUtil; import edu.colostate.vchill.cache.CacheMain; import edu.colostate.vchill.chill.*; import java.io.*; import java.net.Socket; import java.util.ArrayList; /** * Sends data to client. * * @author Alexander Deyke * @author Jochen Deyke * @version 2007-09-28 */ class ProxyDataThread implements Runnable { /** * initial type definition scale information is stored here */ private static final ScaleManager sm = ScaleManager.getInstance(); /** * data connection to client */ private Socket clientDataSocket; private DataInputStream clientDataIn; private DataOutputStream clientDataOut; /** * is the connection up? */ private boolean connected = false; /** * status messages */ public enum Status { READY, SENDING, CONDEMNED, } /** * this thread's status */ private Status status = Status.READY; /** * command to process */ private ControlMessage command; /** * shared cache */ private CacheMain cache; /** * the last data type(s) requested (don't need to notify server if same type is wanted) */ private long dataType; private final Control parent; private final Thread thread; private boolean realtime; public ProxyDataThread(final Socket clientDataSocket, final Control parent, final CacheMain cache) throws IOException { this(clientDataSocket, parent, cache, false); } /** * @param clientDataSocket socket to get type requests from and send data to * @param parent Control to get configuration info from * @param cache data storage * @param realtime whether to start at the most current ray or at the beginning */ public ProxyDataThread(final Socket clientDataSocket, final Control parent, final CacheMain cache, final boolean realtime) throws IOException { System.out.println("in constructor"); this.realtime = realtime; this.clientDataSocket = clientDataSocket; this.clientDataIn = new DataInputStream(new BufferedInputStream(clientDataSocket.getInputStream())); this.clientDataOut = new DataOutputStream(new BufferedOutputStream(clientDataSocket.getOutputStream())); this.cache = cache; this.parent = parent; System.out.println("sending scaling info"); do { Proxy.sleep(250); } while (sm.getTypes().size() < 1); //wait for scaling info for (String scale : sm.getTypes()) { System.out.println(scale); sendHeader(sm.getScale(scale)); } this.dataType = clientDataIn.readLong(); this.thread = new Thread(this, "ProxyDataThread"); } /** * Causes this thread to begin execution; the Java Virtual Machine calls the <code>run</code> method of this thread. */ public void start() { this.thread.start(); } /** * Check connection status * * @return true if the connection is active and ready, false if it is not */ public synchronized boolean connected() { if (this.connected == false) return false; if (!(this.clientDataSocket.isConnected())) { try { System.out.println("ProxyDataThread: Socket timed out, disconnecting"); this.disconnect(); } catch (IOException ioe) { throw new Error("Socket PANIC!! - IOException while trying to disconnect: ", ioe); } } return this.connected; } /** * Idle until receiving a condemnation or a command. */ public /*NOT synchronized*/ void listen() throws IOException { while (this.connected()) { switch (this.status) { case READY: Proxy.sleep(100); break; case CONDEMNED: disconnect(); break; case SENDING: doSend(); break; } } } /** * Closes the connection to the server. */ public synchronized void disconnect() throws IOException { System.out.println("ProxyDataThread: Disconnecting."); this.clientDataIn.close(); this.clientDataIn = null; this.clientDataOut.close(); this.clientDataOut = null; this.clientDataSocket.close(); this.clientDataSocket = null; this.connected = false; } /** * Send a ChillMomentHeader to the client. * * @param header header to send */ private void sendHeader(final ChillHeader header) throws IOException { //System.out.println("Sending ChillHeader " + Integer.toHexString(header.header.recordType)); header.write(this.clientDataOut); this.clientDataOut.flush(); } /** * Send a byte array of data to the client. * * @param data data to send */ private void sendData(final byte[] data) throws IOException { this.clientDataOut.write(data); this.clientDataOut.flush(); } /** * Listen, then exit. */ public /*NOT synchronized*/ void run() { try { this.connected = true; System.out.println("ProxyDataThread: Listening on data socket."); listen(); System.out.println("ProxyDataThread: Data socket thread closing."); } catch (IOException ioe) { System.err.println("ProxyDataThread: Caught exception: " + ioe); System.err.println("...disconnecting"); if (this.parent != null) try { this.parent.killConnection(this); } catch (IOException ioe2) { throw new Error(ioe2); } ioe.printStackTrace(); } } /** * Check if there are any data type change requests. Then return data type. * * @return bits of requested data types */ public synchronized long getDataType() throws IOException { while (this.clientDataIn.available() > 0) { this.dataType = this.clientDataIn.readLong(); } /* for (ChillFieldInfo type : ChillFieldInfo.types) { if ((this.dataType & 1l << type.fieldNumber) != 0) { System.out.println("ProxyDataThread: Got request for " + type.fieldName); } } */ return this.dataType; } /** * Sets this thread to sending and specifies command. * * @param command command to process */ public synchronized void send(final ControlMessage command) { System.out.println("start of send"); this.command = command; this.status = Status.SENDING; System.out.println("end of send"); } /** * Send data to client. */ private void doSend() throws IOException { System.out.println("ProxyDataThread: Initiating sending."); long allAvailableTypes = 0; int currRayNumber = realtime ? cache.getNumberOfRays(this.command, ChillFieldInfo.NCP.fieldName) : 0; //ControlMessage metaCommand = this.command.setType(ChillDefines.META_TYPE); int metaIndex = 0; while (true) { ChillHeader header = (ChillHeader) cache.getDataWait(this.command, ChillDefines.META_TYPE, metaIndex); // Get header if (header == null) { this.status = Status.READY; return; //done } else if (header.header.recordType == ChillDefines.FIELD_SCALE_DATA) { sm.putScale((ChillMomentFieldScale) header); sendHeader(header); } else if (header.header.recordType == ChillDefines.GEN_MOM_DATA) { ChillDataHeader oldHeader = (ChillDataHeader) header; allAvailableTypes = oldHeader.availableData; // If we're calculating hybrid data types, mark them available if (parent.getCalcFlag()) { // If Z, PhiDP, and RhoHV are available, so is KDP. if (((allAvailableTypes & (1l << ChillFieldInfo.Z.fieldNumber)) != 0) && ((allAvailableTypes & (1l << ChillFieldInfo.RHOHV.fieldNumber)) != 0) && ((allAvailableTypes & (1l << ChillFieldInfo.PHIDP.fieldNumber)) != 0)) { allAvailableTypes |= 1l << ChillFieldInfo.KDP.fieldNumber; // If KDP available, so is Rcomp. allAvailableTypes |= 1l << ChillFieldInfo.RCOMP.fieldNumber; } // If Z and ZDR are available, so is HDR. if (((allAvailableTypes & (1l << ChillFieldInfo.Z.fieldNumber)) != 0) && ((allAvailableTypes & (1l << ChillFieldInfo.ZDR.fieldNumber)) != 0)) { allAvailableTypes |= 1l << ChillFieldInfo.HDR.fieldNumber; } // If NCP and ZDR are available, so is NCP+. if (((allAvailableTypes & (1l << ChillFieldInfo.NCP.fieldNumber)) != 0) && ((allAvailableTypes & (1l << ChillFieldInfo.ZDR.fieldNumber)) != 0)) { allAvailableTypes |= 1l << ChillFieldInfo.NCP_PLUS.fieldNumber; } } ArrayList<String> availableRequestedTypes = null; long availableRequestedTypesMask = 0; if (this.clientDataIn.available() > 0 || availableRequestedTypes == null) { availableRequestedTypesMask = getDataType() & allAvailableTypes; availableRequestedTypes = new ArrayList<String>(); for (ChillFieldInfo type : ChillFieldInfo.types) { if ((availableRequestedTypesMask & 1l << type.fieldNumber) != 0) { availableRequestedTypes.add(type.fieldName); } } } // Create new header with correct requestedData field. ChillDataHeader newHeader = new ChillDataHeader(oldHeader); newHeader.requestedData = availableRequestedTypesMask; newHeader.availableData = allAvailableTypes; sendHeader(newHeader); byte[][] data = new byte[availableRequestedTypes.size()][]; byte[] toSocket = new byte[availableRequestedTypes.size() * newHeader.numGates]; for (int t = 0; t < availableRequestedTypes.size(); ++t) { ChillGenRay ray = (ChillGenRay) cache.getDataWait(this.command, availableRequestedTypes.get(t), currRayNumber); data[t] = ViewUtil.getBytes(ray.getData(), ray.getType()); for (int g = 0; g < newHeader.numGates; ++g) { toSocket[availableRequestedTypes.size() * g + t] = data[t][g]; } } sendData(toSocket); //System.out.println("sent ray #" + currRayNumber); ++currRayNumber; Thread.yield(); } else { //other header sendHeader(header); } ++metaIndex; } } public synchronized void condemn() { this.status = Status.CONDEMNED; } public synchronized boolean isReady() { return this.status == Status.READY; } }