package edu.colostate.vchill; import edu.colostate.vchill.ascope.ViewAScopeWindow; import edu.colostate.vchill.chill.*; import edu.colostate.vchill.connection.Controller; import edu.colostate.vchill.data.Ray; import edu.colostate.vchill.gui.WindowManager; import edu.colostate.vchill.numdump.NumDumpWindow; import edu.colostate.vchill.plot.ViewPlotWindow; import java.awt.*; import java.util.LinkedHashSet; import java.util.List; /** * This is the main class that handles the plots and queues up their requests * and manages the stop functionality (still not working). The class shares a * number of structures with ViewControl, but there doesn't seem to be a way to * help this and avoid massive amounts of message passing. * * @author Justin Carlson * @author Jochen Deyke * @author jpont * @version 2010-07-07 * @created June 26, 2003 */ public class ViewControlThread implements Runnable { private static final WindowManager wm = WindowManager.getInstance(); private static final Config config = Config.getInstance(); private static final ScaleManager sm = ScaleManager.getInstance(); private static final LocationManager lm = LocationManager.getInstance(); /** * The current message the thread is using */ private ControlMessage currMessage; /** * Used for message passing */ private final ControlSyncQueue<ControlMessage> queue; /** * The connection to the server */ private final Controller controller; /** * The list of open plot windows */ private final List<ViewPlotWindow> plots; /** * The list of open ascope windows */ private final List<ViewAScopeWindow> ascopes; /** * The list of open numerical windows */ private final List<NumDumpWindow> numdumps; /** The list of open histogram windows */ /** * This thread class will handle all of the plotting requests that the user * makes. It takes messages in through a shared * <code>ControlSyncQueue</code> and references the Controller class only * for the <code>getRay</code> method. * * @param queue The shared message queue to recieve plotting requests. * @param controller This is the controller class that has the connections to the * data retrieval methods, the only method that should be used is * getRay() */ public ViewControlThread(final ControlSyncQueue<ControlMessage> queue, final Controller controller) { this.ascopes = wm.getAScopeList(); this.numdumps = wm.getNumDumpList(); this.plots = wm.getPlotList(); this.queue = queue; this.controller = controller; } /** * This is the main loop where the thread attempts to get the first message * that is on the InputData class. If not found it will wait until something * is added. It then goes into the plotting method. NOTE: There may need to * be some form of file checking to leave sweeps behind instead of clearing * the buffers. */ public void run() { Boolean firstRunThrough = true; while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { } currMessage = queue.get(); if (currMessage == null) { continue; // can't do anything without a request } if (currMessage.getURL() == null || currMessage.getDir() == null || currMessage.getFile() == null || currMessage.getSweep() == null) { continue; // can't plot without all fields set } else if (ChillDefines.REALTIME_DIR.equals(currMessage.getDir()) || ChillDefines.REALTIME_FILE.equals(currMessage.getFile()) || ChillDefines.REALTIME_SWEEP.equals(currMessage .getSweep())) { continue; // plotted as it comes in; don't need to do anything } else { // good to go? String filter = config.getScanFilter(); int sweepNum = 0; try { sweepNum = Integer.parseInt(currMessage.getSweep().split( " ")[1]); } catch (Exception e) { System.out.println("Bad filter"); } // ignore, can be caused by "Nothing to display" int sweepFilter = config.getAutosaveTilt(); if ((filter == null || currMessage.getFile().contains(filter)) && // filename filter (sweepFilter == 0 || sweepFilter == sweepNum)) { // sweep // filter newPlot(); } else { // filter enabled, and scan doesn't match System.out.println("Skipping sweep"); EventQueue.invokeLater(new Runnable() { public void run() { ViewControl.getInstance().sweepDone(); } }); continue; } } } } public void handleAircraft(ChillHeader header) { final ViewPlotWindow[] plotWins = this.plots .toArray(new ViewPlotWindow[this.plots.size()]); switch (header.header.recordType) { case ChillDefines.TRACK_DATA: // aircraft info ChillTrackInfo loc = (ChillTrackInfo) header; for (int i = 0; i < plotWins.length; ++i) { plotWins[i].plotAircraft(loc); } break; case ChillDefines.OLD_EXT_TRACK_DATA: // extended aircraft info ChillOldExtTrackInfo coeti = (ChillOldExtTrackInfo) header; for (int i = 0; i < plotWins.length; ++i) { plotWins[i].plotAircraft(coeti); } break; case ChillDefines.NEW_EXT_TRACK_DATA: // extended aircraft info ChillNewExtTrackInfo cneti = (ChillNewExtTrackInfo) header; for (int i = 0; i < plotWins.length; ++i) { plotWins[i].plotAircraft(cneti); } break; } } /** * This method has three major parts. They would be broken up but for all of * the data passing that would be required. First it finds the list of all * the active windows. It then tries to get a ray; if not found it will * sleep and try a few more times. Once the ray is found, it asks for the * threshold field for the sake of interleaving the fields. Finally it sends * the data to the window for actual plotting. Note: This is also where stop * is implemented; the main while loop looks for a stop action in the * ControlSyncQueue queue and if one is found it breaks out of the while * loops. * <p/> * NOTE: Needs to have a better flow diagram. */ private void newPlot() { final ViewPlotWindow[] plotWins = this.plots .toArray(new ViewPlotWindow[this.plots.size()]); System.out.println("NEW PLOT"); { // request data & ensure a window is open // get threshold field if any window is open LinkedHashSet<String> allTypes = new LinkedHashSet<String>(); String thresh = config.getThresholdType(); if (thresh != null) allTypes.add(thresh); // This is used to do the precaching, and data interleaving. The // requests queue in the buffer, and the lower levels determine // what request to send based on a quick check on the types // requested. int num_windows = 0; // get data needed for plot windows for (ViewPlotWindow win : this.plots) { ++num_windows; allTypes.add(win.getType()); } // get data needed for ascope windows for (ViewAScopeWindow win : this.ascopes) { ++num_windows; allTypes.add(win.getType()); allTypes.add(win.getSecondary()); } // get data needed for numerical windows for (NumDumpWindow win : this.numdumps) { ++num_windows; allTypes.add(win.getType()); } // sm.clear(); //clear data type list; will be recreated from // metadata stream // System.out.println("Requesting " + // currMessage.setType(allTypes)); System.out.println("Requesting sweep"); controller.requestSweep(currMessage, allTypes); // always request to // ensure we get // datatype list if (num_windows == 0) return; // nothing to display } System.out.println("Waiting for sweep to finish"); while (!controller.isSweepDone(currMessage)) { try { Thread.sleep(250); } catch (InterruptedException e) { } // ensure fetchthread gets a chance to see request } {// now that all the types are coming in, start plotting System.out.println("Starting plotting"); wm.setPlotting(true); // final ControlMessage meta = // currMessage.setType(ChillDefines.META_TYPE); for (int metaNum = 0; /* check in loop */ ; ++metaNum) { if (queue.stopping()) break; ChillHeader header = (ChillHeader) controller.getRayWait( currMessage, ChillDefines.META_TYPE, metaNum); if (header == null) break; if (header.header.recordType == ChillDefines.GEN_MOM_DATA) { ChillDataHeader dh = (ChillDataHeader) header; sm.setAvailable(dh.availableData); // System.out.println("requested 0x" + // Long.toHexString(dh.requestedData)); // System.out.println("available 0x" + // Long.toHexString(dh.availableData)); // System.out.println("not avail 0x" + // Long.toHexString(dh.requestedData & ~dh.availableData)); break; } } System.out.println("Checking availability"); final String[] plotTypes = new String[plotWins.length]; for (int i = 0; i < plotWins.length; ++i) { plotTypes[i] = this.checkAvailability(plotWins[i].getType()); } final ViewAScopeWindow[] ascopeWins = this.ascopes .toArray(new ViewAScopeWindow[this.ascopes.size()]); final String[][] ascopeTypes = new String[ascopeWins.length][2]; for (int i = 0; i < ascopeWins.length; ++i) { ascopeTypes[i][0] = this.checkAvailability(ascopeWins[i] .getType()); ascopeTypes[i][1] = this.checkAvailability(ascopeWins[i] .getSecondary()); } final NumDumpWindow[] numdumpWins = this.numdumps .toArray(new NumDumpWindow[this.numdumps.size()]); final String[] numdumpTypes = new String[numdumpWins.length]; for (int i = 0; i < numdumpWins.length; ++i) { numdumpTypes[i] = this.checkAvailability(numdumpWins[i] .getType()); } String threshold = this .checkAvailability(config.getThresholdType()); Ray[] plotPrevRays = new Ray[plotWins.length]; Ray[] plotCurrRays = new Ray[plotWins.length]; Ray[] plotNextRays = new Ray[plotWins.length]; Ray[][] ascopeRays = new Ray[ascopeWins.length][2]; Ray[] numdumpRays = new Ray[numdumpWins.length]; // Ray[] histogramRays = new Ray[histogramWins.length]; Ray threshRay; for (ViewPlotWindow win : plotWins) { win.clearScreen(); // clear any existing aircraft info from previous sweeps win.clearAircraftInfo(); } int rayNum = 0; int metaNum = 0; boolean rangeSet = false; while (true) { if (queue.stopping()) break; ChillHeader header = (ChillHeader) controller.getRayWait( currMessage, ChillDefines.META_TYPE, metaNum); if (header == null) break; switch (header.header.recordType) { case ChillDefines.GEN_MOM_DATA: // System.out.println("0x" + // Long.toHexString(((ChillDataHeader)header).requestedData)); // System.out.println("0x" + // Long.toHexString(((ChillDataHeader)header).availableData)); threshRay = threshold == null ? null : getRayWait( currMessage, threshold, rayNum); for (int i = 0; i < plotWins.length; ++i) { // get rays / // detect end if (plotTypes[i] == null) continue; // seems to be unavailable plotPrevRays[i] = plotCurrRays[i]; plotCurrRays[i] = getRayWait(currMessage, plotTypes[i], rayNum); if (plotCurrRays[i] != null) { // null => not available // => sweep done? if (!rangeSet) { config.setMaxPlotRange(plotCurrRays[i] .getEndRange() * 1e-6); rangeSet = true; } plotNextRays[i] = getRayWait(currMessage, plotTypes[i], rayNum + 1); plotWins[i].plot(plotPrevRays[i], plotCurrRays[i], plotNextRays[i], threshRay); } } for (int i = 0; i < ascopeWins.length; ++i) { // get rays / // detect // end if (ascopeTypes[i][0] != null) ascopeRays[i][0] = getRayWait(currMessage, ascopeTypes[i][0], rayNum); if (ascopeRays[i][0] != null) { // null => not available // => sweep done? if (!rangeSet) { config.setMaxPlotRange(ascopeRays[i][0] .getEndRange()); rangeSet = true; } if (ascopeTypes[i][1] != null) ascopeRays[i][1] = getRayWait(currMessage, ascopeTypes[i][1], rayNum); } } for (int i = 0; i < numdumpWins.length; ++i) { // get rays / // detect // end if (numdumpTypes[i] == null) continue; // seems to be unavailable numdumpRays[i] = getRayWait(currMessage, numdumpTypes[i], rayNum); if (numdumpRays[i] != null) { // null => not available // => sweep done? if (!rangeSet) { config.setMaxPlotRange(numdumpRays[i] .getEndRange()); rangeSet = true; } numdumpWins[i].plot(numdumpRays[i], threshRay); } } /* for (int i = 0; i < ascopeWins.length; ++i) { // get rays / // detect // end if (ascopeTypes[i][0] != null) ascopeRays[i][0] = getRayWait(currMessage, ascopeTypes[i][0], rayNum); if (ascopeRays[i][0] != null) { // null => not available // => sweep done? if (!rangeSet) { config.setMaxPlotRange(ascopeRays[i][0] .getEndRange()); rangeSet = true; } if (ascopeTypes[i][1] != null) ascopeRays[i][1] = getRayWait(currMessage, ascopeTypes[i][1], rayNum); } } */ if (rayNum % config.getRayStep() == 0) { for (ViewPlotWindow win : plotWins) win.rePlotDrawingArea(); for (int i = 0; i < ascopeWins.length; ++i) { ascopeWins[i].plot(ascopeRays[i][0], ascopeRays[i][1]); ascopeWins[i].repaint(ascopeWins[i] .getVisibleRect()); } Thread.yield(); } ++rayNum; break; case ChillDefines.BRIEF_HSK_DATA: // housekeeping header ChillHSKHeader hskh = (ChillHSKHeader) header; lm.setLatitude(hskh.radarLatitude * 1e-6); lm.setLongitude(hskh.radarLongitude * 1e-6); wm.replotOverlay(); break; case ChillDefines.FIELD_SCALE_DATA: sm.putScale((ChillMomentFieldScale) header); break; case ChillDefines.TRACK_DATA: // aircraft info // System.out.println("Aircraft data found"); ChillTrackInfo loc = (ChillTrackInfo) header; for (int i = 0; i < plotWins.length; ++i) { plotWins[i].plotAircraft(loc); } break; case ChillDefines.OLD_EXT_TRACK_DATA: // extended aircraft info ChillOldExtTrackInfo coeti = (ChillOldExtTrackInfo) header; for (int i = 0; i < plotWins.length; ++i) { plotWins[i].plotAircraft(coeti); } break; case ChillDefines.NEW_EXT_TRACK_DATA: // extended aircraft info ChillNewExtTrackInfo cneti = (ChillNewExtTrackInfo) header; for (int i = 0; i < plotWins.length; ++i) { plotWins[i].plotAircraft(cneti); } break; case ChillDefines.HSK_ID_PROCESSOR_INFO: // processor // information ChillProcessorInfo procinf = (ChillProcessorInfo) header; config.setMaxPlotRange(procinf.range_stop_km); rangeSet = true; // don't bother calculating from ray // headers break; case ChillDefines.HSK_ID_END_NOTICE: // end of sweep/volume - // archive doesn't send? System.out.println("viewcontrolthread: end notice"); break; default: // unkown/unimportant header break; } ++metaNum; } // finish up & handle autosave/export for (int i = 0; i < plotWins.length; ++i) { final ViewPlotWindow win = plotWins[i]; win.rePlotDrawingArea(); Ray ray = plotPrevRays[i]; if (ray != null) { // saving possible if (config.isSaveAllEnabled() || // always save ray.getTiltNum() == ray.getSaveTilt()) { // webtilt if (config.isImageAutosaveEnabled()) { // image save // enabled try { EventQueue.invokeAndWait(new Runnable() { public void run() { wm.saveImage(win, currMessage); } }); } catch (Exception e) { throw new Error(e); } } if (config.isImageAutoExportEnabled()) { // export // enabled try { EventQueue.invokeAndWait(new Runnable() { public void run() { wm.export(win, currMessage); } }); } catch (Exception e) { throw new Error(e); } } } } } // done plotting wm.setPlotting(false); EventQueue.invokeLater(new Runnable() { public void run() { System.out.println("Sweep done!"); ViewControl.getInstance().sweepDone(); } }); } System.out.println("END NEW PLOT"); } /** * Check the availability of a certain data type * * @param type the type to check * @return currMessage.setType(type) or null if unavailable */ private String checkAvailability(final String type) { if (type == null) return null; System.out.print("Checking availability of " + type + " ..."); if (controller.getRayWait(currMessage, type, 0, 1000) == null) { System.out.println(" not available"); return null; } else { System.out.println(" OK"); return type; } } /** * This method makes the request from the Control object for the actual ray * data. * * @param msg The describing ControlMessage of data that is being requested. * @param rayIndex The index of the ray being requested. * @return A Ray of data, or null to signify termination or end of the * requested volume. */ public Ray getRay(final ControlMessage msg, final String type, final int rayIndex) { try { return msg == null ? null : (Ray) controller.getRay(msg, type, rayIndex); } catch (Exception e) { System.err.println("Exception in ViewControlThread.getRay: " + e); e.printStackTrace(); return null; } } /** * This is the same as the getRay method with one major difference. If the * ray is not yet available, it will wait for it to come in or for the sweep * to be marked complete. * * @param msg ControlMessage describing the ray that is being requested. * @param rayIndex The index of the requested ray. * @return The ray of data that was requested. */ public Ray getRayWait(final ControlMessage msg, final String type, final int rayIndex) { return msg == null ? null : (Ray) controller.getRayWait(msg, type, rayIndex); } /** * @param msg ControlMessage describing the ray that is being requested. * @param rayIndex The index of the requested ray. * @param timeout the time (in ms) to wait for the ray to come in if it is not * yet available * @return The ray of data that was requested. */ public Ray getRayWait(final ControlMessage msg, final String type, final int rayIndex, final long timeout) { return msg == null ? null : (Ray) controller.getRayWait(msg, type, rayIndex, timeout); } }