Print

Print


Author: [log in to unmask]
Date: Sun Mar 15 11:31:15 2015
New Revision: 2459

Log:
Merge changes from monitoring dev branch into trunk.

Added:
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConditionsCollectionTableModel.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/ConditionsCollectionTableModel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConditionsPanel.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/ConditionsPanel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventDashboard.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/EventDashboard.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusEventsTable.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/SystemStatusEventsTable.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusPanel.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/SystemStatusPanel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/EtEventFilter.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/util/EtEventFilter.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/MonitoringApplicationEventBuilder.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/util/MonitoringApplicationEventBuilder.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/PhysicsSyncEventStation.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/util/PhysicsSyncEventStation.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/PreStartEtStation.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/util/PreStartEtStation.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/RunnableEtStation.java
      - copied unchanged from r2457, java/branches/monitoring-app-dev/src/main/java/org/hps/monitoring/application/util/RunnableEtStation.java
Removed:
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/RunPanel.java
Modified:
    java/trunk/monitoring-app/   (props changed)
    java/trunk/monitoring-app/pom.xml
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventProcessing.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplicationFrame.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotPanel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatusModel.java
    java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/RunModel.java
    java/trunk/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop

Modified: java/trunk/monitoring-app/pom.xml
 =============================================================================
--- java/trunk/monitoring-app/pom.xml	(original)
+++ java/trunk/monitoring-app/pom.xml	Sun Mar 15 11:31:15 2015
@@ -111,6 +111,10 @@
         </dependency>
         <dependency>
             <groupId>org.hps</groupId>
+            <artifactId>hps-ecal-recon</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hps</groupId>
             <artifactId>hps-monitoring-drivers</artifactId>
         </dependency>       
         <dependency>
@@ -134,6 +138,16 @@
             <groupId>org.freehep</groupId>
             <artifactId>freehep-jaida-remote</artifactId>
             <version>3.4.11</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.jdom</groupId>
+                    <artifactId>jdom</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-math</groupId>
+                    <artifactId>commons-math</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
     </dependencies>
-</project>
+</project>

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java	Sun Mar 15 11:31:15 2015
@@ -34,9 +34,10 @@
     // Save a screenshot
     static final String SAVE_SCREENSHOT = "saveScreenshot";
     
-    // Save the plots
+    // Plotting actions
     static final String SAVE_PLOTS = "savePlots";
     static final String CLEAR_PLOTS = "resetPlots";
+    static final String SAVE_SELECTED_PLOTS = "saveSelectedPlots";
     
     // Exit the application.
     static final String EXIT = "exit";

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java	Sun Mar 15 11:31:15 2015
@@ -45,7 +45,6 @@
         setMinimumSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
 
         setLayout(new GridBagLayout());
-        // setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
 
         GridBagConstraints c = new GridBagConstraints();
         c.weightx = c.weighty = 1.0;

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java	Sun Mar 15 11:31:15 2015
@@ -80,10 +80,19 @@
             resumeButton.setEnabled(false);
             connectButton.setActionCommand(Commands.CONNECT);
             connectButton.setIcon(disconnectedIcon);
-        } else {
+            connectButton.setEnabled(true);
+        } else if (status.equals(ConnectionStatus.DISCONNECTING)) {
+            nextButton.setEnabled(false);
+            pauseButton.setEnabled(false);
+            resumeButton.setEnabled(false);
+            connectButton.setEnabled(false);
+        } else if (status.equals(ConnectionStatus.CONNECTED)) {
+            nextButton.setEnabled(false);            
             pauseButton.setEnabled(true);
+            resumeButton.setEnabled(false);
             connectButton.setActionCommand(Commands.DISCONNECT);
             connectButton.setIcon(connectedIcon);
+            connectButton.setEnabled(true);
         }
     }       
     

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventProcessing.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventProcessing.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventProcessing.java	Sun Mar 15 11:31:15 2015
@@ -4,6 +4,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Logger;
 
@@ -14,6 +15,9 @@
 import org.hps.monitoring.application.model.ConnectionStatus;
 import org.hps.monitoring.application.model.SteeringType;
 import org.hps.monitoring.application.util.EtSystemUtil;
+import org.hps.monitoring.application.util.PhysicsSyncEventStation;
+import org.hps.monitoring.application.util.PreStartEtStation;
+import org.hps.monitoring.application.util.RunnableEtStation;
 import org.hps.monitoring.subsys.et.EtSystemMonitor;
 import org.hps.monitoring.subsys.et.EtSystemStripCharts;
 import org.hps.record.LCSimEventBuilder;
@@ -24,26 +28,30 @@
 import org.hps.record.enums.DataSourceType;
 import org.hps.record.et.EtConnection;
 import org.hps.record.evio.EvioDetectorConditionsProcessor;
+import org.jlab.coda.et.EtSystem;
 import org.jlab.coda.et.exception.EtClosedException;
 import org.jlab.coda.et.exception.EtException;
+import org.lcsim.conditions.ConditionsListener;
 import org.lcsim.conditions.ConditionsManager;
 import org.lcsim.conditions.ConditionsReader;
 import org.lcsim.util.Driver;
 
 /**
- * This class encapsulates all of the logic involved with processing events 
- * and managing the related state and objects within the monitoring application.
+ * This class encapsulates all of the logic involved with processing events and managing the related
+ * state and objects within the monitoring application.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
 class EventProcessing {
-    
+
     MonitoringApplication application;
     Logger logger;
     SessionState sessionState;
     List<CompositeRecordProcessor> processors;
     List<Driver> drivers;
-    
+    List<ConditionsListener> conditionsListeners;
+    int stationPosition;
+
     /**
      * This class is used to organize the objects for an event processing session.
      */
@@ -53,33 +61,54 @@
         CompositeLoop loop;
         EventProcessingThread processingThread;
         Thread sessionWatchdogThread;
+        ThreadGroup stationThreadGroup = new ThreadGroup("Station Threads");
+        List<RunnableEtStation> stations = new ArrayList<RunnableEtStation>();
         EtConnection connection;
     }
-    
-    /**
-     * Initialize with reference to the current monitoring application
-     * and a list of extra processors to add to the loop after 
-     * configuration.
+
+    /**
+     * Initialize with reference to the current monitoring application and a list of extra
+     * processors to add to the loop after configuration.
      * @param application The current monitoring application.
      * @param processors A list of processors to add after configuration is performed.
      */
     EventProcessing(
             MonitoringApplication application, 
-            List<CompositeRecordProcessor> processors,
-            List<Driver> drivers) {
+            List<CompositeRecordProcessor> processors, 
+            List<Driver> drivers, 
+            List<ConditionsListener> conditionsListeners) {
         this.application = application;
-        this.sessionState = new SessionState();        
+        this.sessionState = new SessionState();
         this.logger = MonitoringApplication.logger;
         this.processors = processors;
         this.drivers = drivers;
+        this.conditionsListeners = conditionsListeners;
+        this.stationPosition = application.configurationModel.getStationPosition();
     }
     
+    int getNextStationPosition() {
+        this.stationPosition += 1;
+        return this.stationPosition;        
+    }
+
     /**
      * Setup this class from the global configuration.
      * @param configurationModel The global configuration.
      */
     void setup(ConfigurationModel configurationModel) {
-        MonitoringApplication.logger.info("setting up LCSim");
+        
+        // Setup LCSim from the configuration.
+        setupLcsim(configurationModel);
+
+        // Now setup the CompositeLoop.
+        setupLoop(configurationModel);
+    }
+
+    /**
+     * @param configurationModel
+     */
+    private void setupLcsim(ConfigurationModel configurationModel) {
+        MonitoringApplication.logger.info("setting up lcsim");
 
         // Get steering resource or file as a String parameter.
         String steering = null;
@@ -90,22 +119,31 @@
             steering = configurationModel.getSteeringResource();
         }
 
-        MonitoringApplication.logger.config("Set steering to " + steering + " with type " + (steeringType == SteeringType.RESOURCE ? "RESOURCE" : "FILE"));
+        MonitoringApplication.logger.config("set steering " + steering + " with type " + (steeringType == SteeringType.RESOURCE ? "RESOURCE" : "FILE"));
 
         try {
-            // Create and the job manager.  The conditions manager is instantiated from this call but not configured.
+            // Create and the job manager. The conditions manager is instantiated from this call but
+            // not configured.
             sessionState.jobManager = new JobManager();
-            
+
+            // Add conditions listeners after new database conditions manager is initialized from
+            // job manager.
+            DatabaseConditionsManager conditionsManager = DatabaseConditionsManager.getInstance();
+            for (ConditionsListener conditionsListener : conditionsListeners) {
+                logger.config("adding conditions listener " + conditionsListener.getClass().getName());
+                conditionsManager.addConditionsListener(conditionsListener);
+            }
+
             if (configurationModel.hasValidProperty(ConfigurationModel.DETECTOR_ALIAS_PROPERTY)) {
-                // Set a detector alias.                
+                // Set a detector alias.
                 ConditionsReader.addAlias(configurationModel.getDetectorName(), "file://" + configurationModel.getDetectorAlias());
                 logger.config("using detector alias " + configurationModel.getDetectorAlias());
             }
-                        
+
             // Setup the event builder to translate from EVIO to LCIO.
             // This must happen before Driver setup so the builder's listeners are activated first!
             createEventBuilder(configurationModel);
-            
+
             // Configure the job manager for the XML steering.
             sessionState.jobManager.setPerformDryRun(true);
             if (steeringType == SteeringType.RESOURCE) {
@@ -113,24 +151,23 @@
             } else if (steeringType.equals(SteeringType.FILE)) {
                 setupSteeringFile(steering);
             }
-            
+
             // Set conditions tag.
             if (configurationModel.hasValidProperty(ConfigurationModel.CONDITIONS_TAG_PROPERTY) && !configurationModel.getConditionsTag().equals("")) {
-                logger.config("conditions tag is set to " + configurationModel.getConditionsTag());                
+                logger.config("conditions tag is set to " + configurationModel.getConditionsTag());
             } else {
                 logger.config("conditions NOT using a tag");
             }
-           
+
             // Is there a user specified run number from the JobPanel?
             if (configurationModel.hasValidProperty(ConfigurationModel.USER_RUN_NUMBER_PROPERTY)) {
                 int userRunNumber = configurationModel.getUserRunNumber();
                 String detectorName = configurationModel.getDetectorName();
-                DatabaseConditionsManager conditionsManager = DatabaseConditionsManager.getInstance();
                 logger.config("setting user run number " + userRunNumber + " with detector " + detectorName);
                 conditionsManager.setDetector(configurationModel.getDetectorName(), userRunNumber);
                 if (configurationModel.hasPropertyKey(ConfigurationModel.FREEZE_CONDITIONS_PROPERTY)) {
-                    // Freeze the conditions system to ignore run numbers from the events.  
-                    logger.config("user configured to freeze conditions system from monitoring app");
+                    // Freeze the conditions system to ignore run numbers from the events.
+                    logger.config("user configured to freeze conditions system");
                     conditionsManager.freeze();
                 } else {
                     // Allow run numbers to be picked up from the events.
@@ -142,18 +179,15 @@
             logger.info("lcsim setup was successful");
 
         } catch (Throwable t) {
-            // Catch all errors and rethrow them as RuntimeExceptions.
+            // Catch all errors and re-throw them as RuntimeExceptions.
             application.errorHandler.setError(t).setMessage("Error setting up LCSim.").printStackTrace().raiseException();
         }
-        
-        // Now setup the CompositeLoop.
-        setupLoop(configurationModel);
-    }
-    
+    }
+
     /**
      * Create the event builder for converting EVIO events to LCSim.
      */
-    void createEventBuilder(ConfigurationModel configurationModel) {
+    private void createEventBuilder(ConfigurationModel configurationModel) {
 
         // Get the class for the event builder.
         String eventBuilderClassName = configurationModel.getEventBuilderClassName();
@@ -168,12 +202,12 @@
         // Add the builder as a listener so it is notified when conditions change.
         ConditionsManager.defaultInstance().addConditionsListener(sessionState.eventBuilder);
     }
-    
+
     /**
      * Setup the loop from the global configuration.
      * @param configurationModel The global configuration.
      */
-    void setupLoop(ConfigurationModel configurationModel) {
+    private void setupLoop(ConfigurationModel configurationModel) {
 
         CompositeLoopConfiguration loopConfig = new CompositeLoopConfiguration()
             .setStopOnEndRun(configurationModel.getDisconnectOnEndRun())
@@ -187,13 +221,12 @@
         if (configurationModel.hasValidProperty(ConfigurationModel.MAX_EVENTS_PROPERTY)) {
             long maxEvents = configurationModel.getMaxEvents();
             if (maxEvents > 0L) {
-                //logger.config("processing will stop after max events: " + maxEvents);
                 loopConfig.setMaxRecords(maxEvents);
             }
         }
-        
+
         // Add all Drivers from the JobManager.
-        for (Driver driver : sessionState.jobManager.getDriverExecList()) {            
+        for (Driver driver : sessionState.jobManager.getDriverExecList()) {
             loopConfig.add(driver);
             logger.config("added Driver " + driver.getName() + " to job");
         }
@@ -211,30 +244,30 @@
         }
 
         // Add extra CompositeRecordProcessors to the loop config.
-        for (CompositeRecordProcessor processor : processors) {            
-            loopConfig.add(processor);   
+        for (CompositeRecordProcessor processor : processors) {
+            loopConfig.add(processor);
             logger.config("added extra processor " + processor.getClass().getSimpleName() + " to job");
         }
-        
+
         // Add extra Drivers to the loop config.
-        for (Driver driver : drivers) {            
+        for (Driver driver : drivers) {
             loopConfig.add(driver);
             logger.config("added extra Driver " + driver.getName() + " to job");
         }
-                
-        // Enable conditions system activation from EVIO event information.
+
+        // Enable conditions system activation from EVIO event data in case the PRESTART is missed.
         logger.config("added EvioDetectorConditionsProcessor to job with detector " + configurationModel.getDetectorName());
         loopConfig.add(new EvioDetectorConditionsProcessor(configurationModel.getDetectorName()));
 
         // Create the CompositeLoop with the configuration.
-        sessionState.loop = new CompositeLoop(loopConfig);        
-    }    
-    
+        sessionState.loop = new CompositeLoop(loopConfig);
+    }
+
     /**
      * Setup a steering file on disk.
      * @param steering The steering file.
      */
-    void setupSteeringFile(String steering) {
+    private void setupSteeringFile(String steering) {
         sessionState.jobManager.setup(new File(steering));
     }
 
@@ -243,83 +276,121 @@
      * @param steering The steering resource.
      * @throws IOException if there is a problem setting up or accessing the resource.
      */
-    void setupSteeringResource(String steering) throws IOException {
+    private void setupSteeringResource(String steering) throws IOException {
         InputStream is = this.getClass().getClassLoader().getResourceAsStream(steering);
         if (is == null)
             throw new IOException("Steering resource is not accessible or does not exist.");
         sessionState.jobManager.setup(is);
         is.close();
     }
+
+    synchronized void stop() {
+
+        // Kill session watchdog thread.
+        killWatchdogThread();
+
+        // Wake up all ET stations to unblock the system and make sure secondary stations are detached.
+        wakeUpEtStations();
         
-    synchronized void stop() {
-        
-        // Kill session watchdog thread.
-        logger.fine("killing watchdog thread ...");
-        killWatchdogThread();
-        logger.fine("watchdog thread killed");
-        
-        // Wake up ET system in case it is blocked in a getEvents() call.
-        if (sessionState.connection != null) {
-            try {
-                logger.fine("waking up ET stations ...");
-                sessionState.connection.getEtSystem().wakeUpAll(sessionState.connection.getEtStation());
-                logger.fine("ET stations woken up");
-            } catch (IOException | EtException | EtClosedException e) {
-                e.printStackTrace();
-            }
-        }
-        
-        // Stop event processing.
-        logger.fine("commanding event processing to stop ...");
+        // Stop the event processing now that ET system is unblocked.
+        logger.fine("sending STOP command to loop ...");
         sessionState.loop.execute(Command.STOP);
-        logger.fine("event processing commanded to stop");
-        
-        // Cleanup the event processing thread.
+        logger.fine("loop got command STOP");
+
+        // Cleanup the event processing thread since it was told to stop now.
         try {
-            logger.fine("joining on event processing thread ...");
+            logger.fine("waiting for event processing thread to end ...");
             sessionState.processingThread.join();
-            logger.fine("event processing thread joined");
-            
-            // Invalidate event processing thread.
-            sessionState.processingThread = null;
+            logger.fine("event processing thread ended");   
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
-        
+
         // Notify of last error that occurred in event processing.
         if (sessionState.loop.getLastError() != null) {
             // Log the error.
             application.errorHandler.setError(sessionState.loop.getLastError()).log();
         }
+
+        // Invalidate the loop.
+        sessionState.loop = null;
+
+        // Disconnect from the ET system.
+        disconnect();
         
-        // Invalidate loop.
-        sessionState.loop = null;
-               
-        // Disconnect from the ET system.
-        logger.fine("disconnecting from ET system ...");
-        disconnect();
-        logger.fine("ET system disconnected");
-    }
-    
-    /**
-     * Start event processing on the event processing thread
-     * and start the watchdog thread.
+        // Invalidate the event processing object so it is unusable now.
+        invalidate();
+    }
+
+    /**
+     * Wake up all ET stations associated with event processing.
+     */
+    private void wakeUpEtStations() {
+        if (sessionState.connection != null) {
+            logger.fine("waking up ET stations ...");
+
+            // Wake up secondary ET stations.
+            for (RunnableEtStation station : sessionState.stations) {
+                if (station.getEtStation().isUsable()) {
+                    // Wake up the station which will automatically trigger a detach.
+                    try {
+                        logger.finest("waking up " + station.getEtStation().getName() + " ...");
+                        sessionState.connection.getEtSystem().wakeUpAll(station.getEtStation());
+                        logger.finest(station.getEtStation().getName() + " woken up");
+                    } catch (IOException | EtException | EtClosedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            // Wait for station threads to die after being woken up.
+            while (sessionState.stationThreadGroup.activeCount() != 0) {
+                logger.finest("waiting for station threads to die ...");
+                Object lock = new Object();
+                synchronized (lock) {
+                    try {
+                        lock.wait(500);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            logger.finest("destroying station thread group");
+            sessionState.stationThreadGroup.destroy();
+            logger.finest("station thread group destroyed");
+
+            // Wake up the primary ET station doing the event processing.
+            logger.finest("waking up event processing station ...");
+            try {
+                sessionState.connection.getEtSystem().wakeUpAll(sessionState.connection.getEtStation());
+                logger.finest("event processing station woken up");
+            } catch (IOException | EtException | EtClosedException e) {
+                e.printStackTrace();
+            }
+
+            logger.finest("ET stations woken up");
+        }
+    }
+
+    /**
+     * Start event processing on the event processing thread and start the watchdog thread.
      */
     synchronized void start() {
-        
+
         logger.fine("event processing threads are starting");
-        
+
         // Start the event processing thread.
         sessionState.processingThread = new EventProcessingThread(sessionState.loop);
         sessionState.processingThread.start();
-        
+
         // Start the watchdog thread which will auto-disconnect when event processing is done.
         sessionState.sessionWatchdogThread = new SessionWatchdogThread(sessionState.processingThread);
         sessionState.sessionWatchdogThread.start();
-        
+
         logger.fine("started event processing threads");
     }
-    
+
     /**
      * Notify the event processor to pause processing.
      */
@@ -331,7 +402,7 @@
         }
         logger.finest("paused");
     }
-    
+
     /**
      * Get next event if in pause mode.
      */
@@ -344,7 +415,7 @@
         }
         logger.finest("got next event");
     }
-    
+
     /**
      * Resume processing events from pause mode.
      */
@@ -352,19 +423,19 @@
         logger.finest("resuming");
         if (application.connectionModel.getPaused()) {
             // Notify event processor to continue.
-            sessionState.loop.resume();        
+            sessionState.loop.resume();
             application.connectionModel.setPaused(false);
         }
         logger.finest("resumed");
     }
-    
+
     /**
      * Interrupt and join to the processing watchdog thread.
      */
     synchronized void killWatchdogThread() {
-        logger.fine("killing watchdog thread");
         // Is the session watchdog thread not null?
         if (sessionState.sessionWatchdogThread != null) {
+            logger.finest("killing watchdog thread ...");
             // Is the thread still alive?
             if (sessionState.sessionWatchdogThread.isAlive()) {
                 // Interrupt the thread which should cause it to stop.
@@ -379,10 +450,10 @@
             }
             // Set the thread object to null.
             sessionState.sessionWatchdogThread = null;
-        }
-        logger.fine("watchdog thread killed");
-    }
-    
+            logger.finest("watchdog thread killed");
+        }
+    }
+
     /**
      * Cleanup the ET connection.
      */
@@ -390,15 +461,15 @@
         if (sessionState.connection != null) {
             logger.fine("closing ET connection");
             if (sessionState.connection.getEtSystem().alive()) {
-                logger.fine("cleaning up the connection ...");
+                logger.finest("cleaning up the connection ...");
                 sessionState.connection.cleanup();
-                logger.fine("connection cleanup successful");
+                logger.finest("connection cleanup successful");
             }
             sessionState.connection = null;
             logger.fine("ET connection closed");
         }
     }
-    
+
     /**
      * True if the processing thread is active.
      * @return True if processing thread is active.
@@ -406,71 +477,109 @@
     boolean isActive() {
         return sessionState.processingThread != null && sessionState.processingThread.isAlive();
     }
-    
+
     /**
      * Connect to the ET system using the current connection settings.
      */
-    synchronized void connect() throws IOException {
-        logger.fine("connecting to ET system");
+    synchronized void connect() throws IOException {        
         // Setup the network connection if using an ET server.
         if (usingEtServer()) {
             // Create a connection to the ET server.
             try {
+                logger.fine("connecting to ET system ...");
+                
+                // Create the main ET system connection.
                 createEtConnection();
+
+                // Add an attachment that listens for DAQ configuration changes via physics SYNC events.
+                createPhysicsSyncStation();
+                
+                // Add an attachment that listens for PRESTART events.
+                createPreStartStation();
+                
             } catch (Exception e) {
                 throw new IOException(e);
             }
+            
+            logger.fine("ET system is connected");
         } else {
             // This is when a direct file source is used and ET is not needed.
             application.connectionModel.setConnectionStatus(ConnectionStatus.CONNECTED);
         }
-        logger.fine("ET system is connected");
-    }
-    
+        
+    }
+
     /**
      * True if using an ET server.
      * @return True if using an ET server.
      */
     boolean usingEtServer() {
         return application.configurationModel.getDataSourceType().equals(DataSourceType.ET_SERVER);
-    }    
-    
-    /**
-     * Create a connection to an ET system using current parameters from the GUI. If successful, the
-     * application's ConnectionStatus is changed to CONNECTED.
-     */
-    void createEtConnection() {
+    }
+
+    /**
+     * Create a connection to an ET system using current parameters from the GUI. 
+     */
+    synchronized void createEtConnection() {
         // Setup connection to ET system.
         sessionState.connection = EtSystemUtil.createEtConnection(application.configurationModel);
 
         if (sessionState.connection != null) {
             // Set status to connected as there is now a live ET connection.
             application.connectionModel.setConnectionStatus(ConnectionStatus.CONNECTED);
-            //logger.info("successfully connected to ET system");
         } else {
             application.errorHandler.setError(new RuntimeException("Failed to create ET connection.")).log().printStackTrace().raiseException();
         }
     }
+
+    /**
+     * Create the ET that listens for DAQ configuration change via SYNC events.
+     */
+    private void createPhysicsSyncStation() {
+        logger.fine("creating physics SYNC station ...");       
+        PhysicsSyncEventStation configStation = new PhysicsSyncEventStation(
+                this.sessionState.connection.getEtSystem(),
+                this.sessionState.connection.getEtStation().getName() + "_PhysicsSync",
+                getNextStationPosition());
+        sessionState.stations.add(configStation);
+        new Thread(sessionState.stationThreadGroup, configStation).start();
+        logger.fine("physics SYNC station created");
+    }
     
     /**
-     * Disconnect from the current ET session with a particular status.
+     * Create the ET station that listens for GO events in order to initialize the conditions system.
+     */
+    private void createPreStartStation() {
+        logger.fine("creating PRESTART station ...");
+        String detectorName = this.application.configurationModel.getDetectorName();
+        EtSystem system = this.sessionState.connection.getEtSystem();
+        String stationName = this.sessionState.connection.getEtStation().getName() + "_PreStart";
+        int order = getNextStationPosition();
+        PreStartEtStation preStartStation = new PreStartEtStation(
+                detectorName, 
+                system, 
+                stationName, 
+                order);
+        sessionState.stations.add(preStartStation);
+        new Thread(sessionState.stationThreadGroup, preStartStation).start();
+        logger.fine("PRESTART station created");
+    }
+
+    /**
+     * Disconnect from the current ET session.
      * @param status The connection status.
      */
     synchronized void disconnect() {
-        
-        logger.fine("disconnecting");
                 
         // Cleanup the ET connection.
         closeEtConnection();
-                              
+
         // Change application state to disconnected.
         application.connectionModel.setConnectionStatus(ConnectionStatus.DISCONNECTED);
-        
-        logger.fine("disconnected");
-    }    
-               
-    /**
-     * This class notifies the application to disconnect if the event processing thread completes.     
+    }
+
+    /**
+     * This class notifies the application to disconnect if the event processing thread completes.
      */
     class SessionWatchdogThread extends Thread {
 
@@ -479,21 +588,39 @@
         SessionWatchdogThread(Thread processingThread) {
             this.processingThread = processingThread;
         }
-        
+
         public void run() {
             try {
-                // When the event processing thread finishes, the session should be stopped and a
-                // disconnect should occur.
+                // This thread waits on the event processing thread to die.
                 processingThread.join();
-                                
+
                 // Activate a disconnect using the ActionEvent which is used by the disconnect button.
-                logger.fine("processing thread ended so automatic disconnect is happening");
+                logger.finest("processing thread ended so automatic disconnect is happening");
                 application.actionPerformed(new ActionEvent(Thread.currentThread(), 0, Commands.DISCONNECT));
-                               
+
             } catch (InterruptedException e) {
-                logger.fine("SessionWatchdogThread got interrupted");
+                logger.finest("SessionWatchdogThread got interrupted");
                 // This happens when the thread is interrupted by the user pressing the disconnect button.
-            }            
-        }
+            }
+        }
+    }
+    
+    void invalidate() {
+
+        this.application = null;
+        this.conditionsListeners = null;
+        this.drivers = null;
+        this.logger = null;
+        this.processors = null;        
+
+        this.sessionState.jobManager = null;
+        this.sessionState.eventBuilder = null;
+        this.sessionState.loop = null;
+        this.sessionState.processingThread = null;
+        this.sessionState.sessionWatchdogThread = null;
+        this.sessionState.stationThreadGroup = null;
+        this.sessionState.stations = null;
+        this.sessionState.connection = null;
+        this.sessionState = null;
     }
 }

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java	Sun Mar 15 11:31:15 2015
@@ -13,7 +13,7 @@
 import org.hps.monitoring.application.model.ConfigurationModel;
 
 /**
- * 
+ * This is a combo box used to filter the log table messages by level.
  * @author Jeremy McCormick <[log in to unmask]>
  */
 class LogLevelFilterComboBox extends JComboBox<Level> implements ActionListener, PropertyChangeListener {

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java	Sun Mar 15 11:31:15 2015
@@ -5,12 +5,10 @@
 import hep.aida.jfree.plotter.PlotterRegionListener;
 import hep.aida.ref.remote.rmi.client.RmiStoreFactory;
 
-import java.awt.Dimension;
-import java.awt.Rectangle;
-import java.awt.Robot;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
@@ -40,6 +38,7 @@
 import org.hps.monitoring.application.LogTable.LogRecordModel;
 import org.hps.monitoring.application.model.Configuration;
 import org.hps.monitoring.application.model.ConfigurationModel;
+import org.hps.monitoring.application.model.ConnectionStatus;
 import org.hps.monitoring.application.model.ConnectionStatusModel;
 import org.hps.monitoring.application.model.RunModel;
 import org.hps.monitoring.application.util.AIDAServer;
@@ -49,12 +48,11 @@
 import org.hps.monitoring.application.util.TableExporter;
 import org.hps.monitoring.plotting.MonitoringAnalysisFactory;
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
-import org.hps.monitoring.subsys.StatusCode;
 import org.hps.monitoring.subsys.SystemStatus;
-import org.hps.monitoring.subsys.SystemStatusListener;
 import org.hps.monitoring.subsys.SystemStatusRegistry;
 import org.hps.record.composite.CompositeRecordProcessor;
 import org.hps.record.enums.DataSourceType;
+import org.lcsim.conditions.ConditionsListener;
 import org.lcsim.util.Driver;
 import org.lcsim.util.aida.AIDA;
 import org.lcsim.util.log.DefaultLogFormatter;
@@ -62,12 +60,11 @@
 /**
  * This is the primary class that implements the monitoring GUI application.
  * It should not be used directly.  Instead the {@link Main} class should be
- * used from the command line or via the supplied script built automatically 
- * by Maven.
+ * used from the command line.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
-final class MonitoringApplication implements ActionListener, PropertyChangeListener, SystemStatusListener {
+final class MonitoringApplication implements ActionListener, PropertyChangeListener {
 
     // Statically initialize logging, which will be fully setup later.
     static final Logger logger;
@@ -78,14 +75,15 @@
 
     // Default log stream.
     MonitoringApplicationStreamHandler streamHandler;
+    LogHandler logHandler;
     PrintStream sysOut = System.out;
     PrintStream sysErr = System.err;
     
     // Application error handling.
-    final ErrorHandler errorHandler;
+    ErrorHandler errorHandler;
    
     // The main GUI components inside a JFrame.
-    final MonitoringApplicationFrame frame;    
+    MonitoringApplicationFrame frame;    
     
     // The primary data models.
     final RunModel runModel = new RunModel();
@@ -150,41 +148,86 @@
             super.setOutputStream(out);
         }        
     }
-             
+                 
     /**
      * Instantiate and show the monitoring application with the given configuration.
      * @param configuration The Configuration object containing application settings.
      */
     MonitoringApplication(Configuration configuration) {
-                
-        // Setup the main GUI component.
-        frame = new MonitoringApplicationFrame(this);
-        
-        // Setup the error handler.
-        errorHandler = new ErrorHandler(frame, logger);
+        
+        try {
+        
+            // Setup the main GUI component.
+            frame = new MonitoringApplicationFrame(this);
+            
+            // Add window listener to perform clean shutdown.
+            frame.addWindowListener(new WindowListener() {
+
+                @Override
+                public void windowOpened(WindowEvent e) {
+                }
+
+                @Override
+                public void windowClosing(WindowEvent e) {
+                }
+
+                @Override
+                public void windowClosed(WindowEvent e) {
+                    exit();
+                }
+
+                @Override
+                public void windowIconified(WindowEvent e) {
+                }
+
+                @Override
+                public void windowDeiconified(WindowEvent e) {
+                }
+
+                @Override
+                public void windowActivated(WindowEvent e) {
+                }
+
+                @Override
+                public void windowDeactivated(WindowEvent e) {
+                }
+            });
+        
+            // Setup the error handler.
+            errorHandler = new ErrorHandler(frame, logger);
                        
-        // Add this class as a listener on the configuration model.
-        configurationModel.addPropertyChangeListener(this);
-        
-        // Setup the logger.
-        setupLogger();
+            // Add this class as a listener on the configuration model.
+            configurationModel.addPropertyChangeListener(this);
+        
+            // Setup the logger.
+            setupLogger();
                
-        // Setup AIDA plotting and connect it to the GUI.
-        setupAida();
-        
-        // Set the configuration.
-        if (configuration != null) {
-            // There was a user specified configuration.
-            this.configuration = configuration;
-        } else {
-            // Use the default configuration.
-            this.configuration = new Configuration(DEFAULT_CONFIGURATION);
-        }
+            // Setup AIDA plotting and connect it to the GUI.
+            setupAida();
+        
+            // Set the configuration.
+            if (configuration != null) {
+                // There was a user specified configuration.
+                this.configuration = configuration;
+            } else {
+                // Use the default configuration.
+                this.configuration = new Configuration(DEFAULT_CONFIGURATION);
+            }
                                       
-        // Load the configuration.
-        loadConfiguration(this.configuration);
-                
-        logger.info("application initialized successfully");
+            // Load the configuration.
+            loadConfiguration(this.configuration);
+        
+            frame.setEnabled(true);
+        
+            logger.info("application initialized successfully");
+        
+        } catch (Exception e) {
+            // Don't use the ErrorHandler here because we don't know that it initialized successfully.
+            System.err.println("MonitoringApplication failed to initialize without errors!");
+            DialogUtil.showErrorDialog(null, "Error Starting Monitoring Application", "Monitoring application failed to initialize.");
+            e.printStackTrace();
+            System.exit(1);
+        }        
     }
     
     /**
@@ -192,7 +235,8 @@
      */
     void setupLogger() {
         logger.setUseParentHandlers(false);        
-        logger.addHandler(new LogHandler());
+        logHandler = new LogHandler();
+        logger.addHandler(logHandler);
         streamHandler = new MonitoringApplicationStreamHandler(System.out);
         logger.addHandler(streamHandler);
         for (Handler handler : logger.getHandlers()) {
@@ -201,7 +245,7 @@
         logger.setLevel(DEFAULT_LEVEL);
         logger.info("logging initialized");
     }
-    
+        
     /**
      * Static utility method for creating new instance.
      * @param configuration The application settings.
@@ -217,7 +261,9 @@
      */
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
-        // TODO: Handle log level configuration change here.
+        if (evt.getPropertyName().equals(ConfigurationModel.LOG_LEVEL_PROPERTY)) {
+            setLogLevel();
+        }
     }
     
     /**
@@ -232,11 +278,12 @@
         if (Commands.CONNECT.equals(command)) {
             startSession();
         } else if (Commands.DISCONNECT.equals(command)) {
-            processing.stop();
+            runDisconnectThread();
         } else if (Commands.SAVE_PLOTS.equals(command)) {
             savePlots();
         } else if (Commands.EXIT.equals(command)) {
-            exit();
+            // This will trigger the window closing action that cleans everything up.
+            frame.dispose();
         } else if (Commands.PAUSE.equals(command)) { 
             processing.pause();
         } else if (Commands.NEXT.equals(command)) {
@@ -265,8 +312,6 @@
             closeFile();
         } else if (Commands.SAVE_SCREENSHOT.equals(command)) {
             saveScreenshot();
-        } else if (Commands.LOG_LEVEL_CHANGED.equals(command)) {
-            setLogLevel();
         } else if (Commands.SAVE_LOG_TABLE.equals(command)) {
             saveLogTable();
         } else if (Commands.CLEAR_LOG_TABLE.equals(command)) {
@@ -327,11 +372,14 @@
      * Reset the plots and clear the tabs in the plot window.
      */
     void resetPlots() {
-
+        
+        // Clear global list of registered plotters.
+        MonitoringPlotFactory.getPlotterRegistry().clear();  
+        
         // Clear the static AIDA tree in case plots are hanging around from previous sessions.
         AIDA.defaultInstance().clearAll();
 
-        // Reset plot panel which removes all tabs.
+        // Reset plot panel which removes all its tabs.
         frame.plotPanel.reset();
         
         logger.info("plots were cleared");
@@ -341,43 +389,20 @@
      * Configure the system status monitor panel for a new job.
      */
     void setupSystemStatusMonitor() {
+        
         // Clear the system status monitor table.
-        frame.systemStatusTable.getTableModel().clear();
+        frame.systemStatusPanel.clear();
 
         // Get the global registry of SystemStatus objects.
         SystemStatusRegistry registry = SystemStatusRegistry.getSystemStatusRegistery();
 
         // Process the SystemStatus objects.
         for (SystemStatus systemStatus : registry.getSystemStatuses()) {
-            // Add a row to the table for every SystemStatus.
-            frame.systemStatusTable.getTableModel().addSystemStatus(systemStatus);
-
-            // Add this class as a listener so all status changes can be logged.
-            systemStatus.addListener(this);
+            // This will add the status to the two tables.
+            frame.systemStatusPanel.addSystemStatus(systemStatus);
         }
         
         logger.info("system status monitor initialized successfully");
-    }
-    
-    /**
-     * Hook for logging all status changes from the system status monitor.
-     */
-    @Override
-    public void statusChanged(SystemStatus status) {
-
-        // Choose the appropriate log level.
-        Level level = Level.FINE;
-        if (status.getStatusCode().equals(Level.WARNING)) {
-            level = Level.WARNING;
-        } else if (status.getStatusCode().ordinal() >= StatusCode.ERROR.ordinal()) {
-            level = Level.SEVERE;
-        }
-        
-        // Log all status changes.
-        logger.log(level, "STATUS, " + "subsys: " + status.getSubsystem() + ", " 
-                + "code: " + status.getStatusCode().name() 
-                + ", " + "descr: " + status.getDescription() 
-                + ", " + "mesg: " + status.getMessage());
     }
     
     /**
@@ -399,13 +424,18 @@
 
             // List of extra composite record processors including the updater for the RunPanel.
             List<CompositeRecordProcessor> processors = new ArrayList<CompositeRecordProcessor>();
-            processors.add(frame.runPanel.new RunPanelUpdater());
-            
+            processors.add(frame.dashboardPanel.new EventDashboardUpdater());
+            
+            // Add Driver to update the trigger diagnostics tables.
             List<Driver> drivers = new ArrayList<Driver>();
             drivers.add(frame.triggerPanel.new TriggerDiagnosticGUIDriver());
-            
-            // Initialize event processing with the list of processors and reference to the application.
-            processing = new EventProcessing(this, processors, drivers);
+
+            // Add listener to push conditions changes to conditions panel.
+            List<ConditionsListener> conditionsListeners = new ArrayList<ConditionsListener>();
+            conditionsListeners.add(frame.conditionsPanel.new ConditionsPanelListener());
+            
+            // Instantiate the event processing wrapper.
+            processing = new EventProcessing(this, processors, drivers, conditionsListeners);
             
             // Connect to the ET system, if applicable.
             processing.connect();
@@ -418,7 +448,7 @@
             // Setup the system status monitor table.
             setupSystemStatusMonitor();
                                             
-            // Start the event processing thread.
+            // Start the event processing thread.            
             processing.start();            
             
             logger.info("new session successfully initialized");
@@ -437,17 +467,17 @@
     }
            
     /**
-     * Exit from the application.
+     * Exit from the application from exit menu item or hitting close window button.
      */
     void exit() {        
-        if (processing != null && processing.isActive()) {
+        if (connectionModel.isConnected()) {
             processing.stop();
         }
-        frame.setVisible(false);
+        logHandler.setLevel(Level.OFF);
         logger.info("exiting the application");
-        logger.getHandlers()[0].flush();
+        streamHandler.flush();
         System.exit(0);
-    }              
+    }
             
     /**
      * Save AIDA plots to a file using a file chooser.
@@ -601,6 +631,7 @@
     /**
      * Save a screenshot to a file using a file chooser.
      */
+    // FIXME: This might need to be on a new thread to allow the GUI to redraw w/o chooser visible.
     void saveScreenshot() {
         JFileChooser fc = new JFileChooser();
         fc.setAcceptAllFileFilterUsed(false);
@@ -615,8 +646,17 @@
             if (!fileName.endsWith("." + format)) {
                 fileName += "." + format;
             }
+            frame.repaint();
+            Object lock = new Object();
+            synchronized (lock) {
+                try {
+                    lock.wait(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
             writeScreenshot(fileName, format);
-            DialogUtil.showInfoDialog(frame, "Screenshot Saved", "Screenshot was saved to file.");
+            DialogUtil.showInfoDialog(frame, "Screenshot Saved", "Screenshot was saved to file" + '\n' + fileName);
             logger.info("saved screenshot to " + fileName);
         }
     }
@@ -626,15 +666,13 @@
      * @param fileName The name of the output file.
      */
     void writeScreenshot(String fileName, String format) {
-        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-        Rectangle screenRectangle = new Rectangle(screenSize);
+        BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_RGB);
+        frame.paint(image.getGraphics()); 
         try {
-            Robot robot = new Robot();
-            BufferedImage image = robot.createScreenCapture(screenRectangle);
             ImageIO.write(image, format, new File(fileName));
-        } catch (Exception e) {
-            errorHandler.setError(e).setMessage("Failed to take screenshot.").printStackTrace().log().showErrorDialog();
-        }
+        } catch (IOException e) {
+            errorHandler.setError(e).setMessage("Failed to save screenshot.").printStackTrace().log().showErrorDialog();
+        }        
     }            
     
     /**
@@ -785,5 +823,19 @@
         frame.menu.stopAIDAServer();
         logger.info("AIDA server was stopped");
         DialogUtil.showInfoDialog(frame, "AIDA Server Stopped", "The AIDA server was stopped.");
-    }       
+    }    
+    
+    /**
+     * 
+     */
+    void runDisconnectThread() {
+        new Thread() {
+            public void run() {
+                logger.fine("disconnect thread is running ...");
+                connectionModel.setConnectionStatus(ConnectionStatus.DISCONNECTING);
+                MonitoringApplication.this.processing.stop();
+                logger.fine("disconnect thread finished!");
+            }
+        }.run();
+    }
 }

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplicationFrame.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplicationFrame.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplicationFrame.java	Sun Mar 15 11:31:15 2015
@@ -3,14 +3,11 @@
 import java.awt.BorderLayout;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
-import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
 import java.awt.Rectangle;
 
-import javax.swing.BoxLayout;
-import javax.swing.JComponent;
 import javax.swing.JFrame;
 import javax.swing.JPanel;
-import javax.swing.JScrollPane;
 import javax.swing.JSeparator;
 import javax.swing.JSplitPane;
 import javax.swing.JTabbedPane;
@@ -23,13 +20,14 @@
  */
 class MonitoringApplicationFrame extends JFrame {
             
-    RunPanel runPanel;    
+    EventDashboard dashboardPanel;    
     PlotPanel plotPanel;
     PlotInfoPanel plotInfoPanel;
     LogPanel logPanel;
-    SystemStatusTable systemStatusTable;
     JPanel buttonsPanel;
     TriggerDiagnosticsPanel triggerPanel;
+    ConditionsPanel conditionsPanel;
+    SystemStatusPanel systemStatusPanel;
     MenuBar menu; 
     
     JSplitPane mainSplitPane;
@@ -39,14 +37,10 @@
     DataSourceComboBox dataSourceComboBox;
     
     SettingsDialog settingsDialog;
-    
-    // Proportional layout parameters relative to the screen size.
-    static final double FULL_SIZE = 1.0;
-    static final double TOP_PANEL_HEIGHT = 0.05;
-    static final double BOTTOM_PANEL_HEIGHT = FULL_SIZE - TOP_PANEL_HEIGHT;
-    static final double LEFT_PANEL_WIDTH = 0.3;
-    static final double RIGHT_PANEL_WIDTH = FULL_SIZE - LEFT_PANEL_WIDTH;
-    static final double PLOT_PANEL_HEIGHT = 0.8;
+       
+    static final Rectangle BOUNDS = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
+    static final int PIXEL_WIDTH_MAX = (int) BOUNDS.getWidth();
+    static final int PIXEL_HEIGHT_MAX = (int) BOUNDS.getHeight();
     
     /**
      * 
@@ -54,20 +48,22 @@
      */
     public MonitoringApplicationFrame(
             MonitoringApplication application) {
+        
+        // Disable interaction until specifically enabled externally after initialization.
+        setEnabled(false);
                 
         // Create the content panel.
         JPanel contentPanel = new JPanel();
         setContentPane(contentPanel);
-        contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
+        contentPanel.setLayout(new BorderLayout());
         contentPanel.setOpaque(true);
-        setProportionalSize(contentPanel, FULL_SIZE, FULL_SIZE);
-        
+        contentPanel.setPreferredSize(new Dimension(PIXEL_WIDTH_MAX, PIXEL_HEIGHT_MAX));
+                
         // Create the top panel.
         JPanel topPanel = new JPanel();
         topPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 0));
-        setProportionalSize(topPanel, FULL_SIZE, TOP_PANEL_HEIGHT);
-        contentPanel.add(topPanel);
-        
+        contentPanel.add(topPanel, BorderLayout.NORTH);
+                
         // Create the connection status panel.
         JPanel connectionPanel = new ConnectionStatusPanel(application.connectionModel);
         topPanel.add(connectionPanel);
@@ -83,7 +79,6 @@
         
         // Add vertical separator.
         sep = new JSeparator(SwingConstants.VERTICAL);
-        sep.setPreferredSize(new Dimension(5, topPanel.getPreferredSize().height));
         topPanel.add(sep);
         
         // Add the data source combo box.
@@ -93,16 +88,14 @@
         // Create the bottom panel.
         JPanel bottomPanel = new JPanel();
         bottomPanel.setLayout(new BorderLayout());
-        setProportionalSize(bottomPanel, FULL_SIZE, BOTTOM_PANEL_HEIGHT);
-        contentPanel.add(bottomPanel);
-                                
+        contentPanel.add(bottomPanel, BorderLayout.CENTER);
+                                        
         // Create the left panel.
         JPanel leftPanel = new JPanel();
         leftPanel.setLayout(new BorderLayout());
-        setProportionalSize(leftPanel, LEFT_PANEL_WIDTH, FULL_SIZE);
-                        
+                            
         // Create the run dashboard.
-        runPanel = new RunPanel(application.runModel);
+        dashboardPanel = new EventDashboard(application.runModel);
 
         // Create the tabbed pane for content in bottom of left panel such as log table and system monitor.
         JTabbedPane tableTabbedPane = new JTabbedPane();
@@ -112,76 +105,58 @@
         tableTabbedPane.addTab("Log Messages", logPanel);
         
         // Create the system monitor.
-        systemStatusTable = new SystemStatusTable();
-        tableTabbedPane.addTab("System Status Monitor", new JScrollPane(systemStatusTable));
+        //systemStatusTable = new SystemStatusTable();
+        systemStatusPanel = new SystemStatusPanel();
+        tableTabbedPane.addTab("System Status Monitor", systemStatusPanel);
         
         // Add the trigger diagnostics tables.
         triggerPanel = new TriggerDiagnosticsPanel();
         tableTabbedPane.addTab("Trigger Diagnostics", triggerPanel);
         
+        // Add the conditions panel.
+        conditionsPanel = new ConditionsPanel();
+        tableTabbedPane.addTab("Detector Conditions", conditionsPanel);
+        
         // Vertical split pane in left panel.
-        leftSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, runPanel, tableTabbedPane);
-        leftSplitPane.setResizeWeight(0.5);
+        leftSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, dashboardPanel, tableTabbedPane);
+        leftSplitPane.setDividerLocation(250);
         leftPanel.add(leftSplitPane, BorderLayout.CENTER);
                                 
         // Create the right panel.
         JPanel rightPanel = new JPanel();
         rightPanel.setLayout(new BorderLayout());
-        
+                
         // Create the plot info panel.
         plotInfoPanel = new PlotInfoPanel();
                 
         // Create the plot panel.
-        plotPanel = new PlotPanel();
-        plotPanel.setVisible(true); // DEBUG
-        setProportionalSize(plotPanel, RIGHT_PANEL_WIDTH, PLOT_PANEL_HEIGHT);
+        plotPanel = new PlotPanel();        
+        plotInfoPanel.saveButton.addActionListener(plotPanel);
         
         // Create the right panel vertical split pane for displaying plots and their information and statistics.
         rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, plotPanel, plotInfoPanel);
-        setProportionalSize(rightSplitPane, RIGHT_PANEL_WIDTH, FULL_SIZE);
-        rightSplitPane.setResizeWeight(0.8);
+        rightSplitPane.setResizeWeight(0.7);
         rightPanel.add(rightSplitPane, BorderLayout.CENTER);
                        
         // Create the main horizontal split pane for dividing the left and right panels.
         mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
-        mainSplitPane.setResizeWeight(0.15);
+        mainSplitPane.setResizeWeight(0.2);
         bottomPanel.add(mainSplitPane, BorderLayout.CENTER);
         
         // Create the menu bar.
         menu = new MenuBar(application.configurationModel, application.connectionModel, application);
         setJMenuBar(menu);
         dataSourceComboBox.addActionListener(menu);
-                        
+        
+        // Setup the settings dialog box (invisible until activated).
+        settingsDialog = new SettingsDialog(application.configurationModel, application);        
+               
         // Setup the frame now that all components have been added.        
         pack();
         setExtendedState(JFrame.MAXIMIZED_BOTH);
-        setVisible(true);
-        
-        // Setup the settings dialog box.
-        settingsDialog = new SettingsDialog(application.configurationModel, application);
+        setVisible(true); 
     }
-    
-    /**
-     * Set the size of a Swing component using proportions of the current screen bounds.
-     * @param component The component to resize.
-     * @param scaleX The X scaling (must be between 0 and 1).
-     * @param scaleY The Y scaling (must be between 0 and 1).
-     * @param setSize Call the setSize method as well as setPreferredSize (which is the default).
-     * @return
-     */
-    void setProportionalSize(JComponent component, double scaleX, double scaleY) {                    
-        GraphicsConfiguration graphics = this.getGraphicsConfiguration();        
-        Rectangle bounds = graphics.getBounds();        
-        if (scaleX < 0 || scaleX > 1) {
-            throw new IllegalArgumentException("scaleX must be > 0 and <= 1.");
-        }
-        if (scaleY < 0 || scaleY > 1) {
-            throw new IllegalArgumentException("scaleY must be > 0 and <= 1.");
-        }
-        Dimension scaledDimension = new Dimension((int)(bounds.getWidth() * scaleX), (int)(bounds.getHeight() * scaleY));
-        component.setPreferredSize(scaledDimension);
-    }           
-    
+             
     /**
      * Restore default window settings.
      */

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java	Sun Mar 15 11:31:15 2015
@@ -15,10 +15,10 @@
 import hep.aida.ref.function.FunctionDispatcher;
 import hep.aida.ref.function.FunctionListener;
 
+import java.awt.Color;
 import java.awt.Component;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.EventObject;
@@ -27,6 +27,8 @@
 import java.util.TimerTask;
 
 import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JList;
 import javax.swing.JPanel;
@@ -47,6 +49,8 @@
     JComboBox<Object> plotComboBox;
     JTable infoTable = new JTable();
     DefaultTableModel model;
+    JButton saveButton;
+    
     PlotterRegion currentRegion;
     Object currentObject;
     static final int INSET_SIZE = 5;
@@ -63,11 +67,18 @@
      */
     @SuppressWarnings("unchecked")
     PlotInfoPanel() {
-
-        setLayout(new GridBagLayout());
-        setBorder(BorderFactory.createEmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE));
-
-        GridBagConstraints c;
+                
+        setLayout(new FlowLayout(FlowLayout.LEFT));
+
+        JPanel leftPanel = new JPanel();
+        leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
+           
+        JPanel buttonPanel = new JPanel();
+        saveButton = new JButton("Save Plots ...");
+        saveButton.setActionCommand(Commands.SAVE_SELECTED_PLOTS);
+        buttonPanel.add(saveButton);       
+        //c.anchor = GridBagConstraints.NORTHWEST;
+        leftPanel.add(buttonPanel);
 
         plotComboBox = new JComboBox<Object>();
         plotComboBox.setActionCommand(PLOT_SELECTED);
@@ -85,26 +96,17 @@
             }
         });
         plotComboBox.addActionListener(this);
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 0;
-        c.fill = GridBagConstraints.HORIZONTAL;
-        c.insets = new Insets(0, 0, INSET_SIZE, 0);
-        add(plotComboBox, c);
-
+        leftPanel.add(plotComboBox);
+        
         String data[][] = new String[0][0];
         model = new DefaultTableModel(data, COLUMN_NAMES);
         infoTable.setModel(model);
-
-        // FIXME: Are these adequate column size settings? Could prob be bigger...
         infoTable.getColumn("Field").setMinWidth(25);
         infoTable.getColumn("Value").setMinWidth(20);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 1;
-        c.fill = GridBagConstraints.BOTH;
-        add(infoTable, c);
+        infoTable.setMinimumSize(new Dimension(100, 200));
+        leftPanel.add(infoTable);
+        
+        add(leftPanel);
     }
 
     /**

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotPanel.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotPanel.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/PlotPanel.java	Sun Mar 15 11:31:15 2015
@@ -1,15 +1,26 @@
 package org.hps.monitoring.application;
 
+import hep.aida.IPlotter;
+
 import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
 
+import javax.swing.JFileChooser;
 import javax.swing.JPanel;
 import javax.swing.JTabbedPane;
+
+import org.hps.monitoring.application.util.DialogUtil;
+import org.hps.monitoring.plotting.MonitoringPlotFactory;
 
 /**
  * This is the panel containing the tabs with the monitoring plots.
  * @author Jeremy McCormick <[log in to unmask]>
  */
-class PlotPanel extends JPanel {
+class PlotPanel extends JPanel implements ActionListener {
     
     private JTabbedPane plotPane;    
     
@@ -23,8 +34,55 @@
     JTabbedPane getPlotPane() {
         return plotPane;
     }
+
+    /**
+     * Get the indices of the current selected tabs.
+     * @return The indices of the current tabs.
+     */
+    int[] getSelectedTabs() {
+        int[] indices = new int[2];
+        indices[0] = plotPane.getSelectedIndex();
+        Component component = plotPane.getSelectedComponent();
+        if (component instanceof JTabbedPane) {
+            indices[1] = ((JTabbedPane)component).getSelectedIndex();
+        } 
+        return indices;
+    }
+    
+    public void actionPerformed(ActionEvent event) {
+        if (event.getActionCommand().equals(Commands.SAVE_SELECTED_PLOTS)) {
+            int[] indices = getSelectedTabs();
+            IPlotter plotter = MonitoringPlotFactory.getPlotterRegistry().find(indices[0], indices[1]);
+            if (plotter != null) {
+                savePlotter(plotter);
+            } else {
+                DialogUtil.showErrorDialog(this, "Error Finding Plots", "No plots found in selected tab.");
+            }
+        }
+    }
+            
+    static final String DEFAULT_FORMAT = "png";
+    void savePlotter(IPlotter plotter) {
+        JFileChooser fc = new JFileChooser();
+        fc.setAcceptAllFileFilterUsed(false);
+        fc.setDialogTitle("Save Plots - " + plotter.title());
+        fc.setCurrentDirectory(new File("."));
+        int r = fc.showSaveDialog(this);
+        if (r == JFileChooser.APPROVE_OPTION) {                        
+            String path = fc.getSelectedFile().getPath();
+            if (path.lastIndexOf(".") == -1) {
+                path += "." + DEFAULT_FORMAT;
+            }
+            try {
+                plotter.writeToFile(path);
+            } catch (IOException e) {
+                e.printStackTrace();
+                DialogUtil.showErrorDialog(this, "Error Saving Plots", "There was an error saving the plots.");
+            }
+        }        
+    }
     
     void reset() {
-        plotPane.removeAll();
-    }
+        plotPane.removeAll();        
+    }    
 }

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java	Sun Mar 15 11:31:15 2015
@@ -1,6 +1,5 @@
 package org.hps.monitoring.application;
 
-import java.awt.Color;
 import java.awt.Component;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -17,8 +16,7 @@
 import org.hps.monitoring.subsys.StatusCode;
 
 /**
- * A GUI window for showing changes to {@link org.hps.monitoring.subsys.SystemStatus} objects using
- * a <code>JTable</code>.
+ * This table shows the current state of {@link org.hps.monitoring.subsys.SystemStatus} objects.
  */
 class SystemStatusTable extends JTable {
 
@@ -37,28 +35,7 @@
 
                 // Color code the cell by its status.
                 StatusCode statusCode = StatusCode.valueOf((String) value);
-                if (statusCode.ordinal() >= StatusCode.ERROR.ordinal()) {
-                    // Any type of error is red.
-                    label.setBackground(Color.RED);
-                } else if (statusCode.equals(StatusCode.WARNING)) {
-                    // Warnings are yellow.
-                    label.setBackground(Color.YELLOW);
-                } else if (statusCode.equals(StatusCode.OKAY)) {
-                    // Okay is green.
-                    label.setBackground(Color.GREEN);
-                } else if (statusCode.equals(StatusCode.OFFLINE)) {
-                    // Offline is orange.
-                    label.setBackground(Color.ORANGE);
-                } else if (statusCode.equals(StatusCode.UNKNOWN)) {
-                    // Unknown is gray.
-                    label.setBackground(Color.GRAY);
-                } else if (statusCode.equals(StatusCode.CLEARED)) {
-                    // Cleared is light gray.
-                    label.setBackground(Color.LIGHT_GRAY);
-                } else {
-                    // Default is white, though this shouldn't happen!
-                    label.setBackground(Color.WHITE);
-                }
+                label.setBackground(statusCode.getColor());
                 return label;
             }
         });

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java	Sun Mar 15 11:31:15 2015
@@ -10,6 +10,7 @@
 public enum ConnectionStatus {
 
     DISCONNECTED(Color.RED),
+    DISCONNECTING(Color.YELLOW),
     CONNECTED(Color.GREEN);
     
     Color color;    

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatusModel.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatusModel.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatusModel.java	Sun Mar 15 11:31:15 2015
@@ -50,4 +50,16 @@
             listener.propertyChange(new PropertyChangeEvent(this, PAUSED_PROPERTY, oldValue, this.paused));
         }
     }
+    
+    public boolean isConnected() {
+        return this.connectionStatus == ConnectionStatus.CONNECTED;
+    }
+    
+    public boolean isDisconnected() {
+        return this.connectionStatus == ConnectionStatus.DISCONNECTED;
+    }
+    
+    public boolean isDisconnecting() {
+        return this.connectionStatus == ConnectionStatus.DISCONNECTING;
+    }
 }

Modified: java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/RunModel.java
 =============================================================================
--- java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/RunModel.java	(original)
+++ java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/model/RunModel.java	Sun Mar 15 11:31:15 2015
@@ -3,7 +3,7 @@
 import java.util.Date;
 
 /**
- * Backing model for run information that shows in the {@link org.hps.monitoring.application.RunPanel}.
+ * Backing model for run information that shows in the {@link org.hps.monitoring.application.EventDashboard}.
  */
 public final class RunModel extends AbstractModel {
 

Modified: java/trunk/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop
 =============================================================================
--- java/trunk/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop	(original)
+++ java/trunk/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop	Sun Mar 15 11:31:15 2015
@@ -29,8 +29,7 @@
 Host=localhost
 Port=11111
 StationPosition=1
-# Prescale was 1
-Prescale=0
+Prescale=1
 QueueSize=0
 StationName=MY_STATION
 Verbose=false