Print

Print


Commit in hps-java/src/main/java/org/lcsim/hps/monitoring on MAIN
EtEventListener.java+51added 1.1
EventButtonsPanel.java+88added 1.1
FieldsPanel.java+133added 1.1
ConnectionPanel.java+20-2501.18 -> 1.19
ConnectionStatusPanel.java+24-191.5 -> 1.6
DefaultEtEventProcessor.java+157-321.5 -> 1.6
EtConnection.java+76-761.9 -> 1.10
EtEventProcessor.java+16-201.1 -> 1.2
EventPanel.java+89-2171.13 -> 1.14
JobPanel.java+89-2311.10 -> 1.11
MonitoringApplication.java+409-1781.35 -> 1.36
MonitoringCommands.java+29-231.8 -> 1.9
svt/SensorOccupancyPlotsDriver.java+143-1421.1 -> 1.2
+1324-1188
3 added + 10 modified, total 13 files
checkpoint some major revisions to monitoring system; refactored how the tab panels are built; added ability to pause between events; added selectable log level; probably some other stuff I forgot too

hps-java/src/main/java/org/lcsim/hps/monitoring
EtEventListener.java added at 1.1
diff -N EtEventListener.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EtEventListener.java	3 May 2012 16:59:28 -0000	1.1
@@ -0,0 +1,51 @@
+package org.lcsim.hps.monitoring;
+
+/**
+ * Interface for notifying listeners of ET ring events.
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @version $Id: EtEventListener.java,v 1.1 2012/05/03 16:59:28 jeremy Exp $
+ */
+// FIXME: Should all the callback methods get an EtEvent or event number?
+interface EtEventListener {
+
+    /**
+     * Called at beginning of event processing session.
+     */
+    void begin();
+
+    /**
+     * Called at start of single EtEvent.
+     */
+    void startOfEvent();
+    
+    /**
+     * Called at end of processing single EtEvent.
+     */
+    void endOfEvent();
+
+    /**
+     * Called when an error occurs processing current EtEvent.
+     */
+    void errorOnEvent();
+
+    /**
+     * Called when event processing session finishes.
+     */
+    void finish();
+    
+    /** 
+     * Called when a Pre Start event is received from the ET ring,
+     * indicating start of run.
+     * @param seconds Unix time in seconds.
+     * @param runNumber The run number.
+     */
+    void prestart(int seconds, int runNumber);
+    
+    /**
+     * Called when an End Event is received from the ET ring,
+     * indicating end of run.
+     * @param seconds Unix time in seconds.
+     * @param nevents Number of events in run.
+     */
+    void endRun(int seconds, int nevents);
+}
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
EventButtonsPanel.java added at 1.1
diff -N EventButtonsPanel.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventButtonsPanel.java	3 May 2012 16:59:28 -0000	1.1
@@ -0,0 +1,88 @@
+package org.lcsim.hps.monitoring;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+
+public class EventButtonsPanel extends JPanel {
+    
+    JButton nextEventsButton;
+    JButton pauseButton;
+    JButton connectButton;
+    
+    EventButtonsPanel() {
+        
+        Insets insets = new Insets(1, 1, 1, 1);
+        
+        GridBagLayout layout = new GridBagLayout();
+        setLayout(layout);
+        
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridx = 0;
+        c.gridy = 0;
+        c.insets = insets;
+        connectButton = new JButton("Connect");
+        connectButton.setEnabled(true);
+        connectButton.setActionCommand(MonitoringCommands.connectCmd);
+        add(connectButton, c);
+        
+        c = new GridBagConstraints();        
+        c.gridx = 1;
+        c.gridy = 0;
+        c.insets = insets;
+        pauseButton = new JButton("Pause");
+        pauseButton.setActionCommand(MonitoringCommands.pauseCmd);
+        pauseButton.setEnabled(false);
+        add(pauseButton, c);
+        
+        c = new GridBagConstraints();        
+        c.gridx = 2;
+        c.gridy = 0;
+        c.insets = insets;
+        nextEventsButton = new JButton("Next Event");
+        nextEventsButton.setEnabled(false);
+        nextEventsButton.setActionCommand(MonitoringCommands.nextCmd);
+        add(nextEventsButton, c);
+    }
+    
+    void toggleConnectButton() {
+        if (connectButton.getText().equals("Connect")) {
+            connectButton.setText("Disconnect");
+            connectButton.setActionCommand(MonitoringCommands.disconnectCmd);
+        }
+        else {
+            connectButton.setText("Connect");
+            connectButton.setActionCommand(MonitoringCommands.connectCmd);
+        }
+    }
+        
+    void addActionListener(ActionListener listener) {
+        nextEventsButton.addActionListener(listener);
+        pauseButton.addActionListener(listener);
+        connectButton.addActionListener(listener);
+    }
+    
+    void enablePauseButton(boolean e) {
+        this.pauseButton.setEnabled(e);
+    }
+    
+    void enableNextEventsButton(boolean e) {
+        this.nextEventsButton.setEnabled(e);
+    }
+    
+    void setPauseModeState(boolean enable) {
+        this.nextEventsButton.setEnabled(enable);
+        if (enable) {
+            pauseButton.setText("Resume");
+            pauseButton.setActionCommand(MonitoringCommands.resumeCmd);
+        }
+        else {
+            pauseButton.setText("Pause");
+            pauseButton.setActionCommand(MonitoringCommands.pauseCmd);
+        }
+    }
+}
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
FieldsPanel.java added at 1.1
diff -N FieldsPanel.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ FieldsPanel.java	3 May 2012 16:59:28 -0000	1.1
@@ -0,0 +1,133 @@
+package org.lcsim.hps.monitoring;
+
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import java.awt.event.ActionListener;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+/**
+ * This class is used to provide utility methods for the data panels in the application tabs.
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @version $Id: FieldsPanel.java,v 1.1 2012/05/03 16:59:28 jeremy Exp $
+ *
+ */
+public class FieldsPanel extends JPanel {
+
+    private int currY = 0;    
+    private Insets insets;
+    private boolean editable = false;
+    
+    FieldsPanel(Insets insets, boolean editable) {
+        this.insets = insets;
+        this.editable = editable;
+    }
+    
+    FieldsPanel() {
+        this.insets = new Insets(1, 1, 1, 1);
+    }
+    
+    protected final JTextField addField(String name, int size) {
+        return addField(name, "", size, this.editable);
+    }
+    
+    protected final JTextField addField(String name, String value, int size) {
+        return addField(name, value, size, this.editable);
+    }
+    
+    protected final JTextField addField(String name, String value, String tooltip, int size, boolean editable) {
+        JTextField f = addField(name, value, size, editable);
+        f.setToolTipText(tooltip);
+        return f;
+    }
+    
+    protected final JTextField addField(String name, String value, int size, boolean editable) {
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridx = 0;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.WEST;
+        JLabel label = new JLabel(name + ":");
+        add(label, c);
+        
+        c = new GridBagConstraints();
+        c.gridx = 1;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.EAST;
+        JTextField field = new JTextField(value, size);
+        field.setHorizontalAlignment(JTextField.RIGHT);
+        field.setEditable(editable);
+        field.setBackground(Color.WHITE);
+        add(field, c);
+        
+        ++currY;
+        
+        return field;
+    }
+    
+    protected final JComboBox addComboBox(String name, String[] values) {
+        
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridx = 0;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.WEST;
+        JLabel waitModeLabel = new JLabel(name + ":");
+        waitModeLabel.setHorizontalAlignment(JLabel.LEFT);
+        add(waitModeLabel, c);
+
+        c = new GridBagConstraints();
+        c.gridx = 1;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.EAST;
+        JComboBox combo = new JComboBox(values);
+        combo.setEditable(editable);
+        add(combo, c);
+        
+        ++currY;
+        
+        return combo;
+    }
+    
+    protected final JCheckBox addCheckBox(String name, String tooltip, boolean selected, boolean enabled) {
+        JCheckBox c = addCheckBox(name, selected, enabled);
+        c.setToolTipText(tooltip);
+        return c;
+    }
+    
+    protected final JCheckBox addCheckBox(String name, boolean selected, boolean enabled) {
+        
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridx = 0;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.WEST;
+        JLabel label = new JLabel(name + ":");
+        add(label, c);
+        
+        c = new GridBagConstraints();
+        c.gridx = 1;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.EAST;
+        JCheckBox checkbox = new JCheckBox();
+        checkbox.setSelected(selected);
+        checkbox.setEnabled(enabled);
+        add(checkbox, c);
+        
+        ++currY;
+        
+        return checkbox;
+    }
+    
+    void addActionListener(ActionListener listener) {
+        // By default this does nothing.  Sub-classes can assign the listener to individual components.
+    }
+}
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
ConnectionPanel.java 1.18 -> 1.19
diff -u -r1.18 -r1.19
--- ConnectionPanel.java	30 Apr 2012 04:17:58 -0000	1.18
+++ ConnectionPanel.java	3 May 2012 16:59:28 -0000	1.19
@@ -1,7 +1,5 @@
 package org.lcsim.hps.monitoring;
 
-import java.awt.Color;
-import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
 import java.io.File;
@@ -14,18 +12,16 @@
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
-import javax.swing.JLabel;
 import javax.swing.JOptionPane;
-import javax.swing.JPanel;
 import javax.swing.JTextField;
 
 import org.jlab.coda.et.enums.Mode;
 
 /**
  * @author Jeremy McCormick <[log in to unmask]>
- * @version $Id: ConnectionPanel.java,v 1.18 2012/04/30 04:17:58 jeremy Exp $
+ * @version $Id: ConnectionPanel.java,v 1.19 2012/05/03 16:59:28 jeremy Exp $
  */
-class ConnectionPanel extends JPanel {
+class ConnectionPanel extends FieldsPanel {
 
     private JTextField etNameField;
     private JTextField hostField;
@@ -51,251 +47,25 @@
 
     ConnectionPanel() {
 
-        // Starting GUI values will be assigned from default connection parameters. 
-        connectionParameters = new ConnectionParameters();
-
-        setLayout(new GridBagLayout());        
+        super(new Insets(1, 1, 1, 1), true);
+       
+        setLayout(new GridBagLayout());
+
+        // Define fields.
+        etNameField = addField("ET Name", "", 20);
+        hostField = addField("Host", 20);
+        portField = addField("Port", 5);
+        blockingCheckBox = addCheckBox("Blocking", false, true);
+        verboseCheckBox = addCheckBox("Verbose", false, true);
+        statNameField = addField("Station Name", 10);
+        chunkField = addField("Chunk Size", 3);
+        qSizeField = addField("Queue Size", 3);
+        positionField = addField("Station Position", 3);
+        ppositionField = addField("Station Parallel Position", 3);
+        waitComboBox = addComboBox("Wait Mode", waitModes);
+        waitTimeField = addField("Wait Time [microseconds]", 8);
+        prescaleField = addField("Prescale", 8);
         
-        Insets insets = new Insets(1, 1, 1, 1);
-
-        //
-        // Define the fields.
-        //
-
-        GridBagConstraints c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 0;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel etNameLabel = new JLabel("ET Name:");
-        etNameLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(etNameLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 0;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        etNameField = new JTextField("", 20);
-        etNameField.setHorizontalAlignment(JTextField.RIGHT);
-        add(etNameField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 1;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel hostLabel = new JLabel("Host:");
-        hostLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(hostLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 1;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        hostField = new JTextField(20);
-        hostField.setHorizontalAlignment(JTextField.RIGHT);
-        add(hostField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 2;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel portLabel = new JLabel("Port:");
-        portLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(portLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 2;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        portField = new JTextField(5);
-        portField.setHorizontalAlignment(JTextField.RIGHT);
-        add(portField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 3;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel blockingLabel = new JLabel("Blocking:");
-        blockingLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(blockingLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 3;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        blockingCheckBox = new JCheckBox();
-        add(blockingCheckBox, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 4;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel verboseLabel = new JLabel("Verbose:");
-        verboseLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(verboseLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 4;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        verboseCheckBox = new JCheckBox();
-        add(verboseCheckBox, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 5;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel statNameLabel = new JLabel("Station Name:");
-        statNameLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(statNameLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 5;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        statNameField = new JTextField(10);
-        statNameField.setHorizontalAlignment(JTextField.RIGHT);
-        add(statNameField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 6;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel chunkLabel = new JLabel("Chunk Size:");
-        chunkLabel.setToolTipText("Number of events returned in array.");
-        chunkLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(chunkLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 6;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        chunkField = new JTextField(3);
-        chunkField.setHorizontalAlignment(JTextField.RIGHT);
-        add(chunkField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 7;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel qSizeLabel = new JLabel("Queue Size:");
-        qSizeLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(qSizeLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 7;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        qSizeField = new JTextField(3);
-        qSizeField.setHorizontalAlignment(JTextField.RIGHT);
-        add(qSizeField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 8;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel positionLabel = new JLabel("Station Position:");
-        positionLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(positionLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 8;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        positionField = new JTextField(3);
-        positionField.setHorizontalAlignment(JLabel.RIGHT);
-        add(positionField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 9;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel ppositionLabel = new JLabel("Station Parallel Position:");
-        ppositionLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(ppositionLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 9;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        ppositionField = new JTextField(3);
-        ppositionField.setHorizontalAlignment(JLabel.RIGHT);
-        add(ppositionField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 10;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel waitModeLabel = new JLabel("Wait Mode:");
-        waitModeLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(waitModeLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 10;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        waitComboBox = new JComboBox(waitModes);
-        add(waitComboBox, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 11;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel waitTimeLabel = new JLabel("Wait Time [microseconds]:");
-        waitTimeLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(waitTimeLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 11;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        waitTimeField = new JTextField(8);
-        waitTimeField.setBackground(Color.WHITE);
-        waitTimeField.setHorizontalAlignment(JLabel.RIGHT);
-        add(waitTimeField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 12;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel prescaleLabel = new JLabel("Prescale:");
-        prescaleLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(prescaleLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 12;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        prescaleField = new JTextField(8);
-        prescaleField.setBackground(Color.WHITE);
-        prescaleField.setHorizontalAlignment(JLabel.RIGHT);
-        add(prescaleField, c);
-
         // Set default connection parameters which are pushed to GUI.
         setConnectionParameters(new ConnectionParameters());
     }

hps-java/src/main/java/org/lcsim/hps/monitoring
ConnectionStatusPanel.java 1.5 -> 1.6
diff -u -r1.5 -r1.6
--- ConnectionStatusPanel.java	30 Apr 2012 13:36:55 -0000	1.5
+++ ConnectionStatusPanel.java	3 May 2012 16:59:28 -0000	1.6
@@ -8,18 +8,19 @@
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
 
 class ConnectionStatusPanel extends JPanel {
-	
-	JTextField statusField;
-		
-	ConnectionStatusPanel() {
-		
-	    setLayout(new GridBagLayout());
-		
-		Insets insets = new Insets(5,2,1,2);
-		
-		GridBagConstraints c = new GridBagConstraints();
+
+    JTextField statusField;
+
+    ConnectionStatusPanel() {
+
+        setLayout(new GridBagLayout());
+
+        Insets insets = new Insets(5,2,1,2);
+
+        GridBagConstraints c = new GridBagConstraints();
         c.gridx = 0;
         c.gridy = 0;
         c.insets = insets;
@@ -38,14 +39,18 @@
         statusField.setEditable(false);
         statusField.setBackground(Color.WHITE);
         add(statusField, c);
-        
+
         setStatus(ConnectionStatus.DISCONNECTED);
-	}
-	
-	void setStatus(int idx) {
-		if (idx < 0 || idx > (ConnectionStatus.NUMBER_STATUSES - 1)) {
-			throw new IllegalArgumentException("Invalid index value.  Must be between 0 and " + (ConnectionStatus.NUMBER_STATUSES - 1) + ".");
-		}
-		statusField.setText(ConnectionStatus.toString(idx));
-	}
+    }
+
+    void setStatus(final int status) {
+        if (status < 0 || status > (ConnectionStatus.NUMBER_STATUSES - 1)) {
+            throw new IllegalArgumentException("Invalid index value.  Must be between 0 and " + (ConnectionStatus.NUMBER_STATUSES - 1) + ".");
+        }
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                statusField.setText(ConnectionStatus.toString(status));
+            }
+        });
+    }
 }
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
DefaultEtEventProcessor.java 1.5 -> 1.6
diff -u -r1.5 -r1.6
--- DefaultEtEventProcessor.java	30 Apr 2012 20:04:39 -0000	1.5
+++ DefaultEtEventProcessor.java	3 May 2012 16:59:28 -0000	1.6
@@ -15,14 +15,19 @@
 import org.jlab.coda.jevio.EvioException;
 import org.jlab.coda.jevio.EvioReader;
 import org.lcsim.event.EventHeader;
+import org.lcsim.hps.evio.EventConstants;
 import org.lcsim.hps.evio.LCSimEventBuilder;
 import org.lcsim.job.JobControlManager;
 
 /**
  * This class executes the default event processing chain for HPS Test Run monitoring: ET -> EVIO -> LCIO.
- * Its method {@link #process()} should be executed on a separate thread which can be joined until it dies.
+ * Its method {@link #process()} should be executed on a separate thread which can be joined by the caller
+ * until it dies.  {@link EtEventListener} objects can be registered with this class to receive notifications 
+ * as processing occurs.  This is how the {@link MonitoringApplication} updates the GUI without the processor
+ * having a direct reference to the actual application.
+ * 
  * @author Jeremy McCormick <[log in to unmask]>
- * @version $Id: DefaultEtEventProcessor.java,v 1.5 2012/04/30 20:04:39 jeremy Exp $
+ * @version $Id: DefaultEtEventProcessor.java,v 1.6 2012/05/03 16:59:28 jeremy Exp $
  */
 public class DefaultEtEventProcessor implements EtEventProcessor
 {
@@ -34,8 +39,13 @@
     EtConnection et;
     List<EtEventListener> listeners = new ArrayList<EtEventListener>();
     int status;
-    boolean stopProcessing;
-    boolean stopOnError;
+    // FIXME: The volatile keyword might not be needed.  Debugging.
+    volatile boolean stopProcessing;
+    volatile boolean stopOnError;
+    volatile boolean nextEvents;
+    volatile boolean pauseMode = false;
+    volatile boolean done = false;
+    volatile boolean blocked = false; 
     static Logger logger;
 
     /**
@@ -71,7 +81,15 @@
         }   
         
         // Log an initialization message.
-        logger.log(Level.INFO, "Event processor initialized successfully.");
+        logger.log(Level.CONFIG, "Event processor initialized successfully.");
+    }
+    
+    /**
+     * Enable pause mode to hold after getEvents() call.
+     * @param p True to pause after each set of events; false to do real-time processing.
+     */
+    void setPauseMode(boolean p) {
+        this.pauseMode = p;
     }
     
     Logger getLogger() {
@@ -84,7 +102,7 @@
     
     public void setMaxEvents(int maxEvents) {
         this.maxEvents = maxEvents;
-        logger.log(Level.FINER, "Setting maxEvents to <" + maxEvents + ">.");
+        logger.log(Level.INFO, "Set maxEvents to <" + maxEvents + ">.");
     }
     
     void begin() {
@@ -113,6 +131,7 @@
     }
     
     void finish() {
+        logger.log(Level.FINER, "Calling finish() methods of listeners.");
         for (EtEventListener listener : listeners) {
             listener.finish();
         }
@@ -120,7 +139,7 @@
     
     public void stop() {
         this.stopProcessing = true;
-        logger.log(Level.FINER, "Received stop request.");
+        logger.log(Level.FINEST, "Received stop request.");
     }
        
     public int getNumberOfEventsProcessed() {
@@ -148,16 +167,51 @@
     // If in wait mode, continue after waitTime in microseconds if there are no events.
     // If in async mode, expects non-empty event list immediately or an error occurs.
     // If in sleep mode, the call to getEvents() will block, including requests to wake-up, until events arrive.
-    public void processEtEvents() throws EtTimeoutException, MaxEventsException, EventProcessingException, Exception {
+    public void processEtEvents() throws EtTimeoutException, MaxEventsException, Exception {
 
-        // Get events from the ET system.
+        // Get events from the ET system.  This can potentially block forever until it receives events.
+        blocked = true;
         EtEvent[] mevs = et.sys.getEvents(et.att, et.param.waitMode, Modify.NOTHING, et.param.waitTime, et.param.chunk);
+        blocked = false;
 
         // Loop over retrieved EtEvents.
         for (EtEvent mev : mevs) {
+            
+            // Got a request to stop processing.  Break out of loop.
             if (stopProcessing)
                 break;
-            processEtEvent(mev);
+            
+            // Process a single EtEvent using the default processing chain.
+            try {
+                processEtEvent(mev);
+            }
+            // Catch all processing errors including ET -> EVIO, EVIO -> LCIO, and LCSim Driver execution.
+            catch (EventProcessingException e) {               
+                e.getCause().printStackTrace(); // This message shows up in detailed job log.
+                logger.log(Level.WARNING, "Error processing event <" + this.eventsProcessed + ">.");
+                if (stopOnError) {
+                    this.status = ConnectionStatus.ERROR;
+                    logger.log(Level.INFO, "Exiting on first error.");
+                    break;
+                }
+            }
+            finally {
+                // If running in pause mode then hold here until user hits "Next Event" button.
+                if (pauseMode && this.status == ConnectionStatus.CONNECTED) {
+                    logger.log(Level.FINEST, "Pausing until next events requested.");
+                    while (true) {
+                        if (nextEvents) {
+                            logger.log(Level.FINEST, "User requested next set of events.");
+                            nextEvents = false;
+                            break;
+                        }
+                        else if (stopProcessing) {
+                            logger.log(Level.FINEST, "Stop was requested in inner event processing loop.");
+                            break;
+                        }
+                    }
+                }
+            }
         }
     }
 
@@ -180,12 +234,20 @@
             // Create EvioEvent from EtEvent.
             try {
                 evioEvent = createEvioEvent(mev);
+                
+                // Handlers for non-physics events.
+                if (isPreStartEvent(evioEvent)) {
+                    preStartEvent(evioEvent);
+                }
+                else if (isEndEvent(evioEvent)) {
+                    endRun(evioEvent);
+                }
+                
             }
             catch (Exception e) {
                 throw new EventProcessingException("Failed to create EVIO event.", e);
             }
 
-            // For now, non-physics events are simply skipped.
         } while (!eventBuilder.isPhysicsEvent(evioEvent));
 
         // Create the LCSim event.
@@ -202,7 +264,7 @@
             jobManager.processEvent(lcsimEvent);
         }
         catch (Exception e) {
-            throw new EventProcessingException("Error processing LCSim event.", e);
+            throw new EventProcessingException("Error processing the LCSim event.", e);
         }
 
         // Notify listeners of end of event.
@@ -225,27 +287,24 @@
      * Run the event processing from the ET connection.
      */
     public void process() {
-             
-        // Check if the ET connection is valid before starting.
-        if (!et.sys.alive()) {
-            logger.log(Level.SEVERE, "ET system is not alive.");
-            throw new RuntimeException("ET system is not alive.");
-        }
-        
-        // Set status to connected.
+                        
+        // Set current status to connected.
         this.status = ConnectionStatus.CONNECTED;
         
-        // Clear any leftover stop request.
+        // Clear any leftover stop request before starting, just in case.
         stopProcessing = false;
         
+        done = false;
+        
         // Notify listeners of job start.
         begin();
         
-        // Loop until Exception is thrown that stops event processing or stop is requested.
+        // Loop until fatal Exception or stop is requested.
         while (true) { 
 
             // Got a request to stop processing.
             if (stopProcessing) {
+                logger.log(Level.FINEST, "Outer process() loop got stop request.");
                 this.status = ConnectionStatus.DISCONNECT_REQUESTED;
                 break;
             }
@@ -254,34 +313,100 @@
             try {
                 processEtEvents();
             } 
-            // The session timed out.
+            // The ET system timed out in the getEvents() method.
             catch (EtTimeoutException e) {
                 logger.log(Level.WARNING, "ET connection timed out.");
                 this.status = ConnectionStatus.TIMED_OUT; 
             }
-            // Reached max number of events.
+            // Processing reached the maximum number of events.
             catch (MaxEventsException e) {
                 logger.log(Level.INFO, "Reached max events <" + this.maxEvents + ">.");
                 this.status = ConnectionStatus.DISCONNECTING; 
             }
-            // There was some error processing the EtEvent.
+            // Some generic error was thrown.  These are most likely ET system exceptions.
             catch (Exception e) {
-                e.printStackTrace(); // This should show up in detailed job log in terminal or output file.
-                // FIXME: This message seems to show up multiple times in the log table.
-                logger.log(Level.WARNING, "Error processing event <" + this.eventsProcessed + ">.");
-                if (stopOnError) {
-                    this.status = ConnectionStatus.ERROR;
-                    logger.log(Level.INFO, "Exiting on first error.");
-                }
+                e.printStackTrace();
+                this.status = ConnectionStatus.ERROR;
             }
+            // Break out of processing loop if not still in connected state.
             finally {
                 if (this.status != ConnectionStatus.CONNECTED) {
                     break;
                 }
             }
+            logger.getHandlers()[0].flush();
         }
         
+        logger.log(Level.FINEST, "End of processing loop.  About to cleanup.");
+        logger.getHandlers()[0].flush();
+        
         // Notify listeners of job end.
         finish();
+        
+        done = true;
+    }
+    
+    /**
+     * Check if this event is a Pre Start Event.
+     * @param event
+     * @return
+     */
+    private boolean isPreStartEvent(EvioEvent event) {
+        return event.getHeader().getTag() == EventConstants.PRESTART_EVENT_TAG;
+    }
+    
+    /**
+     * Check if this event is an End Event.
+     * @param event
+     * @return True if this event is an End Event; false if not.
+     */
+    private boolean isEndEvent(EvioEvent event) {
+        return event.getHeader().getTag() == EventConstants.END_EVENT_TAG;
+    }
+    
+    /**
+     * Notify listeners of Pre Start event being received by ET system.
+     * @param event The EvioEvent for the Pre Start.
+     */
+    private void preStartEvent(EvioEvent event) {
+        int[] data = event.getIntData();
+        int seconds = data[0];
+        int runNumber = data[1];
+        for (EtEventListener listener : this.listeners) {
+            listener.prestart(seconds, runNumber);
+        }
+    }
+    
+    /**
+     * Notify listeners of end of run being received by ET system.
+     * @param event The EvioEvent for the End Event record, which indicates end of run.
+     */
+    private void endRun(EvioEvent event) {
+        int[] data = event.getIntData();
+        int seconds = data[0];
+        int nevents = data[2];
+        for (EtEventListener listener : this.listeners) {
+            listener.endRun(seconds, nevents);
+        }
+    }
+      
+    public void pauseMode(boolean p) {
+        pauseMode = p;
+    }
+    
+    public void nextEvents() {
+        nextEvents = true;
+    }
+    
+    public void setLogLevel(Level level) {
+        logger.setLevel(level);
+    }
+    
+    public boolean blocked() {
+        return blocked;
+    }
+    
+    public boolean done() {
+        return done;
     }
 }
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
EtConnection.java 1.9 -> 1.10
diff -u -r1.9 -r1.10
--- EtConnection.java	29 Apr 2012 23:51:22 -0000	1.9
+++ EtConnection.java	3 May 2012 16:59:28 -0000	1.10
@@ -12,92 +12,92 @@
  * @author Jeremy McCormick <[log in to unmask]>
  */
 class EtConnection {
-	
+
     ConnectionParameters param;
-	EtSystem sys;
-	EtAttachment att;
-	EtStation stat;
-		
-	private EtConnection(ConnectionParameters param, EtSystem sys, EtAttachment att, EtStation stat) {
-	    this.param = param;
-		this.sys = sys;
-		this.att = att;
-		this.stat = stat;
-	}
-	
-	EtSystem getEtSystem() {
-		return sys;
-	}
-	
-	EtAttachment getEtAttachment() {
-		return att;
-	}
-	
-	EtStation getEtStation() {
-		return stat;
-	}
-	
-	ConnectionParameters getConnectionParameters() {
-	    return param;
-	}
-		
-	void cleanup() {
-	    boolean debug = true;
-	    try {
+    EtSystem sys;
+    EtAttachment att;
+    EtStation stat;
+
+    private EtConnection(ConnectionParameters param, EtSystem sys, EtAttachment att, EtStation stat) {
+        this.param = param;
+        this.sys = sys;
+        this.att = att;
+        this.stat = stat;
+    }
+
+    EtSystem getEtSystem() {
+        return sys;
+    }
+
+    EtAttachment getEtAttachment() {
+        return att;
+    }
+
+    EtStation getEtStation() {
+        return stat;
+    }
+
+    ConnectionParameters getConnectionParameters() {
+        return param;
+    }
+
+    void cleanup() {
+        boolean debug = false;
+        try {
             if (debug)
-                System.out.println("sys.detach ...");
+                System.out.println("ET cleanup - sys.detach ...");
             sys.detach(att);
             if (debug)
-                System.out.println("sys.removeStation ...");
+                System.out.println("ET cleanup - sys.removeStation ...");
             sys.removeStation(stat);
             if (debug)
-                System.out.println("sys.close ...");
+                System.out.println("ET cleanup - sys.close ...");
             sys.close();
             if (debug)
-                System.out.println("cleanup successful");
+                System.out.println("ET cleanup - successful");
         }
         catch (Exception e) {
             e.printStackTrace();
         }		
-	}
-	
-	static EtConnection createEtConnection(ConnectionParameters cn) {
-	    try {
-	        	        
-	        // make a direct connection to ET system's tcp server
-	        EtSystemOpenConfig config = new EtSystemOpenConfig(cn.etName, cn.host, cn.port);
-
-	        // create ET system object with verbose debugging output
-	        EtSystem sys = new EtSystem(config, EtConstants.debugInfo);
-	        sys.open();
-
-	        // configuration of a new station
-	        EtStationConfig statConfig = new EtStationConfig();
-	        statConfig.setFlowMode(cn.flowMode);
-	        if (!cn.blocking) {
-	            statConfig.setBlockMode(EtConstants.stationNonBlocking);
-	            if (cn.qSize > 0) {
-	                statConfig.setCue(cn.qSize);
-	            }
-	        }
-	        // Set prescale.
-	        if (cn.prescale > 0) {
-	            System.out.println("setting prescale to " + cn.prescale);
-	            statConfig.setPrescale(cn.prescale);
-	        }
-
-	        // Create the station.
-	        EtStation stat = sys.createStation(statConfig, cn.statName, cn.position, cn.pposition);
-
-	        // attach to new station
-	        EtAttachment att = sys.attach(stat);
-
-	        // Return new connection.
-	        return new EtConnection(cn, sys, att, stat);
-	        
-	    } catch (Exception e) {
-	        e.printStackTrace();
-	        return null;
-	    }
-	}       
+    }
+
+    static EtConnection createEtConnection(ConnectionParameters cn) {
+        try {
+
+            // make a direct connection to ET system's tcp server
+            EtSystemOpenConfig config = new EtSystemOpenConfig(cn.etName, cn.host, cn.port);
+
+            // create ET system object with verbose debugging output
+            EtSystem sys = new EtSystem(config, EtConstants.debugInfo);
+            sys.open();
+
+            // configuration of a new station
+            EtStationConfig statConfig = new EtStationConfig();
+            statConfig.setFlowMode(cn.flowMode);
+            if (!cn.blocking) {
+                statConfig.setBlockMode(EtConstants.stationNonBlocking);
+                if (cn.qSize > 0) {
+                    statConfig.setCue(cn.qSize);
+                }
+            }
+            // Set prescale.
+            if (cn.prescale > 0) {
+                System.out.println("setting prescale to " + cn.prescale);
+                statConfig.setPrescale(cn.prescale);
+            }
+
+            // Create the station.
+            EtStation stat = sys.createStation(statConfig, cn.statName, cn.position, cn.pposition);
+
+            // attach to new station
+            EtAttachment att = sys.attach(stat);
+
+            // Return new connection.
+            return new EtConnection(cn, sys, att, stat);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }       
 }
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
EtEventProcessor.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- EtEventProcessor.java	29 Apr 2012 18:11:26 -0000	1.1
+++ EtEventProcessor.java	3 May 2012 16:59:28 -0000	1.2
@@ -1,5 +1,7 @@
 package org.lcsim.hps.monitoring;
 
+import java.util.logging.Level;
+
 import org.jlab.coda.et.EtEvent;
 import org.jlab.coda.et.exception.EtTimeoutException;
 
@@ -35,29 +37,23 @@
     
     // Set maximum number of events to process.
     void setMaxEvents(int maxEvents);
-   
-    // Interface for notification of start, end, and error during event processing.
-    // FIXME: Should the callback methods get an EtEvent or event number?
-    interface EtEventListener {
-        
-        // Called at beginning of event processing job.
-        void begin();
-        
-        // Called at start of processing one event.
-        void startOfEvent();
-        
-        // Called at end of processing one event.
-        void endOfEvent();
-        
-        // Called when an error occurs during single event processing.
-        void errorOnEvent();
-        
-        // Called at end of event processing job.
-        void finish();
-    }
     
+    // Change to pause mode which allows pausing between events.
+    void pauseMode(boolean p);
+    
+    // Continue with next events if in pause mode.
+    void nextEvents();
+    
+    // Set the log level.
+    void setLogLevel(Level level);
+    
+    boolean done();
+    
+    boolean blocked();
+           
     // Exception that is thrown when an error occurs during event processing.
     static class EventProcessingException extends Exception {
+
         EventProcessingException(Exception e) {
             super(e);
         }

hps-java/src/main/java/org/lcsim/hps/monitoring
EventPanel.java 1.13 -> 1.14
diff -u -r1.13 -r1.14
--- EventPanel.java	30 Apr 2012 04:17:58 -0000	1.13
+++ EventPanel.java	3 May 2012 16:59:28 -0000	1.14
@@ -1,17 +1,15 @@
 package org.lcsim.hps.monitoring;
 
-import java.awt.Color;
-import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
 import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 
-import javax.swing.JLabel;
-import javax.swing.JPanel;
 import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
 
-class EventPanel extends JPanel { 
+class EventPanel extends FieldsPanel { 
 
     private JTextField eventCounterField; // number of events in this job
     private JTextField elapsedTimeField; // elapsed time between job start
@@ -21,7 +19,11 @@
     private JTextField sessionSuppliedField; // number of events supplied in this session; ignored by reset command
     private JTextField totalSuppliedField; // number of events supplied since the application started
     private JTextField maxEventsField; // maximum number of events to process before disconenct
-    private DecimalFormat rateFormat = new DecimalFormat("#.##");
+    private JTextField runNumberField; // Run number from CODA Pre Start event.
+    private JTextField runStartField; // Start of run date from CODA Pre Start event.
+    private JTextField runEventsField; // Number of events in run.
+    private JTextField runStopField; // End of run date from CODA End Event.
+    
     private static final int defaultEventRefresh = 1;
     private int eventRefresh = defaultEventRefresh;
     private int eventCount;
@@ -30,190 +32,27 @@
     private int totalSupplied;
     private boolean updateEvent = true;
     
+    private final static DecimalFormat rateFormat = new DecimalFormat("#.##");
+    final static SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM-dd-yyyy HH:mm:ss");
+ 
     EventPanel() {
-
-        setLayout(new GridBagLayout());
-        
-        Insets insets = new Insets(1, 1, 1, 1);
         
-        GridBagConstraints c = new GridBagConstraints();
-
-        // Panel for labels and values.
-        c.gridx = 0;
-        c.gridy = 0;
-        c.insets = insets;
-        JPanel fieldsPanel = new JPanel();
-        fieldsPanel.setLayout(new GridBagLayout());
-        add(fieldsPanel, c);
-
-        // Panel for control buttons.
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 1;        
-        c.insets = insets;
-        JPanel buttonsPanel = new JPanel();
-        buttonsPanel.setLayout(new GridBagLayout());
-        add(buttonsPanel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 0;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel eventLabel = new JLabel("Events Processed:");
-        eventLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(eventLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 0;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        eventCounterField = new JTextField("0", 10);
-        eventCounterField.setHorizontalAlignment(JTextField.RIGHT);
-        eventCounterField.setEditable(false);
-        eventCounterField.setBackground(Color.WHITE);
-        fieldsPanel.add(eventCounterField, c);
+        super(new Insets(0, 0, 0, 0), false);
 
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 1;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel timeLabel = new JLabel("Elapsed Time [seconds]:");
-        timeLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(timeLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 1;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        elapsedTimeField = new JTextField("0", 10);
-        elapsedTimeField.setHorizontalAlignment(JTextField.RIGHT);
-        elapsedTimeField.setEditable(false);
-        elapsedTimeField.setBackground(Color.WHITE);
-        fieldsPanel.add(elapsedTimeField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 2;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel avgRateLabel = new JLabel("Average Event Rate [Hz]:");
-        avgRateLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(avgRateLabel, c);
+        setLayout(new GridBagLayout());
 
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 2;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        avgEventRateField = new JTextField("0", 6);
-        avgEventRateField.setHorizontalAlignment(JTextField.RIGHT);
-        avgEventRateField.setEditable(false);        
-        avgEventRateField.setBackground(Color.WHITE);
-        fieldsPanel.add(avgEventRateField, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 3;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel refreshLabel = new JLabel("Event Refresh:");
-        refreshLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(refreshLabel, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 3;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        refreshField = new JTextField(Integer.toString(eventRefresh), 8);
-        refreshField.setHorizontalAlignment(JTextField.RIGHT);
-        refreshField.setEditable(false);
-        refreshField.setBackground(Color.WHITE);
-        fieldsPanel.add(refreshField, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 4;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel badEventsLabel = new JLabel("Bad Events:");
-        badEventsLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(badEventsLabel, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 4;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        badEventsField = new JTextField("0", 8);
-        badEventsField.setHorizontalAlignment(JTextField.RIGHT);
-        badEventsField.setEditable(false);
-        badEventsField.setBackground(Color.WHITE);
-        fieldsPanel.add(badEventsField, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 5;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel sessionSuppliedLabel = new JLabel("Session Supplied Events:");
-        sessionSuppliedLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(sessionSuppliedLabel, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 5;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        sessionSuppliedField = new JTextField("0", 8);
-        sessionSuppliedField.setHorizontalAlignment(JTextField.RIGHT);
-        sessionSuppliedField.setEditable(false);
-        sessionSuppliedField.setBackground(Color.WHITE);
-        fieldsPanel.add(sessionSuppliedField, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 6;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel totalSuppliedLabel = new JLabel("Total Supplied Events:");
-        totalSuppliedLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(totalSuppliedLabel, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 6;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        totalSuppliedField = new JTextField("0", 8);
-        totalSuppliedField.setHorizontalAlignment(JTextField.RIGHT);
-        totalSuppliedField.setEditable(false);
-        totalSuppliedField.setBackground(Color.WHITE);
-        fieldsPanel.add(totalSuppliedField, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 7;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel maxEventsLabel = new JLabel("Max Events:");
-        maxEventsLabel.setHorizontalAlignment(JLabel.LEFT);
-        fieldsPanel.add(maxEventsLabel, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 7;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        maxEventsField = new JTextField("-1", 8);
-        maxEventsField.setHorizontalAlignment(JTextField.RIGHT);
-        maxEventsField.setEditable(false);
-        maxEventsField.setBackground(Color.WHITE);
-        fieldsPanel.add(maxEventsField, c);
+        elapsedTimeField = addField("Elapsed Time [seconds]", "0", 10);
+        eventCounterField = addField("Events Processed", "0", 10);
+        avgEventRateField = addField("Average Events Per Second", "0", 6);
+        badEventsField = addField("Event Errors", "0", 8); 
+        runNumberField = addField("Run Number", "", 8);
+        runStartField = addField("Run Started", "", 22);
+        runStopField = addField("Run Stopped", "", 22);
+        runEventsField = addField("Events in Run", "", 10);
+        sessionSuppliedField = addField("Session Supplied Events", "0", 8);
+        totalSuppliedField = addField("Total Supplied Events", "0", 8);
+        refreshField = addField("Event Refresh", Integer.toString(eventRefresh), 8);
+        maxEventsField = addField("Max Events", "-1", 8);
     }
             
     int getEventRefresh() {
@@ -232,6 +71,40 @@
         });
     }
     
+    void setRunNumber(final int runNumber) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                runNumberField.setText(Integer.toString(runNumber));
+            }
+        });
+    }
+    
+    void setRunEventCount(final int eventCount) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                runEventsField.setText(Integer.toString(eventCount));
+            }
+        });
+    }
+    
+    void setRunStartTime(final long millis) {
+        final Date d = new Date(millis);
+        SwingUtilities.invokeLater(new Runnable() {
+           public void run() {
+               runStartField.setText(dateFormat.format(d));
+           }
+        });
+    }
+    
+    void setRunEndTime(final long millis) {
+        final Date d = new Date(millis);
+        SwingUtilities.invokeLater(new Runnable() {
+           public void run() {
+               runStopField.setText(dateFormat.format(d));
+           }
+        });
+    }
+    
     private void checkUpdateEvent() {
         updateEvent = ((eventCount % getEventRefresh()) == 0);
     }
@@ -275,9 +148,9 @@
     
     void updateAverageEventRate(long jobStartTime) {
         if (updateEvent) {
-            double jobTime = System.currentTimeMillis() - jobStartTime;
+            final double jobTime = System.currentTimeMillis() - jobStartTime;
             if (jobTime > 0) {
-                double jobSeconds = jobTime / 1000;
+                final double jobSeconds = jobTime / 1000;
                 final double eventsPerSecond = eventCount / jobSeconds;
                 SwingUtilities.invokeLater(new Runnable() {
                     public void run() {
@@ -305,54 +178,53 @@
         });                
     }
         
-	synchronized void reset() {
+    synchronized void reset() {
         resetEventCount();
         resetBadEventCount();
         resetAverageEventRate();
         resetElapsedTime();
     }
 	    
-	private void resetEventCount() {
-		eventCount = 0;
-		SwingUtilities.invokeLater(new Runnable() {
+    private void resetEventCount() {
+        eventCount = 0;
+        SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                 eventCounterField.setText("0");
             }
         });  		
-	}
+    }
 
-	private void resetElapsedTime() {
-	    SwingUtilities.invokeLater(new Runnable() {
-	        public void run() {
-	            elapsedTimeField.setText("0");
-	        }
-	    });
-	}   
+    private void resetElapsedTime() {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                elapsedTimeField.setText("0");
+            }
+        });
+    }   
 
-	private void resetAverageEventRate() {
-	    SwingUtilities.invokeLater(new Runnable() {
+    private void resetAverageEventRate() {
+        SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                 avgEventRateField.setText("0");
             }
         });	
-	}
+    }
+
+    private void resetBadEventCount() {
+        this.badEventCount = 0;
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                badEventsField.setText("0");
+            }
+        });
+    }
 
-	private void resetBadEventCount() {
-		this.badEventCount = 0;
-		SwingUtilities.invokeLater(new Runnable() {
-		    public void run() {
-		        badEventsField.setText("0");
-		    }
-		});
-	}
-	
-	void resetSessionSupplied() {
-	    this.sessionSupplied = 0;
-	    SwingUtilities.invokeLater(new Runnable() {
+    void resetSessionSupplied() {
+        this.sessionSupplied = 0;
+        SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                 sessionSuppliedField.setText("0");
             }
         });
-	    
-	}	
+    }	
 }
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
JobPanel.java 1.10 -> 1.11
diff -u -r1.10 -r1.11
--- JobPanel.java	30 Apr 2012 22:27:36 -0000	1.10
+++ JobPanel.java	3 May 2012 16:59:28 -0000	1.11
@@ -1,22 +1,19 @@
 package org.lcsim.hps.monitoring;
 
-import java.awt.Color;
-import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
-import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.io.File;
 import java.io.InputStream;
+import java.util.logging.Level;
 
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
-import javax.swing.JLabel;
 import javax.swing.JOptionPane;
-import javax.swing.JPanel;
 import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
 
-class JobPanel extends JPanel implements ActionListener {
+class JobPanel extends FieldsPanel {
 
     private JComboBox steeringComboBox;
     private JTextField steeringField;
@@ -28,228 +25,49 @@
     JTextField logFileField;
     JCheckBox remoteAidaCheckBox;
     JTextField aidaNameField;
+    JCheckBox pauseModeCheckBox;
+    JComboBox logLevelComboBox;
 
     private String defaultEventBuilderClassName = "";
 
-    private final static String[] steeringTypes = { "RESOURCE", "FILE", "NONE" };	
+    private final static String[] steeringTypes = {"RESOURCE", "FILE", "NONE"};	
     final static int RESOURCE = 0;
     final static int FILE = 1;
     final static int NONE = 2;
+    
+    String[] logLevels = new String[] {
+        Level.ALL.toString(),
+        Level.FINEST.toString(),
+        Level.FINER.toString(),
+        Level.FINE.toString(),
+        Level.CONFIG.toString(),
+        Level.INFO.toString(),
+        Level.WARNING.toString(),
+        Level.SEVERE.toString(),
+        Level.OFF.toString()};
 
     JobPanel() {
 
-        setLayout(new GridBagLayout());
-
-        Insets insets = new Insets(1, 1, 1, 1);
+        super(new Insets(1, 1, 1, 1), true);
 
-        GridBagConstraints c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 0;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel lcsimSteeringTypeLabel = new JLabel("Steering Type:");
-        lcsimSteeringTypeLabel.setHorizontalAlignment(JLabel.LEFT);
-        lcsimSteeringTypeLabel.setToolTipText("Type of LCSim steering file, either resource or local file.");
-        add(lcsimSteeringTypeLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 0;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        steeringComboBox  = new JComboBox(steeringTypes);
-        steeringComboBox.setSelectedIndex(NONE);
-        steeringComboBox.setBackground(Color.WHITE);
-        add(steeringComboBox, c);
+        setLayout(new GridBagLayout());
 
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 1;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel lcsimSteeringLabel = new JLabel("Steering File:");
-        lcsimSteeringLabel.setHorizontalAlignment(JLabel.LEFT);
-        lcsimSteeringLabel.setToolTipText("Resource in jar pointing to LCSim steering file.");
-        add(lcsimSteeringLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 1;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        steeringField = new JTextField("", 30);
-        steeringField.setHorizontalAlignment(JTextField.RIGHT);
-        steeringField.setBackground(Color.WHITE);
+        pauseModeCheckBox = addCheckBox("Pause mode", false, true);
+        disconnectOnErrorCheckBox = addCheckBox("Disconnect on error", true, true);
+        disconnectWarningCheckBox = addCheckBox("Warn before disconnect", false, true);
+        logLevelComboBox = addComboBox("Log Level", this.logLevels);
+        logLevelComboBox.setActionCommand(MonitoringCommands.logLevelCmd);
+        steeringComboBox = addComboBox("Steering Type", steeringTypes);  
+        steeringField = addField("Steering File", 30);
         steeringField.setActionCommand(MonitoringCommands.steeringCmd);
-        steeringField.addActionListener(this);
-        add(steeringField, c);		
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 2;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel detectorLabel = new JLabel("Detector Name:");
-        detectorLabel.setHorizontalAlignment(JLabel.LEFT);
-        detectorLabel.setToolTipText("Name of detector in LCSim conditions system.");
-        add(detectorLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 2;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        detectorNameField = new JTextField("", 20);
-        detectorNameField.setHorizontalAlignment(JTextField.RIGHT);
-        detectorNameField.setBackground(Color.WHITE);
-        add(detectorNameField, c);   
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 3;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel eventBuilderLabel = new JLabel("Event Builder Class:");
-        eventBuilderLabel.setHorizontalAlignment(JLabel.LEFT);
-        eventBuilderLabel.setToolTipText("Class used to convert to LCSim events from EVIO.");
-        add(eventBuilderLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 3;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        eventBuilderField = new JTextField("", 30);
-        eventBuilderField.setHorizontalAlignment(JTextField.RIGHT);
-        eventBuilderField.setBackground(Color.WHITE);
-        eventBuilderField.addActionListener(this);
+        detectorNameField = addField("Detector Name", 20);
+        eventBuilderField = addField("Event Builder Class", 30);
         eventBuilderField.setActionCommand(MonitoringCommands.eventBuilderCmd);
-        add(eventBuilderField, c);
+        logCheckBox = addCheckBox("Log to File", false, false);
+        logFileField = addField("Log File", "", "Full path to log file.", 30, false);
+        remoteAidaCheckBox = addCheckBox("Enabled remote AIDA", false, true);
+        aidaNameField = addField("Remote AIDA name", "", 15, true);
 
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 4;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel jobComboBoxLabel = new JLabel("Log to File:");
-        jobComboBoxLabel.setHorizontalAlignment(JLabel.LEFT);
-        add(jobComboBoxLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 4;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        logCheckBox = new JCheckBox();
-        logCheckBox.setEnabled(false);
-        add(logCheckBox, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 5;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel logFileBoxLabel = new JLabel("Log File:");
-        logFileBoxLabel.setHorizontalAlignment(JLabel.LEFT);
-        logFileBoxLabel.setToolTipText("Full path of log file.");        
-        add(logFileBoxLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 5;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        logFileField = new JTextField("", 30);
-        logFileField.setHorizontalAlignment(JLabel.RIGHT);
-        logFileField.setEditable(false);
-        logFileField.setBackground(Color.WHITE);
-        add(logFileField, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 6;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel disconnectWarningLabel = new JLabel("Warn before disconnect:");
-        disconnectWarningLabel.setHorizontalAlignment(JLabel.LEFT);
-        disconnectWarningLabel.setToolTipText("Whether or not to warn with a dialog box before disconnect.");
-        add(disconnectWarningLabel, c);        
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 6;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;             
-        disconnectWarningCheckBox = new JCheckBox();
-        disconnectWarningCheckBox.setSelected(false);
-        add(disconnectWarningCheckBox, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 7;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel disconnectOnErrorLabel = new JLabel("Disconnect on error:");
-        disconnectOnErrorLabel.setHorizontalAlignment(JLabel.LEFT);
-        disconnectOnErrorLabel.setToolTipText("Whether or not to disconnect if there is an error in event processing.");
-        add(disconnectOnErrorLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 7;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;             
-        disconnectOnErrorCheckBox = new JCheckBox();
-        disconnectOnErrorCheckBox.setSelected(true);
-        add(disconnectOnErrorCheckBox, c);        
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 8;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel remoteAidaLabel = new JLabel("Enable remote AIDA:");
-        remoteAidaLabel.setHorizontalAlignment(JLabel.LEFT);
-        remoteAidaLabel.setToolTipText("Enable remote AIDA server for viewing plots in realtime using JAS.");
-        add(remoteAidaLabel, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 8;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;     
-        remoteAidaCheckBox = new JCheckBox();
-        remoteAidaCheckBox.setSelected(false);
-        add(remoteAidaCheckBox, c);
-        
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 9;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.WEST;
-        JLabel aidaNameLabel = new JLabel("Remote AIDA name:");
-        aidaNameLabel.setHorizontalAlignment(JLabel.LEFT);
-        aidaNameLabel.setToolTipText("AIDA session name for connecting in JAS.");
-        add(aidaNameLabel, c);
-
-        c = new GridBagConstraints();
-        c.gridx = 1;
-        c.gridy = 9;
-        c.insets = insets;
-        c.anchor = GridBagConstraints.EAST;
-        aidaNameField = new JTextField("", 15);
-        aidaNameField.setHorizontalAlignment(JLabel.RIGHT);
-        aidaNameField.setText("hps");
-        add(aidaNameField, c);
-    }
-
-    public void actionPerformed(ActionEvent e) {
-        if (MonitoringCommands.steeringCmd == e.getActionCommand()) {
-            editSteering();
-        } 
-        else if (MonitoringCommands.eventBuilderCmd == e.getActionCommand()) {
-            editEventBuilder();
-        }
     }
     
     boolean isAIDAServerEnabled() {
@@ -260,7 +78,7 @@
         return aidaNameField.getText();
     }
 
-    private void editSteering() {
+    void editSteering() {
         String steering = steeringField.getText();
         int steeringType = steeringComboBox.getSelectedIndex();		
         if (RESOURCE == steeringType) {
@@ -268,7 +86,7 @@
             InputStream is = getClass().getResourceAsStream(steering);
             if (is == null) {
                 JOptionPane.showMessageDialog(this, "The LCSim steering resource does not exist.");
-                disableSteering();
+                //disableSteering();
             }
         } 
         else if (FILE == steeringType) {
@@ -276,7 +94,7 @@
             File f = new File(steering);
             if (!f.exists()) {
                 JOptionPane.showMessageDialog(this, "The LCSim steering file does not exist.");
-                disableSteering();
+                //disableSteering();
             }
         } 
         else if (NONE == steeringType) {
@@ -286,12 +104,14 @@
         }
     }
 
+    /*
     private void disableSteering() {
         JOptionPane.showMessageDialog(this, "No valid steering file was selected, so job will run without LCSim.");
         steeringComboBox.setSelectedIndex(NONE);
     }
+    */
 
-    private void editEventBuilder() {
+    void editEventBuilder() {
         String eventBuilderClassName = eventBuilderField.getText();
         try {
             // Test that the event builder class can be created successfully.
@@ -313,7 +133,11 @@
     }
 
     private void resetEventBuilder() {
-        eventBuilderField.setText(defaultEventBuilderClassName);
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                eventBuilderField.setText(defaultEventBuilderClassName);
+            }
+        });
     }
 
     String getEventBuilderClassName() {
@@ -324,20 +148,30 @@
         }
     }
 
-    void setSteeringFile(String steeringFile) {
-        steeringField.setText(steeringFile);
-        steeringComboBox.setSelectedIndex(FILE);
-        this.actionPerformed(new ActionEvent(this, 0, MonitoringCommands.steeringCmd));
+    void setSteeringFile(final String steeringFile) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                steeringField.setText(steeringFile);
+                steeringComboBox.setSelectedIndex(FILE);
+            }
+        });
     }
 
-    void setSteeringResource(String steeringResource) {
-        steeringField.setText(steeringResource);
-        steeringComboBox.setSelectedIndex(RESOURCE);
-        this.actionPerformed(new ActionEvent(this, 0, MonitoringCommands.steeringCmd));
+    void setSteeringResource(final String steeringResource) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                steeringField.setText(steeringResource);
+                steeringComboBox.setSelectedIndex(RESOURCE);
+            }
+        });
     }
 
-    void setDetectorName(String detectorName) {
-        detectorNameField.setText(detectorName);
+    void setDetectorName(final String detectorName) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                detectorNameField.setText(detectorName);
+            }
+        });
     }
 
     String getSteering() {
@@ -359,9 +193,13 @@
         return !("".equals(steeringField.getText().trim())) && (this.steeringComboBox.getSelectedIndex() != NONE);
     }
 
-    void setDefaultEventBuilder(String defaultEventBuilderClassName) {
+    void setDefaultEventBuilder(final String defaultEventBuilderClassName) {
         this.defaultEventBuilderClassName = defaultEventBuilderClassName;
-        eventBuilderField.setText(this.defaultEventBuilderClassName);
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                eventBuilderField.setText(defaultEventBuilderClassName);
+            }
+        });
     }
 
     void enableJobPanel(boolean enable) {
@@ -371,5 +209,25 @@
         eventBuilderField.setEnabled(enable);
         remoteAidaCheckBox.setEnabled(enable);
         aidaNameField.setEnabled(enable);
+        pauseModeCheckBox.setEnabled(enable);
     }	
+    
+    void addActionListener(ActionListener listener) {
+        steeringField.addActionListener(listener);
+        eventBuilderField.addActionListener(listener);
+        //pauseModeCheckBox.addActionListener(listener);
+        logLevelComboBox.addActionListener(listener);
+    }
+
+    boolean pauseMode() {
+        return this.pauseModeCheckBox.isSelected();
+    }
+    
+    void setPauseMode(boolean p) {
+        this.pauseModeCheckBox.setSelected(p);
+    }
+    
+    Level getLogLevel() {
+        return Level.parse((String)this.logLevelComboBox.getSelectedItem());
+    }
 }
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring
MonitoringApplication.java 1.35 -> 1.36
diff -u -r1.35 -r1.36
--- MonitoringApplication.java	30 Apr 2012 22:48:05 -0000	1.35
+++ MonitoringApplication.java	3 May 2012 16:59:28 -0000	1.36
@@ -3,19 +3,25 @@
 import static org.lcsim.hps.monitoring.MonitoringCommands.clearLogCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.connectCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.disconnectCmd;
+import static org.lcsim.hps.monitoring.MonitoringCommands.eventBuilderCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.eventRefreshCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.exitCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.loadConnectionCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.logCmd;
+import static org.lcsim.hps.monitoring.MonitoringCommands.logLevelCmd;
+import static org.lcsim.hps.monitoring.MonitoringCommands.nextCmd;
+import static org.lcsim.hps.monitoring.MonitoringCommands.pauseCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.resetConnectionSettingsCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.resetDriversCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.resetEventsCmd;
+import static org.lcsim.hps.monitoring.MonitoringCommands.resumeCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.saveConnectionCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.saveLogCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.savePlotsCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.screenshotCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.setMaxEventsCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.setSteeringFileCmd;
+import static org.lcsim.hps.monitoring.MonitoringCommands.steeringCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.terminalCmd;
 import static org.lcsim.hps.monitoring.MonitoringCommands.updateTimeCmd;
 
@@ -73,7 +79,7 @@
  * calling its main() method.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
- * @version $Id: MonitoringApplication.java,v 1.35 2012/04/30 22:48:05 jeremy Exp $
+ * @version $Id: MonitoringApplication.java,v 1.36 2012/05/03 16:59:28 jeremy Exp $
  */
 public class MonitoringApplication {
 
@@ -85,7 +91,8 @@
     private EventPanel eventPanel;
     private JobPanel jobPanel;			
     private JMenuBar menuBar;	
-    
+    private EventButtonsPanel buttonsPanel;
+
     // References to menu items that will be enabled/disabled depending on application state.
     private JMenuItem connectItem;
     private JMenuItem disconnectItem;
@@ -111,13 +118,14 @@
     private JobControlManager jobManager;
     private LCSimEventBuilder eventBuilder;
     private EtEventProcessor eventProcessor;
+    private Thread eventProcessingThread;
 
     // Job timing.
     private Timer timer;
     private long jobStartTime;
 
     // ActionListener for GUI event dispatching.
-    private MonitoringApplicationActionListener actionListener;
+    private ActionListener actionListener;
 
     // Logging objects.
     private static Logger logger;
@@ -127,14 +135,21 @@
 
     // Some default GUI size parameters.
     private final int maxWidth = 800;
-    private final int logHeight = 320;
+    private final int logHeight = 300;
 
     // Format for screenshots.  Hard-coded to PNG.
     private static final String screenshotFormat = "png";
 
     // The AIDA remote server.
     private AIDAServer server;
-    
+
+    // Listener for processing EtEvents.
+    private EtEventListener etListener = new MonitoringApplicationEtListener();
+
+    // Maximum time to wait for ET system to disconnect before zombifying the station, in milliseconds.
+    // TODO: Make this an option in JobPanel.
+    private int maxCleanupTime = 5000;
+
     /**
      * Constructor for monitoring application.  Users should not
      * need to use this; call main() method instead.
@@ -142,7 +157,7 @@
     private MonitoringApplication() {
 
         // Create the ActionEventListener for event dispatching.
-        actionListener = new MonitoringApplicationActionListener(this);
+        actionListener = new MonitoringApplicationActionListener();
 
         // Setup the application menus.
         createMenu();
@@ -168,7 +183,7 @@
         // Main panel for the application.
         mainPanel = new JPanel();
         mainPanel.setLayout(new GridBagLayout());
-        
+
         // Connection status panel.
         GridBagConstraints c = new GridBagConstraints();
         c.gridx = 0;
@@ -179,18 +194,28 @@
         connectionStatusPanel = new ConnectionStatusPanel();
         mainPanel.add(connectionStatusPanel, c);
 
+        // Event processing buttons.
+        c = new GridBagConstraints();
+        c.gridx = 0;
+        c.gridy = 1;
+        c.anchor = GridBagConstraints.CENTER;
+        buttonsPanel = new EventButtonsPanel();
+        buttonsPanel.addActionListener(actionListener);
+        mainPanel.add(buttonsPanel, c);
+
         // Sub-panels.
         connectionPanel = new ConnectionPanel();
         eventPanel = new EventPanel();
         jobPanel = new JobPanel();
+        jobPanel.addActionListener(actionListener);
 
-        // Tabs.
+        // Tab panels.
         c = new GridBagConstraints();
         c.fill = GridBagConstraints.BOTH;
         c.weightx = c.weighty = 1.0;
         JPanel tabsPanel = new JPanel();
         tabs = new JTabbedPane();
-        tabs.addTab("Connection", connectionPanel);
+        tabs.addTab("Connection Settings", connectionPanel);
         tabs.addTab("Event Monitor", eventPanel);
         tabs.addTab("Job Settings", jobPanel);
         tabsPanel.add(tabs, c);
@@ -198,7 +223,7 @@
         // Add tabs to main panel.
         c = new GridBagConstraints();
         c.gridx = 0;
-        c.gridy = 1;        
+        c.gridy = 2;        
         c.fill = GridBagConstraints.BOTH;
         c.weightx = c.weighty = 1.0;
         mainPanel.add(tabsPanel, c);
@@ -340,7 +365,7 @@
         saveLogItem.addActionListener(actionListener);
         saveLogItem.setToolTipText("Save the log records to a tab delimited text file.");
         logMenu.add(saveLogItem);
-        
+
         JMenuItem clearLogItem = new JMenuItem("Clear log");
         clearLogItem.setMnemonic(KeyEvent.VK_C);
         clearLogItem.setActionCommand(clearLogCmd);
@@ -389,7 +414,7 @@
 
         GridBagConstraints c = new GridBagConstraints();
         c.gridx = 0;
-        c.gridy = 2;
+        c.gridy = 3;
         c.fill = GridBagConstraints.BOTH;
         c.weightx = c.weighty = 1.0;
         JScrollPane logPane = new JScrollPane(logTable);
@@ -402,8 +427,8 @@
      */
     private void setupLogger() {
         logger = Logger.getLogger(this.getClass().getSimpleName());
-        logger.setUseParentHandlers(false);
         logHandler = new MonitoringApplicationLogHandler();
+        logger.setUseParentHandlers(false);
         logger.addHandler(logHandler);
         logger.setLevel(Level.ALL);
     }
@@ -426,6 +451,7 @@
      * Run the monitoring application from the command line.
      * @param args The command line arguments.
      */
+    // TODO: Command line arguments should be: [etFile] [steeringFile] (according to Matthew Graham!)
     public static void main(String[] args)
     {   
         final String defaultDetectorName;
@@ -433,7 +459,7 @@
         final String defaultEventBuilder;
 
         if (args.length == 0) {
-            defaultDetectorName = "HPS-Test-JLAB-v4pt0";
+            defaultDetectorName = "HPS-Test-JLAB-v4pt0"; 
             defaultSteering = "/org/lcsim/hps/steering/TestRunMonitoring.lcsim";
             defaultEventBuilder = LCSimTestRunEventBuilder.class.getCanonicalName();
         }
@@ -458,23 +484,12 @@
     }
 
     /**
-     * Runs the application's session loop.
+     * Runs the application loop.
      */
     private void run() {
-        // GUI outer loop.  
-        while (true) {  
-            try {                
-                // Execute a monitoring session.
-                session();
-            }
-            // Top-level exception catcher.
-            catch (Exception e) {
-                e.printStackTrace(); // This should show up in terminal or log file.
-                logger.log(Level.SEVERE, e.getMessage());
-                disconnect(ConnectionStatus.ERROR);
-                showDialog(e.getMessage());
-            }
-        }      
+        // GUI loop.
+        while (true) {
+        }
     }
 
     /**
@@ -500,133 +515,205 @@
     /**
      * The ActionListener implementation for handling all GUI events.
      */
-    private static final class MonitoringApplicationActionListener implements ActionListener {
-
-        MonitoringApplication app;
-
-        MonitoringApplicationActionListener(MonitoringApplication app) {
-            this.app = app;
-        }
+    private final class MonitoringApplicationActionListener implements ActionListener {
 
         public void actionPerformed(ActionEvent e) {
             String cmd = e.getActionCommand();
+            if (cmd != MonitoringCommands.updateTimeCmd)
+                logger.log(Level.FINEST, "Action performed <" + cmd + ">.");
             if (connectCmd.equals(cmd)) {
-                app.requestConnection();
+                startSessionThread();
             } 
             else if (disconnectCmd.equals(cmd)) {
-                app.eventProcessor.stop();
-                app.disconnect(ConnectionStatus.DISCONNECTING);
+                startDisconnectThread();
             } 
             else if (eventRefreshCmd.equals(cmd)) {
-                app.setEventRefresh();
+                setEventRefresh();
             }
             else if (savePlotsCmd.equals(cmd)) {
-                app.savePlots();
+                savePlots();
             } 
             else if (resetDriversCmd.equals(cmd)) {
-                app.resetDrivers();
+                resetDrivers();
             } 
             else if (logCmd.equals(cmd)) {
-                app.logToFile();
+                logToFile();
             } 
             else if (terminalCmd.equals(cmd)) {
-                app.logToTerminal();
+                logToTerminal();
             }
             else if (screenshotCmd.equals(cmd)) {
-                app.screenshot();
+                screenshot();
             }
             else if (exitCmd.equals(cmd)) {
-                app.exit();
+                exit();
             }
             else if (updateTimeCmd.equals(cmd)) {
-                app.updateTime();
+                updateTime();
             }
             else if (resetEventsCmd.equals(cmd)) {
-                app.resetJob();
+                resetJob();
             } 
             else if (saveConnectionCmd.equals(cmd)) {
-                app.connectionPanel.save();
+                connectionPanel.save();
             } 
             else if (loadConnectionCmd.equals(cmd)) {
-                app.connectionPanel.load();
+                connectionPanel.load();
             } 
             else if (resetConnectionSettingsCmd.equals(cmd)) {
-                app.connectionPanel.reset();
+                connectionPanel.reset();
             }
             else if (setSteeringFileCmd.equals(cmd)) {
-                app.selectSteeringFile();
+                selectSteeringFile();
             } 
             else if (setMaxEventsCmd.equals(cmd)) {
-                app.setMaxEvents();
+                setMaxEvents();
             }
             else if (saveLogCmd.equals(cmd)) {
-                app.saveLogToFile();
+                saveLogToFile();
             }
             else if (clearLogCmd.equals(cmd)) {
-                app.clearLog();
+                clearLog();
             }
-        } 
-    }
+            else if (steeringCmd.equals(cmd)) {
+                jobPanel.editSteering();
+            } 
+            else if (eventBuilderCmd.equals(cmd)) {
+                jobPanel.editEventBuilder();
+            }
+            else if (pauseCmd.equals(cmd)) {
+                pause();
+            }
+            else if (nextCmd.equals(cmd)) {
+                next();
+            }
+            else if (resumeCmd.equals(cmd)) {
+                resume();
+            }
+            else if (logLevelCmd.equals(cmd)) {
+                setLogLevel();
+            }
+        }
 
+        private void startDisconnectThread() {
+            Runnable r = new Runnable() {
+                public void run() {
+                    disconnect();
+                }
+            };
+            Thread t = new Thread(r, "Disconnect Thread");
+            t.start();
+        }
+    }
+    
     /**
-     * The listener for hooking into the event processor.
+     * This is the primary entry point for starting a monitoring session.
+     * The session is started on a new thread so it doesn't block.
      */
-    private static final class MonitoringApplicationEtListener implements EtEventProcessor.EtEventListener {
-
-        MonitoringApplication app;
+    private void startSessionThread() {
+        if (getConnectionStatus() != ConnectionStatus.CONNECTED) {
+            Runnable r = new Runnable() {
+                public void run() {
+                    session();
+                }
+            };
+            Thread t = new Thread(r, "Session Thread");
+            t.start();
+        }
+        else {
+            logger.log(Level.SEVERE, "Ignoring connection request.  Already connected!");
+        }
+    }
 
-        MonitoringApplicationEtListener(MonitoringApplication app) {
-            this.app = app;
+    /**
+     * Set a new log level for the application and also forward to the event processor.
+     */
+    private void setLogLevel() {
+        Level newLevel = jobPanel.getLogLevel(); 
+        logger.setLevel(newLevel);
+        if (eventProcessor != null) {
+            eventProcessor.setLogLevel(newLevel);
         }
+    }
+
+    /**
+     * The listener for hooking into the event processor.
+     */
+    private final class MonitoringApplicationEtListener implements EtEventListener {
 
         public void begin() {
+
             // Reset event GUI.
-            app.eventPanel.reset();
+            eventPanel.reset();
 
             // This is only reset between different jobs.
-            app.eventPanel.resetSessionSupplied();
+            eventPanel.resetSessionSupplied();
 
             // Start the job timer.
-            app.startTimer();
+            startTimer();
         }
 
         public void startOfEvent() {
-            app.eventPanel.updateEventCount();
+            eventPanel.updateEventCount();
         }
 
         public void endOfEvent() {
-            app.eventPanel.updateAverageEventRate(app.jobStartTime);
+            eventPanel.updateAverageEventRate(jobStartTime);
         }
 
         public void errorOnEvent() {
-            app.eventPanel.updateBadEventCount();
+            eventPanel.updateBadEventCount();
         }
 
         public void finish() {
+            logger.log(Level.FINEST, "In finish() method to cleanup job.");
             try {
                 // Call cleanup methods of Drivers.
-                if (app.jobManager != null)
-                    app.jobManager.finish();
+                try {
+                    if (jobManager != null)
+                        jobManager.finish();
+                }
+                catch (Exception e) {
+                    e.printStackTrace();
+                    logger.log(Level.WARNING, "Error calling jobManager.finish() in job cleanup.");
+                }
 
                 // Stop the job timer.
-                app.timer.stop();
-                app.timer = null;
+                timer.stop();
+                timer = null;
 
                 // Push final event counts to GUI.
-                app.eventPanel.endJob();
-                
-                // Disconnect from remote AIDA session.
-                if (app.server != null) {
-                    logger.log(Level.FINER, "Closing remote AIDA server.");
-                    app.server.close();
-                    app.server = null;
-                    logger.log(Level.INFO, "Remote AIDA server was closed.");
+                eventPanel.endJob();
+
+                // Disconnect from remote AIDA session if active.
+                if (server != null) {
+                    logger.log(Level.CONFIG, "Closing remote AIDA server.");
+                    server.close();
+                    server = null;
+                    logger.log(Level.CONFIG, "Remote AIDA server was closed.");
                 }
             }
             catch (Exception e) {
                 e.printStackTrace();
+                logger.log(Level.WARNING, "Error in finish() method <" + e.getMessage() + ">.");
             }
         }
+
+        public void prestart(int seconds, int runNumber) {
+            final long millis = ((long)seconds) * 1000;
+            eventPanel.setRunNumber(runNumber);
+            eventPanel.setRunStartTime(millis);
+            logger.log(Level.INFO, "Set run number <" + runNumber + "> from Pre Start.");
+            logger.log(Level.INFO, "Pre Start time <" + EventPanel.dateFormat.format(new Date(millis)) + ">.");
+        }
+
+        public void endRun(int seconds, int events) {
+            final long millis = ((long)seconds) * 1000;
+            eventPanel.setRunEndTime(millis);
+            eventPanel.setRunEventCount(events);
+            logger.log(Level.INFO, "Set number of events in run to <" + events + ">.");
+            logger.log(Level.INFO, "End Event time <" + EventPanel.dateFormat.format(new Date(millis)) + ">.");
+        }
     }
 
     /**
@@ -640,21 +727,14 @@
     }
 
     /**
-     * Request a connection to the ET system.
-     */
-    private void requestConnection() {
-        setConnectionStatus(ConnectionStatus.CONNECTION_REQUESTED);
-        logger.log(Level.INFO, "Connection requested.");
-    }
-
-    /**
      * Set the connection status.
      * @param status The connection status.
      */
     private void setConnectionStatus(int status) {
         connectionStatus = status;
         connectionStatusPanel.setStatus(status);
-        logger.log(Level.INFO, "Connection status changed to <" + ConnectionStatus.toString(status) + ">.");
+        logger.log(Level.CONFIG, "Connection status changed to <" + ConnectionStatus.toString(status) + ">.");
+        logHandler.flush();
     }
 
     /**
@@ -808,11 +888,15 @@
                     System.setOut(ps);
                     System.setErr(ps);				
 
-                    jobPanel.logFileField.setText(logFile.getPath());
-                    jobPanel.logCheckBox.setSelected(true);
-                    terminalItem.setEnabled(true);
-                    logItem.setEnabled(false);
-                    
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            jobPanel.logFileField.setText(logFile.getPath());
+                            jobPanel.logCheckBox.setSelected(true);
+                            terminalItem.setEnabled(true);
+                            logItem.setEnabled(false);
+                        }
+                    });
+
                     logger.log(Level.INFO, "Redirected print output to file <" + logFile.getPath() + ">.");
                 } catch (IOException e) {
                     logger.log(Level.SEVERE, "Error redirecting print output to file <" + logFile.getPath() + ">.");
@@ -829,10 +913,14 @@
     private void logToTerminal() {
         System.setOut(sysOut);
         System.setErr(sysErr);		
-        jobPanel.logFileField.setText("");
-        jobPanel.logCheckBox.setSelected(false);
-        terminalItem.setEnabled(false);
-        logItem.setEnabled(true);
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                jobPanel.logFileField.setText("");
+                jobPanel.logCheckBox.setSelected(false);
+                terminalItem.setEnabled(false);
+                logItem.setEnabled(true);
+            }
+        });
         logger.log(Level.INFO, "Redirected print output to terminal.");
     }
 
@@ -900,6 +988,13 @@
 
         // Re-enable the JobPanel.
         jobPanel.enableJobPanel(true);
+
+        // Set relevant event panel buttons to disabled.
+        buttonsPanel.enablePauseButton(false);
+        buttonsPanel.enableNextEventsButton(false);
+
+        // Toggle connection button to proper setting.
+        buttonsPanel.toggleConnectButton();
     }
 
     /**
@@ -923,14 +1018,22 @@
         logItem.setEnabled(false);
         terminalItem.setEnabled(false);
         steeringItem.setEnabled(false);
+
+        // Enable relevant event panel buttons.
+        buttonsPanel.enablePauseButton(true);
+        buttonsPanel.setPauseModeState(jobPanel.pauseMode());
+
+        // Toggle connection button to proper settings.
+        buttonsPanel.toggleConnectButton();
     }
 
     /**
      * Exit from the application.
      */
     private void exit() {
-        if (connection != null)
-            connection.cleanup();
+        if (connection != null) {
+            this.cleanupConnection();
+        }
         System.exit(0);
     }
 
@@ -988,15 +1091,6 @@
     }
 
     /**
-     * Loop until a connection is requested by the GUI.
-     */
-    private void waitForConnectionRequest() {
-        while (!connectionRequested()) {
-            try { Thread.sleep(1000); } catch (InterruptedException e)  {};
-        }        
-    }
-
-    /**
      * Get the current max events setting.
      * @return The maximum number of events to process before disconnect.
      */
@@ -1005,46 +1099,53 @@
     }
 
     /**
-     * Run a monitoring session, which is everything that happens from waiting for and 
-     * getting a connection request to disconnecting after the event processing thread finishes.
+     * Execute a monitoring session.
      */
     private void session() {
 
-        logger.log(Level.FINER, "Waiting for connection request.");
+        logger.log(Level.INFO, "Starting a new monitoring session.");
         
-        // Wait until connection is requested by the GUI.
-        waitForConnectionRequest();
-
-        // Setup LCSim.
-        setupLCSim();
+        int endStatus = ConnectionStatus.DISCONNECTING;
         
-        // Start a remote AIDA session if selected in GUI.
-        setupRemoteAida();
+        try {
 
-        // Connect to the ET system.
-        connect();
+            // Setup LCSim.
+            setupLCSim();
 
-        // Create the event processing thread.
-        Thread t = createEventProcessorThread();
+            // Start a remote AIDA session if selected in GUI.
+            setupRemoteAida();
 
-        // Start up the event processing thread.
-        t.start();
-        logger.log(Level.INFO, "Started event processing thread.");
+            // Connect to the ET system.
+            connect();
 
-        // Wait for the event processing thread to finish.
-        try {
-            logger.log(Level.FINER, "Joining event processor thread.");
-            t.join();
+            // Create the event processing thread.
+            eventProcessingThread = createEventProcessingThread();
+
+            // Start up the event processing thread.
+            eventProcessingThread.start();
+            logger.log(Level.FINEST, "Started event processing thread.");
+
+            logHandler.flush();
+            
+            // Wait for the event processing thread to finish.
+            try {
+                 eventProcessingThread.join();
+            }
+            catch (InterruptedException e) {}
+            logger.log(Level.INFO, "Event processor finished with status <" + ConnectionStatus.toString(eventProcessor.getStatus()) + ">.");
+            endStatus = eventProcessor.getStatus();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+            logger.log(Level.SEVERE, "Fatal error in monitoring session.");
+            endStatus = ConnectionStatus.ERROR;
+        }
+        finally {
+            logHandler.flush();
+            // Disconnect if needed.
+            if (getConnectionStatus() != ConnectionStatus.DISCONNECTED)
+                disconnect(endStatus);
         }
-        catch (InterruptedException e) {}
-        t = null;
-        logger.log(Level.FINE, "Event processor exited with status <" + ConnectionStatus.toString(eventProcessor.getStatus()) + ">.");
-
-        // Disconnect with the status which was set by the event processor, if not disconnected already.
-        if (getConnectionStatus() != ConnectionStatus.DISCONNECTED)
-            disconnect(eventProcessor.getStatus());
-        
-        logger.log(Level.INFO, "Monitoring session ended.");
     }
 
     /**
@@ -1069,20 +1170,24 @@
      * Create the Thread for processing EtEvents.
      * @return The Thread for event processing.
      */
-    private Thread createEventProcessorThread()
+    private Thread createEventProcessingThread()
     {
+        // Create a new event processor.
         eventProcessor = new DefaultEtEventProcessor(
                 this.connection, 
                 this.eventBuilder, 
                 this.jobManager, 
                 getMaxEvents(), 
                 disconnectOnError(),
-                logHandler);
-        eventProcessor.addListener(new MonitoringApplicationEtListener(this));
+                this.logHandler);
+        // Add the application's listener for callback to GUI.
+        eventProcessor.addListener(this.etListener);
+        // Set initial pause mode from job panel after which it can be toggled via event buttons.
+        eventProcessor.pauseMode(this.jobPanel.pauseMode()); 
         Runnable run = new Runnable() {
             public void run() {
                 eventProcessor.process();
-            }
+             }
         };
         return new Thread(run, "EtEvent Processing Thread");
     }
@@ -1094,12 +1199,14 @@
 
         logger.log(Level.INFO, "Connecting to ET system.");
         
+        setConnectionStatus(ConnectionStatus.CONNECTION_REQUESTED);
+        
         // Make sure applicable menu items are enabled or disabled.
         setConnectedGuiState();
 
         // Create a connection to the ET server.
         createEtConnection();
-        
+
         logger.log(Level.INFO, "Successfully connected to ET system.");
     }
 
@@ -1151,47 +1258,116 @@
         return jobPanel.disconnectOnErrorCheckBox.isSelected();
     }
 
+    private void disconnect() {
+        disconnect(ConnectionStatus.DISCONNECTING);
+    }
+
     /**
      * Disconnect from the ET session with a particular status.
      * @param status The connection status.
      */
     synchronized private void disconnect(int status) {
 
-        logger.log(Level.FINER, "Disconnecting from ET system.");
-        
+        logger.log(Level.CONFIG, "Disconnecting from ET system with status <" + ConnectionStatus.toString(status) + ">.");
+
+        // Check if disconnected already.
         if (getConnectionStatus() == ConnectionStatus.DISCONNECTED) {
-            logger.log(Level.WARNING, "Disconnect was called but already disconnected.");
+            logger.log(Level.WARNING, "ET system is already disconnected.");
+            return;
+        }
+
+        // Check if in the process of disconnecting.
+        if (getConnectionStatus() == ConnectionStatus.DISCONNECTING) {
+            logger.log(Level.WARNING, "ET system is already disconnecting.");
             return;
         }
         
         // Show a warning dialog box before disconnecting, if this option is selected.
         if (warnOnDisconnect()) {
-            logger.log(Level.FINER, "Waiting for user to verify disconnect request.");
+            logger.log(Level.FINEST, "Waiting for user to verify disconnect request.");
             showDialog("You are about to be disconnected.");
         }
 
-        // Set the application status from the caller while disconnecting.
-        setConnectionStatus(status);
-
-        // Disconnect from the ET system.
-        if (connection != null) {
-
-            // Disconnect from the ET system.
-            // FIXME: This can block forever!  
-            // Should probably put on a separate thread and wait for 10 seconds or so.
-            connection.cleanup(); 
-
-            // The connection is now unusable so set it to null.
-            connection = null;
+        // Stop event processing if currently connected.
+        if (eventProcessor != null) {
+            logger.log(Level.FINE, "Stopping the event processor.");
+            // This sets a flag to tell the event processor to exit from the process loop.
+            // It will not work if it is blocked but in that case the processor will exit
+            // when the ET connection goes down.
+            eventProcessor.stop();
         }
+        
+        // Set the application status from the caller.
+        setConnectionStatus(status);        
+       
+        // Cleanup the ET session.
+        cleanupConnection();
 
         // Update state of GUI to disconnected.
         setDisconnectedGuiState();
 
         // Finally, change application state to fully disconnected.
         setConnectionStatus(ConnectionStatus.DISCONNECTED);
-        
-        logger.log(Level.INFO, "Disconnected from ET system.");
+
+        logger.log(Level.CONFIG, "Disconnected from ET system.");
+    }
+
+    class EtCleanupThread extends Thread {
+
+        boolean succeeded;
+
+        EtCleanupThread() {
+            super("ET Cleanup Thread");
+        }
+
+        public void run() {
+            connection.cleanup();
+            connection = null;
+            succeeded = true;
+        }
+
+        public boolean succeeded() {
+            return succeeded;
+        }
+
+        public void stopCleanup() {
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Cleanup the ET connection.
+     */
+    private void cleanupConnection() {
+
+        if (connection != null) {
+
+            // Put cleanup on a separate thread in case it hangs.
+            EtCleanupThread cleanupThread = new EtCleanupThread();
+            logger.log(Level.FINEST, "Starting EtCleanupThread to disconnect from ET system.");
+            logHandler.flush();
+            cleanupThread.start();
+            try {
+                cleanupThread.join(this.maxCleanupTime);
+            }
+            catch (InterruptedException e) {}
+
+            if (cleanupThread.succeeded()) {
+                logger.log(Level.INFO, "EtCleanupThread succeeded in disconnecting from ET system.");
+            }
+            else {
+                logger.log(Level.SEVERE, "EtCleanupThread failed to disconnect.  Your station <" + this.connection.stat.getName() + "> is zombified.");
+                // Make the cleanup thread yield.
+                cleanupThread.stopCleanup();
+                // Stop the cleanup thread.
+                cleanupThread.stop();
+                // Join to cleanup thread until it dies.
+                logger.log(Level.FINEST, "Waiting for EtCleanupThread to die");
+                logger.log(Level.FINEST, "EtCleanupThread was killed.");
+                // The connection is now unusable so set it to null.
+                this.connection = null;
+            }                                         
+        }
     }
 
     /**
@@ -1199,12 +1375,15 @@
      */
     private void setupLCSim() {
 
-        logger.log(Level.FINER, "Setting up LCSim.");
-        
+        logger.log(Level.CONFIG, "Setting up LCSim.");
+
+        // Clear AIDA tree in case plots hanging around from previous sessions.
+        resetAidaTree();
+
         // Check if the LCSim steering file looks valid.
         if (!validSteering()) {
             logger.log(Level.SEVERE, "Steering file <" + getSteering() + "> is not valid.");
-            throw new RuntimeException("Invalid steering file or resource.");
+            throw new RuntimeException("Invalid steering file or resource < " + getSteering() + ">.");
         }
 
         // Get steering resource or file as a String parameter.
@@ -1231,7 +1410,7 @@
             throw new RuntimeException("Failed to setup LCSim.", e);
         }
 
-        logger.log(Level.INFO, "LCSim setup with steering <" + steering + ">.");
+        logger.log(Level.CONFIG, "LCSim setup with steering <" + steering + ">.");
     }
 
     /**
@@ -1240,8 +1419,8 @@
      */
     private void createEventBuilder() {
 
-        logger.log(Level.FINER, "Initializing event builder.");
-        
+        logger.log(Level.CONFIG, "Initializing event builder.");
+
         // Setup the EventBuilder class.
         String eventBuilderClassName = getEventBuilderClassName();
 
@@ -1253,8 +1432,8 @@
 
         // Set the detector name on the event builder so it can find conditions data.
         eventBuilder.setDetectorName(getDetectorName());
-        
-        logger.log(Level.INFO, "Successfully initialized event builder <" + eventBuilderClassName + ">.");
+
+        logger.log(Level.CONFIG, "Successfully initialized event builder <" + eventBuilderClassName + ">.");
     }       
 
     /**
@@ -1263,22 +1442,23 @@
      */
     private void createEtConnection() {
 
-        logger.log(Level.FINER, "Creating ET connection.");
-        
-        // Cache connection parameters.
+        // Cache connection parameters from GUI to local variable.
         connectionParameters = getConnectionParameters();
 
         // Setup connection to ET system.
         connection = EtConnection.createEtConnection(connectionParameters);
+
         if (connection != null) {
+
             // Set status to connected as there is now a live ET connection.
             setConnectionStatus(ConnectionStatus.CONNECTED);
-            logger.log(Level.INFO, "Successfully created ET connection.");
+
+            logger.log(Level.CONFIG, "Created ET connection to <" + connectionParameters.etName + ">.");
         } 
         else {
             // An error occurred.
             setConnectionStatus(ConnectionStatus.ERROR);
-            logger.log(Level.SEVERE, "Failed to create ET connection.");
+            logger.log(Level.SEVERE, "Failed to create ET connection to <" + connectionParameters.etName + ">.");
             throw new RuntimeException("Failed to create ET connection.");
         }
     }
@@ -1351,7 +1531,7 @@
             }
         }
     }
-    
+
     /**
      * Clear all data from log table.
      */
@@ -1359,4 +1539,55 @@
         logTableModel.setRowCount(0);
         logger.log(Level.INFO, "Log was cleared.");
     }
+
+    /**
+     * Get whether or not connected to ET system with active monitoring session.
+     * @return True if connected to ET system; false if not.
+     */
+    boolean connected() {
+        return connectionStatus == ConnectionStatus.CONNECTED;
+    }
+
+    /**
+     * Get next set of events from event processor if in pause mode.
+     */
+    private void next() {
+        if (connectionStatus == ConnectionStatus.CONNECTED) {   
+            logger.log(Level.FINE, "Notifying event processor to get next events.");
+            eventProcessor.nextEvents();
+        }
+        else {
+            logger.log(Level.WARNING, "Ignored next events command because app is disconnected.");
+        }
+    }
+
+    private void resume() {
+        if (connected()) {
+            // Notify event processor to continue.
+            eventProcessor.pauseMode(false);
+            eventProcessor.nextEvents();
+
+            // Set state of event buttons.
+            buttonsPanel.setPauseModeState(false);
+            
+            // Toggle job panel setting.
+            jobPanel.pauseModeCheckBox.setSelected(false);
+                        
+            logger.log(Level.FINEST, "Disabled pause mode and will now process in real-time.");
+        }
+    }
+    
+    private void pause() { 
+        if (connected()) {
+            eventProcessor.pauseMode(true);
+            buttonsPanel.setPauseModeState(true);
+            jobPanel.pauseModeCheckBox.setSelected(true);
+            logger.log(Level.FINEST, "Enabled pause mode.");
+        }
+    } 
+
[truncated at 1000 lines; 6 more skipped]

hps-java/src/main/java/org/lcsim/hps/monitoring
MonitoringCommands.java 1.8 -> 1.9
diff -u -r1.8 -r1.9
--- MonitoringCommands.java	29 Apr 2012 23:17:49 -0000	1.8
+++ MonitoringCommands.java	3 May 2012 16:59:28 -0000	1.9
@@ -1,31 +1,37 @@
 package org.lcsim.hps.monitoring;
 
 /**
- * These strings are used to identify ActionEvents in the MonitoringApplication
- * and its sub-components.
+ * These strings are used to identify ActionEvents in the MonitoringApplication.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
 final class MonitoringCommands {
-	static final String connectCmd = "connect";
-	static final String disconnectCmd = "disconnect";
-	static final String saveConnectionCmd = "saveConnection";
-	static final String loadConnectionCmd = "loadConnection";
-	static final String resetConnectionSettingsCmd = "resetConnectionSettings";
-	static final String resetEventsCmd = "resetEvents";
-	static final String savePlotsCmd = "savePlots";
-	static final String resetDriversCmd = "resetDrivers";
-	static final String steeringCmd = "steering";
-	static final String eventBuilderCmd = "eventBuilder";
-	static final String refreshCmd = "eventRefresh";
-	static final String exitCmd = "exit";
-	static final String logCmd = "logFile";
-	static final String terminalCmd = "logTerminal";
-	static final String screenshotCmd = "screenshot";
-	static final String eventRefreshCmd = "eventRefreshEdit";
-	static final String updateTimeCmd = "updateTime";
-	static final String setSteeringFileCmd = "setSteeringFile";
-	static final String setMaxEventsCmd = "setMaxEvents";
-	static final String saveLogCmd = "saveLogRecords";
-	static final String clearLogCmd = "clearLogRecords";
+
+    private MonitoringCommands() {}
+
+    static final String connectCmd = "connect";
+    static final String disconnectCmd = "disconnect";
+    static final String saveConnectionCmd = "saveConnection";
+    static final String loadConnectionCmd = "loadConnection";
+    static final String resetConnectionSettingsCmd = "resetConnectionSettings";
+    static final String resetEventsCmd = "resetEvents";
+    static final String savePlotsCmd = "savePlots";
+    static final String resetDriversCmd = "resetDrivers";
+    static final String steeringCmd = "steering";
+    static final String eventBuilderCmd = "eventBuilder";
+    static final String refreshCmd = "eventRefresh";
+    static final String exitCmd = "exit";
+    static final String logCmd = "logFile";
+    static final String terminalCmd = "logTerminal";
+    static final String screenshotCmd = "screenshot";
+    static final String eventRefreshCmd = "eventRefreshEdit";
+    static final String updateTimeCmd = "updateTime";
+    static final String setSteeringFileCmd = "setSteeringFile";
+    static final String setMaxEventsCmd = "setMaxEvents";
+    static final String saveLogCmd = "saveLogRecords";
+    static final String clearLogCmd = "clearLogRecords";
+    static final String pauseCmd = "pause";
+    static final String resumeCmd = "resume";
+    static final String nextCmd = "next";
+    static final String logLevelCmd = "logLevel";
 }
\ No newline at end of file

hps-java/src/main/java/org/lcsim/hps/monitoring/svt
SensorOccupancyPlotsDriver.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- SensorOccupancyPlotsDriver.java	29 Apr 2012 23:18:09 -0000	1.1
+++ SensorOccupancyPlotsDriver.java	3 May 2012 16:59:28 -0000	1.2
@@ -34,146 +34,147 @@
  */
 public class SensorOccupancyPlotsDriver extends Driver implements Resettable {
 
-	private String rawTrackerHitCollectionName = "RawTrackerHitMaker_RawTrackerHits";
-	private String trackerName = "Tracker";
-	private AIDA aida = AIDA.defaultInstance();
-	private IPlotter plotter;
-	private Detector detector;
-	private List<SiSensor> sensors;
-	private Map<String, int[]> occupancyMap;
-	private Map<String, Integer> sensorRegionMap;
-	private int eventCount = 0;
-	private int eventRefreshRate = 1000;
-
-	public SensorOccupancyPlotsDriver() {
-	}
-
-	public void setRawTrackerHitCollectionName(String rawTrackerHitCollectionName) {
-		this.rawTrackerHitCollectionName = rawTrackerHitCollectionName;
-	}
-
-	public void setEventRefreshRate(int eventRefreshRate) {
-		this.eventRefreshRate = eventRefreshRate;
-	}
-
-	private int computePlotterRegion(SiSensor sensor) {
-
-		IIdentifierHelper helper = sensor.getIdentifierHelper();
-		IIdentifier id = sensor.getIdentifier();
-
-		int layer = helper.getValue(id, "layer"); // 1-10; axial layers are odd layers; stereo layers are even
-		int module = helper.getValue(id, "module"); // 0-1; module number is top or bottom
-
-		// Compute the sensor's x and y grid coordinates and then translate to region number.
-		int ix = (layer - 1) / 2;
-		int iy = 0;
-		if (module > 0) {
-			iy += 2;
-		}
-		if (layer % 2 == 0) {
-			iy += 1;
-		}
-		int region = ix * 4 + iy;
-		//System.out.println(sensor.getName() + "; lyr=" + layer + "; mod=" + module + " -> xy[" + ix + "][" + iy + "] -> reg="+region);
-		return region;
-	}
-
-	protected void detectorChanged(Detector detector) {
-
-		// Setup the plotter.
-		IAnalysisFactory fac = aida.analysisFactory();
-		plotter = fac.createPlotterFactory().create(detector.getDetectorName() + " : HPS SVT Sensor Occupancy Plots");
-		IPlotterStyle pstyle = plotter.style();
-		pstyle.dataStyle().fillStyle().setColor("green");
-		pstyle.dataStyle().markerStyle().setColor("green");
-		pstyle.dataStyle().errorBarStyle().setVisible(false);
-		pstyle.statisticsBoxStyle().setVisible(false);
-
-		// Create regions.
-		plotter.createRegions(5, 4);
-
-		// Cache Detector object.
-		this.detector = detector;
-
-		// Make a list of SiSensors in the SVT.
-		sensors = this.detector.getSubdetector(trackerName).getDetectorElement().findDescendants(SiSensor.class);
-
-		// Reset the data structure that keeps track of strip occupancies.
-		resetOccupancyMap();
-
-		// For now throw an error if there are "too many" sensors.
-		if (sensors.size() > 20) {
-			throw new RuntimeException("Can't handle > 20 sensors at a time.");
-		}
-
-		// Map a map of sensors to their region numbers in the plotter.
-		sensorRegionMap = new HashMap<String, Integer>();
-		for (SiSensor sensor : sensors) {
-			int region = computePlotterRegion(sensor);
-			sensorRegionMap.put(sensor.getName(), region);
-		}
-
-		// Setup the occupancy plots.
-		aida.tree().cd("/");
-		for (SiSensor sensor : sensors) {
-			IHistogram1D occupancyPlot = aida.histogram1D(sensor.getName(), 640, 0, 639);
-			occupancyPlot.reset();
-			int region = sensorRegionMap.get(sensor.getName());
-			plotter.region(region).plot(occupancyPlot);
-			JASHist hist = ((PlotterRegion) plotter.region(region)).getPlot();
-			hist.setAllowUserInteraction(false);
-			hist.setAllowPopupMenus(false);
-		}
-		plotter.show();
-	}
-
-	public void process(EventHeader event) {
-		if (event.hasCollection(RawTrackerHit.class, rawTrackerHitCollectionName)) {
-
-			// Get RawTrackerHit collection from event.
-			List<RawTrackerHit> rawTrackerHits = event.get(RawTrackerHit.class, rawTrackerHitCollectionName);
-
-			// Increment strip hit count.
-			for (RawTrackerHit hit : rawTrackerHits) {
-				int[] strips = occupancyMap.get(hit.getDetectorElement().getName());
-				strips[hit.getIdentifierFieldValue("strip")] += 1;
-			}
-
-			// Plot strip occupancies.
-			if (eventCount % eventRefreshRate == 0) {
-				for (SiSensor sensor : sensors) {
-					IHistogram1D sensorHist = aida.histogram1D(sensor.getName());
-					sensorHist.reset();
-					int[] strips = occupancyMap.get(sensor.getName());
-					for (int i = 0; i < strips.length; i++) {
-						double stripOccupancy = (double) strips[i] / (double) (eventCount);
-						if (stripOccupancy != 0) {
-							sensorHist.fill(i, stripOccupancy);
-						}
-					}
-				}
-			}
-
-			// Increment event counter.
-			++eventCount;
-		}
-	}
-
-	private void resetOccupancyMap() {
-		occupancyMap = new HashMap<String, int[]>();
-		for (SiSensor sensor : sensors) {
-			occupancyMap.put(sensor.getName(), new int[640]);
-		}
-	}
-
-	public void endOfData() {
-		if (plotter != null) {
-			plotter.hide();
-		}
-	}
-
-	public void reset() {
-		eventCount = 0;
-		resetOccupancyMap();
-	}
+    private String rawTrackerHitCollectionName = "RawTrackerHitMaker_RawTrackerHits";
+    private String trackerName = "Tracker";
+    private AIDA aida = AIDA.defaultInstance();
+    private IPlotter plotter;
+    private Detector detector;
+    private List<SiSensor> sensors;
+    private Map<String, int[]> occupancyMap;
+    private Map<String, Integer> sensorRegionMap;
+    private int eventCount = 0;
+    private int eventRefreshRate = 1000;
+
+    public SensorOccupancyPlotsDriver() {
+    }
+
+    public void setRawTrackerHitCollectionName(String rawTrackerHitCollectionName) {
+        this.rawTrackerHitCollectionName = rawTrackerHitCollectionName;
+    }
+
+    public void setEventRefreshRate(int eventRefreshRate) {
+        this.eventRefreshRate = eventRefreshRate;
+    }
+
+    private int computePlotterRegion(SiSensor sensor) {
+
+        IIdentifierHelper helper = sensor.getIdentifierHelper();
+        IIdentifier id = sensor.getIdentifier();
+
+        int layer = helper.getValue(id, "layer"); // 1-10; axial layers are odd layers; stereo layers are even
+        int module = helper.getValue(id, "module"); // 0-1; module number is top or bottom
+
+        // Compute the sensor's x and y grid coordinates and then translate to region number.
+        int ix = (layer - 1) / 2;
+        int iy = 0;
+        if (module > 0) {
+            iy += 2;
+        }
+        if (layer % 2 == 0) {
+            iy += 1;
+        }
+        int region = ix * 4 + iy;
+        //System.out.println(sensor.getName() + "; lyr=" + layer + "; mod=" + module + " -> xy[" + ix + "][" + iy + "] -> reg="+region);
+        return region;
+    }
+
+    protected void detectorChanged(Detector detector) {
+
+        // Setup the plotter.
+        IAnalysisFactory fac = aida.analysisFactory();
+        plotter = fac.createPlotterFactory().create(detector.getDetectorName() + " : HPS SVT Sensor Occupancy Plots");
+        IPlotterStyle pstyle = plotter.style();
+        pstyle.dataStyle().fillStyle().setColor("green");
+        pstyle.dataStyle().markerStyle().setColor("green");
+        pstyle.dataStyle().errorBarStyle().setVisible(false);
+        pstyle.statisticsBoxStyle().setVisible(false);
+
+        // Create regions.
+        plotter.createRegions(5, 4);
+
+        // Cache Detector object.
+        this.detector = detector;
+
+        // Make a list of SiSensors in the SVT.
+        sensors = this.detector.getSubdetector(trackerName).getDetectorElement().findDescendants(SiSensor.class);
+
+        // Reset the data structure that keeps track of strip occupancies.
+        resetOccupancyMap();
+
+        // For now throw an error if there are "too many" sensors.
+        if (sensors.size() > 20) {
+            throw new RuntimeException("Can't handle > 20 sensors at a time.");
+        }
+
+        // Map a map of sensors to their region numbers in the plotter.
+        sensorRegionMap = new HashMap<String, Integer>();
+        for (SiSensor sensor : sensors) {
+            int region = computePlotterRegion(sensor);
+            sensorRegionMap.put(sensor.getName(), region);
+        }
+
+        // Setup the occupancy plots.
+        aida.tree().cd("/");
+        for (SiSensor sensor : sensors) {
+            IHistogram1D occupancyPlot = aida.histogram1D(sensor.getName(), 640, 0, 639);
+            occupancyPlot.reset();
+            int region = sensorRegionMap.get(sensor.getName());
+            plotter.region(region).plot(occupancyPlot);
+            JASHist hist = ((PlotterRegion) plotter.region(region)).getPlot();
+            hist.setAllowUserInteraction(false);
+            hist.setAllowPopupMenus(false);
+        }
+        plotter.show();
+    }
+
+    public void process(EventHeader event) {
+        if (event.hasCollection(RawTrackerHit.class, rawTrackerHitCollectionName)) {
+
+            // Get RawTrackerHit collection from event.
+            List<RawTrackerHit> rawTrackerHits = event.get(RawTrackerHit.class, rawTrackerHitCollectionName);
+
+            // Increment strip hit count.
+            for (RawTrackerHit hit : rawTrackerHits) {
+                int[] strips = occupancyMap.get(hit.getDetectorElement().getName()); // FIXME: Null pointer exception here???
+                strips[hit.getIdentifierFieldValue("strip")] += 1;
+            }
+
+            // Plot strip occupancies.
+            if (eventCount % eventRefreshRate == 0) {
+                for (SiSensor sensor : sensors) {
+                    IHistogram1D sensorHist = aida.histogram1D(sensor.getName());
+                    sensorHist.reset();
+                    int[] strips = occupancyMap.get(sensor.getName());
+                    for (int i = 0; i < strips.length; i++) {
+                        double stripOccupancy = (double) strips[i] / (double) (eventCount);
+                        if (stripOccupancy != 0) {
+                            sensorHist.fill(i, stripOccupancy);
+                        }
+                    }
+                }
+            }
+
+            // Increment event counter.
+            ++eventCount;
+        }
+    }
+
+    private void resetOccupancyMap() {
+        occupancyMap = new HashMap<String, int[]>();
+        for (SiSensor sensor : sensors) {
+            System.out.println("putting map entry for --> " + sensor.getName());
+            occupancyMap.put(sensor.getName(), new int[640]);
+        }
+    }
+
+    public void endOfData() {
+        if (plotter != null) {
+            plotter.hide();
+        }
+    }
+
+    public void reset() {
+        eventCount = 0;
+        resetOccupancyMap();
+    }
 }
\ No newline at end of file
CVSspam 0.2.12


Use REPLY-ALL to reply to list

To unsubscribe from the LCD-CVS list, click the following link:
https://listserv.slac.stanford.edu/cgi-bin/wa?SUBED1=LCD-CVS&A=1