package edu.colostate.vchill.proxy; import edu.colostate.vchill.*; import edu.colostate.vchill.ChillDefines.Channel; import edu.colostate.vchill.cache.CacheMain; import edu.colostate.vchill.cache.CacheMainCyclic; import edu.colostate.vchill.chill.*; import edu.colostate.vchill.gui.GUIUtil; import edu.colostate.vchill.socket.SocketArchCtl.Command; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; /** * Driver class for a realtime proxy server. * Listens to socket requests and creates new threads to handle them. * * @author Jochen Deyke * @author jpont * @version 2010-08-30 */ public final class RealtimeProxy extends Proxy implements Control { private ControlMessage pseudo; /** * private constructor prevents instantiation */ private RealtimeProxy() { //change defaults; can be overriden via commandline args super.serverName = "force.chill.colostate.edu"; super.cacheSize = 3; } public static void main(final String[] args) throws IOException { new RealtimeProxy().parseCommandLineArguments(args).run(); System.exit(0); } public void run() throws IOException { if (guiflag) GUIUtil.startGUI("Java VCHILL Realtime Proxy Server"); //gui requested // instantiate cache cache = new CacheMainCyclic(cacheSize); //connect to realtime data server pseudo = new ControlMessage(serverName + ":" + serverPort, ChillDefines.REALTIME_DIR, ChillDefines.REALTIME_FILE, ChillDefines.REALTIME_SWEEP); new Thread(new CacheThread(cache, serverName, serverPort, calcflag), "RealtimeProxy.CacheThread").start(); //set up listening server socket ServerSocket clientsocket = null; try { clientsocket = new ServerSocket(listenPort); System.out.println("RealtimeProxy: Listening on port " + listenPort); } catch (IOException ioe) { throw new Error("RealtimeProxy: Could not listen on port " + listenPort, ioe); } while (true) { // repeat forever Socket socket = clientsocket.accept(); DataInputStream in = new DataInputStream(socket.getInputStream()); int fromSocket = in.readInt(); if (fromSocket == Command.HALT_COMMAND.ordinal()) { String pwd = in.readUTF(); if (password.equals(pwd)) { System.out.println("Realtime proxy server shutting down"); return; } else { System.out.println("Shutdown attempt with bad password: " + pwd); System.out.println("Realtime proxy server NOT shutting down"); } } else if (fromSocket == ChillDefines.HELLO) { fromSocket = in.readInt(); //channel if (fromSocket == Channel.MOM_DAT.ordinal()) { System.out.println("RealtimeProxy: Data socket request identified from " + socket.getInetAddress().getHostName()); //now do something constructive with it ProxyDataThread thread = new ProxyDataThread(socket, this, cache, true); thread.start(); thread.send(pseudo); } else { System.out.println("Bad request from " + socket.getInetAddress().getHostName() + ": unknown channel type"); } } else { System.out.println("Bad request from " + socket.getInetAddress().getHostName() + ": outdated version?"); } Proxy.sleep(100); } } /** * Sets the global setting variables according to the arguments * passed in. If an error is encountered, the application is * terminated. * * @param args The command line arguments as passed to main * @return this object; useful for chaining this method into creation */ private RealtimeProxy parseCommandLineArguments(final String[] args) { //parse commandline arguments if (args.length % 2 != 0) die(); for (int i = 0; i < args.length; i += 2) { if (args[i].equals("-server") || args[i].equals("-s")) { serverName = args[i + 1]; } else if (args[i].equals("-serverport") || args[i].equals("-sp")) { try { serverPort = Integer.parseInt(args[i + 1]); } catch (NumberFormatException e) { die(); } if (serverPort < 1 || serverPort > 65535) die(); } else if (args[i].equals("-listenport") || args[i].equals("-lp")) { try { listenPort = Integer.parseInt(args[i + 1]); } catch (NumberFormatException e) { die(); } if (listenPort < 1 || listenPort > 65535) die(); } else if (args[i].equals("-timeout") || args[i].equals("-t") || args[i].equals("-to")) { try { timeout = 60000l * Integer.parseInt(args[i + 1]); } catch (NumberFormatException e) { die(); } if (timeout < 0) die(); } else if (args[i].equals("-calculation") || args[i].equals("-c")) { if (args[i + 1].equals("on")) { calcflag = true; } else if (args[i + 1].equals("off")) { calcflag = false; } else { die(); } } else if (args[i].equals("-password") || args[i].equals("-p")) { password = args[i + 1]; } else if (args[i].equals("-gui") || args[i].equals("-g")) { if (args[i + 1].equals("on")) { guiflag = true; } else if (args[i + 1].equals("off")) { guiflag = false; } else { die(); } } else { die(); } } addCalculatedTypeScales(); return this; } /** * Prints usage message and exits. */ private static void die() { System.err.println("Usage: java RealtimeProxy [-server name] [-serverport port]"); System.err.println(" [-listenport port] [-timeout minutes] [-calculation on|off]"); System.err.println(" [-password pwd] [-gui on|off]"); System.err.println("Defaults: force.chill.colostate.edu 2510 2510 60 on secret"); System.err.println("server: what server the proxy should connect to"); System.err.println("serverport: what port the proxy should connect to"); System.err.println("listenport: what port the proxy should listen on"); System.err.println("timeout: how long the control socket can idle before being terminated"); System.err.println(" 0 means forever"); System.err.println("calculation: if the proxy should calculate hybrid data types"); System.err.println(" Turn this off if you're connecting to another proxy"); System.err.println("password: a password which must be matched to shut down the proxy"); System.exit(1); } public synchronized void killConnection(final ProxyDataThread pdt) { } private class CacheThread implements Runnable { private final CacheMain cache; private Socket socket; private DataInputStream in; private DataOutputStream out; private final String server; private final int port; private final boolean calcflag; /** * @param cache a shared cache to load data into * @param server the name of the server to connect to * @param port the port to connect to */ public CacheThread(final CacheMain cache, final String server, final int port, final boolean calcflag) { this.cache = cache; this.server = server; this.port = port; this.calcflag = calcflag; this.connect(); } /** * Open a connection to the server */ private void connect() { System.out.println("RealtimeProxy: trying to connect to " + this.server + ":" + this.port); int allTypes = 0; for (ChillFieldInfo type : ChillFieldInfo.types) { if ((type.fieldNumber < ChillFieldInfo.CALC_CUTOFF) || !this.calcflag) { allTypes |= 1l << type.fieldNumber; //System.out.println(Integer.toHexString(allTypes)); } } try { this.socket = new Socket(this.server, this.port); this.in = new DataInputStream(new BufferedInputStream(this.socket.getInputStream())); this.out = new DataOutputStream(new BufferedOutputStream(this.socket.getOutputStream())); this.out.writeInt(Channel.MOM_DAT.ordinal()); this.out.writeInt(allTypes); this.out.flush(); } catch (Exception e) { throw new Error(e); } System.out.println("connected"); } /** * Continually load data into the cache, processing new type and stop requests as they occur. */ public void run() { System.out.println("RealtimeProxy.CacheThread is running"); ChillHSKHeader hskH = null; LOOP: { ChillGenRay prevZDR = null; while (true) { ChillGenRay rayZ = null; ChillGenRay rayZDR = null; ChillGenRay rayNCP = null; ChillGenRay rayPHIDP = null; ChillGenRay rayRHOHV = null; try { if (this.in.available() > 0) { //get data //check whether this is scaling info or moment header ChillHeaderHeader headerH = new ChillHeaderHeader(this.in); switch (headerH.recordType) { case ChillDefines.GEN_MOM_DATA: ChillDataHeader dataH = new ChillDataHeader(this.in, headerH); ArrayList<String> types = dataH.calculateTypes(); byte[][] data = new byte[types.size()][dataH.numGates]; byte[] interleavedData = new byte[types.size() * dataH.numGates]; this.in.readFully(interleavedData); for (int t = 0; t < types.size(); ++t) { //split data types String type = types.get(t); if (type == null) continue; for (int g = 0; g < dataH.numGates; ++g) { data[t][g] = interleavedData[types.size() * g + t]; } ChillGenRay ray = new ChillGenRay(hskH, dataH, type, data[t]); { //save references to some types for calculations if (type.equals(ChillFieldInfo.Z.fieldName)) rayZ = ray; else if (type.equals(ChillFieldInfo.ZDR.fieldName)) rayZDR = ray; else if (type.equals(ChillFieldInfo.NCP.fieldName)) rayNCP = ray; else if (type.equals(ChillFieldInfo.PHIDP.fieldName)) rayPHIDP = ray; else if (type.equals(ChillFieldInfo.RHOHV.fieldName)) rayRHOHV = ray; } cache.addRay(pseudo, type, ray); } CALC: { double[] dataZ = rayZ == null ? null : rayZ.getData(); double[] dataZDR = rayZDR == null ? null : rayZDR.getData(); double[] dataZDRp = prevZDR == null ? null : prevZDR.getData(); double[] dataNCP = rayNCP == null ? null : rayNCP.getData(); double[] dataPHIDP = rayPHIDP == null ? null : rayPHIDP.getData(); double[] dataRHOHV = rayRHOHV == null ? null : rayRHOHV.getData(); double[] dataHDR = null; double[] dataKDP = null; HDR: { //if (dataZ == null) System.out.println ("Z missing"); //if (dataZDR == null) System.out.println ("ZDR missing"); if (dataZ == null || dataZDR == null) { //System.out.println ("not calculating HDR"); break HDR; } //System.out.println("calculating HDR"); dataHDR = HdrUtil.calculateHDR(dataZ, dataZDR); //store results cache.addRay(pseudo, ChillFieldInfo.HDR.fieldName, new ChillGenRay(hskH, dataH, ChillFieldInfo.HDR.fieldName, dataHDR)); } KDP: { //if (dataPHIDP == null) System.out.println("PHIDP missing"); //if (dataRHOHV == null) System.out.println("RHOHV missing"); if (dataPHIDP == null || dataZ == null || dataRHOHV == null) { //System.out.println("not calculating KDP"); break KDP; } //System.out.println("calculating KDP"); dataKDP = KdpUtil.calculateKDP(dataPHIDP, dataZ, dataRHOHV, rayPHIDP.getStartRange() * 1e-6, rayPHIDP.getGateWidth()); //store result cache.addRay(pseudo, ChillFieldInfo.KDP.fieldName, new ChillGenRay(hskH, dataH, ChillFieldInfo.KDP.fieldName, dataKDP)); } RCOMP: { //if (dataKDP == null) System.out.println("KDP missing"); if (dataKDP == null || dataZ == null || dataZDR == null) { //System.out.println("not calculating rain"); break RCOMP; } //System.out.println("calculating rain"); double[] rain = RainUtil.calculateCompositeRain(dataKDP, dataZ, dataZDR); //store result cache.addRay(pseudo, ChillFieldInfo.RCOMP.fieldName, new ChillGenRay(hskH, dataH, ChillFieldInfo.RCOMP.fieldName, rain)); } NCP_PLUS: { //if (dataNCP == null) System.out.println("NCP missing"); if (dataNCP == null || dataZDR == null) { //System.out.println("not calculating NCP_PLUS"); break NCP_PLUS; } //System.out.println("calculating NCP_PLUS"); double[] dataNCP_PLUS = NcpPlusUtil.calculateNCP_PLUS(dataNCP, dataZDRp, dataZDR, null); //store result cache.addRay(pseudo, ChillFieldInfo.NCP_PLUS.fieldName, new ChillGenRay(hskH, dataH, ChillFieldInfo.NCP_PLUS.fieldName, dataNCP_PLUS)); } } //System.out.println("bytes available after data = " + this.in.available()); break; case ChillDefines.BRIEF_HSK_DATA: hskH = new ChillHSKHeader(this.in, headerH); break; case ChillDefines.FIELD_SCALE_DATA: ChillMomentFieldScale scale = new ChillMomentFieldScale(this.in, headerH); sm.putScale(scale); break; case ChillDefines.TRACK_DATA: ChillTrackInfo track = new ChillTrackInfo(this.in, headerH); break; default: System.out.println("Don't know how to handle header of type " + headerH.recordType); ChillHeader generic = new ChillHeader(headerH); break; } } else { Proxy.sleep(100); } //wait for data } catch (Exception e) { throw new Error(e); } prevZDR = rayZDR; } } //end loop } } }