Print

Print


Author: [log in to unmask]
Date: Wed Mar 25 14:43:27 2015
New Revision: 2555

Log:
Merge in r2364 through r2554 to prod branch.

Added:
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerPlotsModule.java
      - copied unchanged from r2554, java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerPlotsModule.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/DaqMapHandler.java
      - copied unchanged from r2554, java/trunk/conditions/src/main/java/org/hps/conditions/svt/DaqMapHandler.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java
      - copied unchanged from r2554, java/trunk/conditions/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtConditionsReader.java
      - copied unchanged from r2554, java/trunk/conditions/src/main/java/org/hps/conditions/svt/SvtConditionsReader.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/daqconfig/
      - copied from r2554, java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/daqconfig/
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/triggerbank/
      - copied from r2554, java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/triggerbank/
    java/branches/prod/evio/src/main/java/org/hps/evio/BasicPhysicsEventBuilder.java
      - copied unchanged from r2554, java/trunk/evio/src/main/java/org/hps/evio/BasicPhysicsEventBuilder.java
    java/branches/prod/evio/src/test/java/org/hps/evio/EpicsScalarDataTest.java
      - copied unchanged from r2554, java/trunk/evio/src/test/java/org/hps/evio/EpicsScalarDataTest.java
    java/branches/prod/evio/src/test/java/org/hps/evio/ScalarsTest.java
      - copied unchanged from r2554, java/trunk/evio/src/test/java/org/hps/evio/ScalarsTest.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConditionsCollectionTableModel.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConditionsCollectionTableModel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConditionsPanel.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/ConditionsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/EventDashboard.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/EventDashboard.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusEventsTable.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusEventsTable.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusPanel.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/AIDAServer.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/AIDAServer.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/EventTagFilter.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/EventTagFilter.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/MonitoringApplicationEventBuilder.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/MonitoringApplicationEventBuilder.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/PhysicsSyncEventStation.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/PhysicsSyncEventStation.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/PreStartEtStation.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/PreStartEtStation.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/RunnableEtStation.java
      - copied unchanged from r2554, java/trunk/monitoring-app/src/main/java/org/hps/monitoring/application/util/RunnableEtStation.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SamplesPlots.java
      - copied unchanged from r2554, java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SamplesPlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtHitPlots.java
      - copied unchanged from r2554, java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtHitPlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalLedSequenceMonitor.java
      - copied unchanged from r2554, java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalLedSequenceMonitor.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/PlotterRegistry.java
      - copied unchanged from r2554, java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/PlotterRegistry.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/ValueProvider.java
      - copied unchanged from r2554, java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/ValueProvider.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsListener.java
      - copied unchanged from r2554, java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsListener.java
    java/branches/prod/record-util/src/main/java/org/hps/record/epics/
      - copied from r2554, java/trunk/record-util/src/main/java/org/hps/record/epics/
    java/branches/prod/record-util/src/main/java/org/hps/record/et/EtStationThread.java
      - copied unchanged from r2554, java/trunk/record-util/src/main/java/org/hps/record/et/EtStationThread.java
    java/branches/prod/record-util/src/main/java/org/hps/record/et/PreStartProcessor.java
      - copied unchanged from r2554, java/trunk/record-util/src/main/java/org/hps/record/et/PreStartProcessor.java
    java/branches/prod/record-util/src/main/java/org/hps/record/et/SyncEventProcessor.java
      - copied unchanged from r2554, java/trunk/record-util/src/main/java/org/hps/record/et/SyncEventProcessor.java
    java/branches/prod/record-util/src/main/java/org/hps/record/scalars/
      - copied from r2554, java/trunk/record-util/src/main/java/org/hps/record/scalars/
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/EcalLedSequenceMonitor.lcsim
      - copied unchanged from r2554, java/trunk/steering-files/src/main/resources/org/hps/steering/monitoring/EcalLedSequenceMonitor.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/readout/CommRun2014TightPairs.lcsim
      - copied unchanged from r2554, java/trunk/steering-files/src/main/resources/org/hps/steering/readout/CommRun2014TightPairs.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/readout/EngineeringRun2014PrescaledTriggers.lcsim
      - copied unchanged from r2554, java/trunk/steering-files/src/main/resources/org/hps/steering/readout/EngineeringRun2014PrescaledTriggers.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/holly/ECalSimReadout.lcsim
      - copied unchanged from r2554, java/trunk/steering-files/src/main/resources/org/hps/steering/users/holly/ECalSimReadout.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/holly/QuickEcalReadout.lcsim
      - copied unchanged from r2554, java/trunk/steering-files/src/main/resources/org/hps/steering/users/holly/QuickEcalReadout.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/mgraham/NoTimeNoTriggerReadout.lcsim
      - copied unchanged from r2554, java/trunk/steering-files/src/main/resources/org/hps/steering/users/mgraham/NoTimeNoTriggerReadout.lcsim
Removed:
    java/branches/prod/conditions/src/main/java/org/hps/conditions/deprecated/
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TriggerModule.java
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/daqconfig/
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/triggerbank/
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/RunPanel.java
    java/branches/prod/record-util/src/test/java/org/
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/readout/CommRun2014LoosePairs.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/celentan/LedAnalysis.lcsim
    java/branches/prod/users/src/main/java/org/hps/users/celentan/LedAnalysis.java
Modified:
    java/branches/prod/   (props changed)
    java/branches/prod/analysis/src/main/java/org/hps/analysis/ecal/EcalHitPlots.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java
    java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectCollection.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObject.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/database/ConditionsObjectConverter.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableMetaData.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableRegistry.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalBadChannel.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalCalibration.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalGain.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLed.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLedCalibration.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalTimeShift.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtChannel.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtDaqMapping.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/CalibrationHandler.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtChannel.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDaqMapping.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java
    java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtChannel.java
    java/branches/prod/conditions/src/test/java/org/hps/conditions/EngRunConditionsTest.java
    java/branches/prod/detector-data/detectors/HPS-ECalCommissioning-v2/detector.properties
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCPrimaryTriggerDriver.java
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/SinglesTriggerDriver.java
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TestRunTriggerDriver.java
    java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TriggerDriver.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalPedestalCalculator.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterDriver.java
    java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java
    java/branches/prod/evio/pom.xml
    java/branches/prod/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java
    java/branches/prod/evio/src/main/java/org/hps/evio/LCSimTestRunEventBuilder.java
    java/branches/prod/evio/src/main/java/org/hps/evio/SVTHitWriter.java
    java/branches/prod/evio/src/main/java/org/hps/evio/TestRunSvtEvioReader.java
    java/branches/prod/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java
    java/branches/prod/evio/src/main/java/org/hps/evio/TriggerConfigEvioReader.java
    java/branches/prod/evio/src/main/java/org/hps/evio/TriggerDataWriter.java
    java/branches/prod/monitoring-app/   (props changed)
    java/branches/prod/monitoring-app/pom.xml
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/AbstractFieldsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionSettingsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/DatePanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/EventProcessing.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/JobSettingsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogTable.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Main.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MenuBar.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplicationFrame.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/PlotPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SettingsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/TriggerDiagnosticsPanel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConfigurationModel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatusModel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/RunModel.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ErrorHandler.java
    java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ResourceUtil.java
    java/branches/prod/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop
    java/branches/prod/monitoring-app/src/main/scripts/evio_file_producer.sh
    java/branches/prod/monitoring-app/src/main/scripts/start_et_ring.sh
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtTimingInPlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/trackrecon/TrackTimePlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalClusterPlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalHitPlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringPlots.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringUtilities.java
    java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalPedestalViewer.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/StatusCode.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatusImpl.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalPedestalMonitor.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java
    java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java
    java/branches/prod/record-util/src/main/java/org/hps/record/RecordProcessingException.java
    java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoop.java
    java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoopConfiguration.java
    java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java
    java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java
    java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioFileProducer.java
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/ECalLedCommissioning.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/EcalMonitoringFinal.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/TriggerDiagnosticsMonitoring.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/celentan/LedAnalysisFromEvio.lcsim
    java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/mgraham/AlignmentMonitorTest.lcsim
    java/branches/prod/tracking/src/main/java/org/hps/readout/svt/SimpleSvtReadout.java
    java/branches/prod/users/src/main/java/org/hps/users/celentan/DummyDriverRaw.java
    java/branches/prod/users/src/main/java/org/hps/users/phansson/ROOTFlatTupleDriver.java

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/ecal/EcalHitPlots.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/ecal/EcalHitPlots.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/ecal/EcalHitPlots.java	Wed Mar 25 14:43:27 2015
@@ -4,11 +4,13 @@
 import hep.aida.IHistogram2D;
 import hep.aida.IPlotter;
 import hep.aida.IPlotterFactory;
+
 import java.util.List;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.SSPData;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
+
 import org.hps.recon.ecal.ECalUtils;
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.SSPData;
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.GenericObject;

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java	Wed Mar 25 14:43:27 2015
@@ -1,10 +1,14 @@
 package org.hps.analysis.trigger;
+
+import hep.aida.IHistogram1D;
+import hep.aida.IHistogram2D;
 
 import java.awt.Point;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -18,28 +22,32 @@
 import org.hps.analysis.trigger.event.TriggerEfficiencyModule;
 import org.hps.analysis.trigger.event.TriggerMatchEvent;
 import org.hps.analysis.trigger.event.TriggerMatchStatus;
+import org.hps.analysis.trigger.event.TriggerPlotsModule;
 import org.hps.analysis.trigger.util.OutputLogger;
 import org.hps.analysis.trigger.util.Pair;
 import org.hps.analysis.trigger.util.PairTrigger;
 import org.hps.analysis.trigger.util.SinglesTrigger;
 import org.hps.analysis.trigger.util.Trigger;
 import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
-import org.hps.readout.ecal.TriggerModule;
-import org.hps.readout.ecal.daqconfig.ConfigurationManager;
-import org.hps.readout.ecal.daqconfig.DAQConfig;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.SSPCluster;
-import org.hps.readout.ecal.triggerbank.SSPData;
-import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger;
-import org.hps.readout.ecal.triggerbank.SSPPairTrigger;
-import org.hps.readout.ecal.triggerbank.SSPSinglesTrigger;
-import org.hps.readout.ecal.triggerbank.SSPTrigger;
-import org.hps.readout.ecal.triggerbank.TIData;
+import org.hps.recon.ecal.daqconfig.ConfigurationManager;
+import org.hps.recon.ecal.daqconfig.DAQConfig;
+import org.hps.recon.ecal.daqconfig.PairTriggerConfig;
+import org.hps.recon.ecal.daqconfig.SinglesTriggerConfig;
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.SSPCluster;
+import org.hps.recon.ecal.triggerbank.SSPData;
+import org.hps.recon.ecal.triggerbank.SSPNumberedTrigger;
+import org.hps.recon.ecal.triggerbank.SSPPairTrigger;
+import org.hps.recon.ecal.triggerbank.SSPSinglesTrigger;
+import org.hps.recon.ecal.triggerbank.SSPTrigger;
+import org.hps.recon.ecal.triggerbank.TIData;
+import org.hps.recon.ecal.triggerbank.TriggerModule;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.Cluster;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.GenericObject;
 import org.lcsim.util.Driver;
+import org.lcsim.util.aida.AIDA;
 
 public class TriggerDiagnosticDriver extends Driver {
 	// Store the LCIO collection names for the needed objects.
@@ -62,6 +70,10 @@
 	private int activeTrigger = -1;
 	private TriggerModule[] singlesTrigger = new TriggerModule[2];
 	private TriggerModule[] pairsTrigger = new TriggerModule[2];
+	private boolean[][] singlesCutsEnabled = new boolean[2][3];
+	private boolean[][] pairCutsEnabled = new boolean[2][7];
+	private boolean[] singlesTriggerEnabled = new boolean[2];
+	private boolean[] pairTriggerEnabled = new boolean[2];
 	
 	// Verification settings.
 	private int nsa = 100;
@@ -69,13 +81,14 @@
 	private int windowWidth = 200;
 	private int hitAcceptance = 1;
 	private int noiseThreshold = 50;
+	private double energyAcceptance = 0.003;
 	private long localWindowStart = 0;
 	private boolean readDAQConfig = false;
-	private double energyAcceptance = 0.03;
 	private int localWindowThreshold = 10 * 1000;
 	private boolean performClusterVerification = true;
 	private boolean performSinglesTriggerVerification = true;
 	private boolean performPairTriggerVerification = true;
+	private boolean enforceTimeCompliance = false;
 	
 	// Efficiency tracking variables.
 	private ClusterMatchStatus clusterRunStats = new ClusterMatchStatus();
@@ -103,7 +116,8 @@
     private boolean printSinglesTriggerInternalFail = true;
     private boolean printPairTriggerEfficiencyFail = true;
     private boolean printPairTriggerInternalFail = true;
-    
+    private int     printResultsEveryNEvents = 100000;
+
     // Cut index arrays for trigger verification.
 	private static final int ENERGY_MIN   = TriggerDiagnosticUtil.SINGLES_ENERGY_MIN;
 	private static final int ENERGY_MAX   = TriggerDiagnosticUtil.SINGLES_ENERGY_MAX;
@@ -113,12 +127,101 @@
 	private static final int ENERGY_SLOPE = TriggerDiagnosticUtil.PAIR_ENERGY_SLOPE;
 	private static final int COPLANARITY  = TriggerDiagnosticUtil.PAIR_COPLANARITY;
     
+	// Track the total run time.
+	private long startTime = -1;
+	private long endTime = -1;
+	
+	// Cut names for logging.
+	private static final String[][] cutNames = {
+			{ "E_min", "E_max", "hit count", "null" },
+			{ "E_sum", "E_diff", "E_slope", "coplanar" }
+	};
+	
+	// Temporary AIDA Plots
+	private TriggerPlotsModule globalTriggerPlots = new TriggerPlotsModule(0, 0);
+	private static final int RECON   = 0;
+	private static final int SSP     = 1;
+	private static final int ALL     = 0;
+	private static final int MATCHED = 1;
+	private static final int FAILED  = 2;
+	private AIDA aida = AIDA.defaultInstance();
+	private IHistogram1D[][] clusterHitPlot = {
+			{
+				aida.histogram1D("cluster/Recon Cluster Hit Count (All)",     9, 0.5, 9.5),
+				aida.histogram1D("cluster/Recon Cluster Hit Count (Matched)", 9, 0.5, 9.5),
+				aida.histogram1D("cluster/Recon Cluster Hit Count (Failed)",  9, 0.5, 9.5)
+			},
+			{
+				aida.histogram1D("cluster/SSP Cluster Hit Count (All)",     9, 0.5, 9.5),
+				aida.histogram1D("cluster/SSP Cluster Hit Count (Matched)", 9, 0.5, 9.5),
+				aida.histogram1D("cluster/SSP Cluster Hit Count (Failed)",  9, 0.5, 9.5)
+			}
+	};
+	private IHistogram1D[][] clusterEnergyPlot = {
+			{
+				aida.histogram1D("cluster/Recon Cluster Energy (All)",     300, 0.0, 3.0),
+				aida.histogram1D("cluster/Recon Cluster Energy (Matched)", 300, 0.0, 3.0),
+				aida.histogram1D("cluster/Recon Cluster Energy (Failed)",  300, 0.0, 3.0)
+			},
+			{
+				aida.histogram1D("cluster/SSP Cluster Energy (All)",     300, 0.0, 3.0),
+				aida.histogram1D("cluster/SSP Cluster Energy (Matched)", 300, 0.0, 3.0),
+				aida.histogram1D("cluster/SSP Cluster Energy (Failed)",  300, 0.0, 3.0)
+			}
+	};
+	private IHistogram1D[][] clusterTimePlot = {
+			{
+				aida.histogram1D("cluster/Recon Cluster Time (All)",     115, 0, 460),
+				aida.histogram1D("cluster/Recon Cluster Time (Matched)", 115, 0, 460),
+				aida.histogram1D("cluster/Recon Cluster Time (Failed)",  115, 0, 460)
+			},
+			{
+				aida.histogram1D("cluster/SSP Cluster Time (All)",     115, 0, 460),
+				aida.histogram1D("cluster/SSP Cluster Time (Matched)", 115, 0, 460),
+				aida.histogram1D("cluster/SSP Cluster Time (Failed)",  115, 0, 460)
+			}
+	};
+	private IHistogram2D[][] clusterPositionPlot = {
+			{
+				aida.histogram2D("cluster/Recon Cluster Position (All)",     47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/Recon Cluster Position (Matched)", 47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/Recon Cluster Position (Failed)",  47, -23.5, 23.5, 11, -5.5, 5.5)
+			},
+			{
+				aida.histogram2D("cluster/SSP Cluster Position (All)",     47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/SSP Cluster Position (Matched)", 47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/SSP Cluster Position (Failed)",  47, -23.5, 23.5, 11, -5.5, 5.5)
+			}
+	};
+	private IHistogram2D[] energyhitDiffPlot = {
+		aida.histogram2D("cluster/Recon-SSP Energy-Hit Difference (All)",     21, -0.010, 0.010, 6, -3, 3),
+		aida.histogram2D("cluster/Recon-SSP Energy-Hit Difference (Matched)", 21, -0.010, 0.010, 6, -3, 3),
+		aida.histogram2D("cluster/Recon-SSP Energy-Hit Difference (Failed)",  21, -0.010, 0.010, 6, -3, 3)
+	};
+	
 	/**
 	 * Define the trigger modules. This should be replaced by parsing
 	 * the DAQ configuration at some point.
 	 */
 	@Override
 	public void startOfData() {
+		// By default, all triggers and cuts are enabled.
+		for(int i = 0; i < 2; i++) {
+			// Enable the triggers.
+			pairTriggerEnabled[i] = true;
+			singlesTriggerEnabled[i] = true;
+			
+			// Enable the singles cuts.
+			for(int j = 0; j < singlesCutsEnabled.length; j++) {
+				singlesCutsEnabled[i][j] = true;
+			}
+			
+			// Enable the pair cuts.
+			for(int j = 0; j < pairCutsEnabled.length; j++) {
+				pairCutsEnabled[i][j] = true;
+			}
+		}
+		
 		// If the DAQ configuration should be read, attach a listener
 		// to track when it updates.
 		if(readDAQConfig) {
@@ -127,6 +230,10 @@
 				public void actionPerformed(ActionEvent e) {
 					// Get the DAQ configuration.
 					DAQConfig daq = ConfigurationManager.getInstance();
+					
+					// Update the plotting energy slope values.
+					globalTriggerPlots.setEnergySlopeParamF(0, daq.getSSPConfig().getPair1Config().getEnergySlopeCutConfig().getParameterF());
+					globalTriggerPlots.setEnergySlopeParamF(1, daq.getSSPConfig().getPair2Config().getEnergySlopeCutConfig().getParameterF());
 					
 					// Load the DAQ settings from the configuration manager.
 					singlesTrigger[0].loadDAQConfiguration(daq.getSSPConfig().getSingles1Config());
@@ -137,6 +244,33 @@
 					nsb = daq.getFADCConfig().getNSB();
 					windowWidth = daq.getFADCConfig().getWindowWidth();
 					
+					// Get the trigger configurations from the DAQ.
+					SinglesTriggerConfig[] singles = { daq.getSSPConfig().getSingles1Config(),
+							daq.getSSPConfig().getSingles2Config() };
+					PairTriggerConfig[] pairs = { daq.getSSPConfig().getPair1Config(),
+							daq.getSSPConfig().getPair2Config() };
+					
+					// Update the enabled/disabled statuses.
+					for(int i = 0; i < 2; i++) {
+						// Set the trigger enabled status.
+						pairTriggerEnabled[i] = pairs[i].isEnabled();
+						singlesTriggerEnabled[i] = singles[i].isEnabled();
+						
+						// Set the singles cut statuses.
+						singlesCutsEnabled[i][ENERGY_MIN] = singles[i].getEnergyMinCutConfig().isEnabled();
+						singlesCutsEnabled[i][ENERGY_MAX] = singles[i].getEnergyMaxCutConfig().isEnabled();
+						singlesCutsEnabled[i][HIT_COUNT] = singles[i].getHitCountCutConfig().isEnabled();
+						
+						// Set the pair cut statuses.
+						pairCutsEnabled[i][ENERGY_MIN] = pairs[i].getEnergyMinCutConfig().isEnabled();
+						pairCutsEnabled[i][ENERGY_MAX] = pairs[i].getEnergyMaxCutConfig().isEnabled();
+						pairCutsEnabled[i][HIT_COUNT] = pairs[i].getHitCountCutConfig().isEnabled();
+						pairCutsEnabled[i][3 + ENERGY_SUM] = pairs[i].getEnergySumCutConfig().isEnabled();
+						pairCutsEnabled[i][3 + ENERGY_DIFF] = pairs[i].getEnergyDifferenceCutConfig().isEnabled();
+						pairCutsEnabled[i][3 + ENERGY_SLOPE] = pairs[i].getEnergySlopeCutConfig().isEnabled();
+						pairCutsEnabled[i][3 + COPLANARITY] = pairs[i].getCoplanarityCutConfig().isEnabled();
+					}
+					
 					// Print a DAQ configuration settings header.
 					System.out.println();
 					System.out.println();
@@ -154,51 +288,6 @@
 		System.out.println("======================================================================");
 		System.out.println("=== Cluster/Trigger Verification Settings ============================");
 		System.out.println("======================================================================");
-		
-		// Set the FADC settings.
-		nsa = 100;
-		nsb = 20;
-		windowWidth = 400;
-		
-		/*
-		// Define the first singles trigger.
-		singlesTrigger[0] = new TriggerModule();
-		singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.010);
-		singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 8.191);
-		singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2);
-		
-		// Define the second singles trigger.
-		singlesTrigger[1] = new TriggerModule();
-		singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.010);
-		singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 0.050);
-		singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2);
-		
-		// Define the first pairs trigger.
-		pairsTrigger[0] = new TriggerModule();
-		pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.020);
-		pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 0.055);
-		pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 1);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.010);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 2.000);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 1.200);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.400);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.0055);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 40);
-		pairsTrigger[0].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 16);
-		
-		// Define the second pairs trigger.
-		pairsTrigger[1] = new TriggerModule();
-		pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.010);
-		pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 1.800);
-		pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.020);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 2.000);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 1.200);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.400);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.0055);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 40);
-		pairsTrigger[1].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 16);
-		*/
 		
 		// Define the first singles trigger.
 		singlesTrigger[0] = new TriggerModule();
@@ -255,6 +344,10 @@
 	 */
 	@Override
 	public void endOfData() {
+		PrintResults();
+	}
+	
+	public void PrintResults() {
 		// Print the cluster/trigger verification header.
 		System.out.println();
 		System.out.println();
@@ -263,17 +356,48 @@
 		System.out.println("======================================================================");
 		
 		// Print the general event failure rate.
-		System.out.println("Event Failure Rate:");
-		System.out.printf("\tNoise Events          :: %d / %d (%7.3f%%)%n",
+		int headSpaces = getPrintSpaces(totalEvents, triggerRunStats[0].getEventsOfTypeSeen(0), triggerRunStats[0].getEventsOfTypeSeen(1),
+				triggerRunStats[1].getEventsOfTypeSeen(0), triggerRunStats[1].getEventsOfTypeSeen(1));
+		System.out.println("General Event Statistics:");
+		System.out.printf("\tEvent Start Time      :: %.3f s%n", (startTime / Math.pow(10, 9)));
+		System.out.printf("\tEvent End Time        :: %.3f%n", (endTime / Math.pow(10, 9)));
+		System.out.printf("\tEvent Run Time        :: %.3f%n", ((endTime - startTime) / Math.pow(10, 9)));
+		System.out.printf("\tNoise Events          :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
 				noiseEvents, totalEvents, (100.0 * noiseEvents / totalEvents));
-		System.out.printf("\tCluster Events Failed :: %d / %d (%7.3f%%)%n",
+		System.out.printf("\tCluster Events Failed :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
 				failedClusterEvents, totalEvents, (100.0 * failedClusterEvents / totalEvents));
-		System.out.printf("\tSingles Events Failed :: %d / %d (%7.3f%%)%n",
+		System.out.printf("\tSingles Events Failed :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
 				failedSinglesEvents, totalEvents, (100.0 * failedSinglesEvents / totalEvents));
-		System.out.printf("\tPair Events Failed    :: %d / %d (%7.3f%%)%n",
+		System.out.printf("\tPair Events Failed    :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
 				failedPairEvents, totalEvents, (100.0 * failedPairEvents / totalEvents));
 		
+		// Print out how many events were triggered by a type along
+		// with how many were verified.
+		System.out.println();
+		System.out.println("Event Triggering Type Verification:");
+		System.out.printf("\tSingles Trigger 1     :: %" + headSpaces + "d / %" + headSpaces + "d",
+				triggerRunStats[0].getEventsOfTypeSeen(0), triggerRunStats[0].getEventsOfType(0));
+		if(triggerRunStats[0].getEventsOfType(0) != 0) {
+			System.out.printf(" (%7.3f%%)%n", (100.0 * triggerRunStats[0].getEventsOfTypeSeen(0) / triggerRunStats[0].getEventsOfType(0)));
+		} else { System.out.println(); }
+		System.out.printf("\tSingles Trigger 2     :: %" + headSpaces + "d / %" + headSpaces + "d",
+				triggerRunStats[0].getEventsOfTypeSeen(1), triggerRunStats[0].getEventsOfType(1));
+		if(triggerRunStats[0].getEventsOfType(0) != 0) {
+			System.out.printf(" (%7.3f%%)%n", (100.0 * triggerRunStats[0].getEventsOfTypeSeen(1) / triggerRunStats[0].getEventsOfType(1)));
+		} else { System.out.println(); }
+		System.out.printf("\tPair Trigger 1        :: %" + headSpaces + "d / %" + headSpaces + "d",
+				triggerRunStats[1].getEventsOfTypeSeen(0), triggerRunStats[0].getEventsOfType(0));
+		if(triggerRunStats[1].getEventsOfType(0) != 0) {
+			System.out.printf(" (%7.3f%%)%n", (100.0 * triggerRunStats[1].getEventsOfTypeSeen(0) / triggerRunStats[1].getEventsOfType(0)));
+		} else { System.out.println(); }
+		System.out.printf("\tPair Trigger 2        :: %" + headSpaces + "d / %" + headSpaces + "d",
+				triggerRunStats[1].getEventsOfTypeSeen(1), triggerRunStats[0].getEventsOfType(1));
+		if(triggerRunStats[1].getEventsOfType(0) != 0) {
+			System.out.printf(" (%7.3f%%)%n", (100.0 * triggerRunStats[1].getEventsOfTypeSeen(1) / triggerRunStats[1].getEventsOfType(1)));
+		} else { System.out.println(); }
+		
 		// Print the cluster verification data.
+		System.out.println();
 		System.out.println("Cluster Verification:");
 		System.out.printf("\tRecon Clusters        :: %d%n", clusterRunStats.getReconClusterCount());
 		System.out.printf("\tSSP Clusters          :: %d%n", clusterRunStats.getSSPClusterCount());
@@ -282,7 +406,7 @@
 		System.out.printf("\tFailed (Energy)       :: %d%n", clusterRunStats.getEnergyFailures());
 		System.out.printf("\tFailed (Hit Count)    :: %d%n", clusterRunStats.getHitCountFailures());
 		if(clusterRunStats.getReconClusterCount() == 0) { System.out.printf("\tCluster Efficiency    :: N/A%n"); }
-		else { System.out.printf("\tCluster Efficiency :: %7.3f%%%n", 100.0 * clusterRunStats.getMatches() / clusterRunStats.getReconClusterCount()); }
+		else { System.out.printf("\tCluster Efficiency    :: %7.3f%%%n", 100.0 * clusterRunStats.getMatches() / clusterRunStats.getReconClusterCount()); }
 		
 		// Print the trigger verification data.
 		for(int triggerType = 0; triggerType < 2; triggerType++) {
@@ -310,61 +434,67 @@
 			else { System.out.printf("(%7.3f%%)%n" , (100.0 * triggerRunStats[triggerType].getMatchedReconTriggers() / triggerRunStats[triggerType].getReconTriggerCount())); }
 			
 			// Print the individual cut performances.
-			int halfSSPTriggers = triggerRunStats[triggerType].getSSPSimTriggerCount() / 2;
 			if(triggerType == 0) {
 			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+				int sspTriggerCount = triggerRunStats[0].getTotalSSPTriggers(triggerNum);
+				System.out.println();
+				System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
+				if(sspTriggerCount == 0) {
+					System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[0].getUnmatchedTriggers(triggerNum));
+						System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), sspTriggerCount);
+						System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX), sspTriggerCount);
+						System.out.printf("\t\tCluster Hit Count          :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), sspTriggerCount);
+					} else {
+						System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[0].getUnmatchedTriggers(triggerNum));
+						System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), sspTriggerCount,
+								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN) / sspTriggerCount));
+						System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX), sspTriggerCount,
+								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX) / sspTriggerCount));
+						System.out.printf("\t\tCluster Hit Count          :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), sspTriggerCount,
+								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT) / sspTriggerCount));
+					}
+				}
+			} else {
+				for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+					int sspTriggerCount = triggerRunStats[1].getTotalSSPTriggers(triggerNum);
 					System.out.println();
 					System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
-					if(triggerRunStats[0].getSSPSimTriggerCount() == 0) {
-						System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), halfSSPTriggers);
-						System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX), halfSSPTriggers);
-						System.out.printf("\t\tCluster Hit Count          :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), halfSSPTriggers);
+					if(sspTriggerCount == 0) {
+						System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[1].getUnmatchedTriggers(triggerNum));
+						System.out.printf("\t\tPair Energy Sum            :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), sspTriggerCount);
+						System.out.printf("\t\tPair Energy Difference     :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF), sspTriggerCount);
+						System.out.printf("\t\tPair Energy Slope          :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE), sspTriggerCount);
+						System.out.printf("\t\tPair Coplanarity           :: %" + spaces + "d / %" + spaces + "d%n",
+								triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), sspTriggerCount);
 					} else {
-						System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), halfSSPTriggers,
-								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN) / halfSSPTriggers));
-						System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX), halfSSPTriggers,
-								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX) / halfSSPTriggers));
-						System.out.printf("\t\tCluster Hit Count          :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), halfSSPTriggers,
-								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT) / halfSSPTriggers));
+						System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[1].getUnmatchedTriggers(triggerNum));
+						System.out.printf("\t\tPair Energy Sum            :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), sspTriggerCount,
+								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM) / sspTriggerCount));
+						System.out.printf("\t\tPair Energy Difference     :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF), sspTriggerCount,
+								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF) / sspTriggerCount));
+						System.out.printf("\t\tPair Energy Slope          :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE), sspTriggerCount,
+								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE) / sspTriggerCount));
+						System.out.printf("\t\tPair Coplanarity           :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
+								triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), sspTriggerCount,
+								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY) / sspTriggerCount));
 					}
 				}
-			} else {
-				for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
-					System.out.println();
-					System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
-					if(triggerRunStats[1].getSSPSimTriggerCount() == 0) {
-						System.out.printf("\t\tPair Energy Sum            :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), halfSSPTriggers);
-						System.out.printf("\t\tPair Energy Difference     :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF), halfSSPTriggers);
-						System.out.printf("\t\tPair Energy Slope          :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE), halfSSPTriggers);
-						System.out.printf("\t\tPair Coplanarity           :: %" + spaces + "d / %" + spaces + "d%n",
-								triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), halfSSPTriggers);
-					} else {
-						System.out.printf("\t\tPair Energy Sum            :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), halfSSPTriggers,
-								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM) / halfSSPTriggers));
-						System.out.printf("\t\tPair Energy Difference     :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF), halfSSPTriggers,
-								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF) / halfSSPTriggers));
-						System.out.printf("\t\tPair Energy Slope          :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE), halfSSPTriggers,
-								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE) / halfSSPTriggers));
-						System.out.printf("\t\tPair Coplanarity           :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
-								triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), halfSSPTriggers,
-								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY) / halfSSPTriggers));
-					}
-				}
-			}
-		}
-		
+			}
+		}
+		
+		System.out.println();
 		this.efficiencyRunStats.printModule();
 	}
 	
@@ -374,7 +504,7 @@
 	@Override
 	public void process(EventHeader event) {
 		// ==========================================================
-		// ==== Initialize the Event ================================
+		// ==== Event Pre-Initialization ============================
 		// ==========================================================
         
 		// If DAQ settings are to be used, check if they are initialized
@@ -385,14 +515,12 @@
 			}
 		}
 		
-        // Print the verification header.
-		OutputLogger.printNewLine(2);
-		OutputLogger.println("======================================================================");
-		OutputLogger.println("==== Cluster/Trigger Verification ====================================");
-		OutputLogger.println("======================================================================");
-		
 		// Increment the total event count.
 		totalEvents++;
+		
+		if(totalEvents%printResultsEveryNEvents == 0){
+			PrintResults();
+		}
 		
 		// Reset the output buffer and print flags.
 		clusterFail = false;
@@ -400,12 +528,61 @@
 		singlesEfficiencyFail = false;
 		pairInternalFail = false;
 		pairEfficiencyFail = false;
+		OutputLogger.clearLog();
+		
+		// Track the times.
+		if(startTime == -1) { startTime = event.getTimeStamp(); }
+		else { endTime = event.getTimeStamp(); }
+		
+		
+		
+		// ==========================================================
+		// ==== Output GTP Information ==============================
+		// ==========================================================
+		
+        // Print the verification header.
+		OutputLogger.printNewLine(2);
+		OutputLogger.println("======================================================================");
+		OutputLogger.println("==== FADC/GTP Readout ================================================");
+		OutputLogger.println("======================================================================");
+		
+		OutputLogger.println("FADC Hits:");
+		for(CalorimeterHit hit : event.get(CalorimeterHit.class, "EcalCalHits")) {
+			int ix = hit.getIdentifierFieldValue("ix");
+			int iy = hit.getIdentifierFieldValue("iy");
+			OutputLogger.printf("\tHit at (%3d, %3d) with %7.3f GeV at time %3.0f ns%n", ix, iy, hit.getCorrectedEnergy(), hit.getTime());
+		}
+		OutputLogger.printNewLine(2);
+		OutputLogger.println("GTP Clusters:");
+		for(Cluster cluster : event.get(Cluster.class, clusterCollectionName)) {
+			OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(cluster));
+			for(CalorimeterHit hit : cluster.getCalorimeterHits()) {
+				int ix = hit.getIdentifierFieldValue("ix");
+				int iy = hit.getIdentifierFieldValue("iy");
+				OutputLogger.printf("\t\t> (%3d, %3d) :: %7.3f GeV%n", ix, iy, hit.getCorrectedEnergy());
+			}
+		}
+		
+		
+		
+		// ==========================================================
+		// ==== Initialize the Event ================================
+		// ==========================================================
+		
+        // Print the verification header.
+		OutputLogger.printNewLine(2);
+		OutputLogger.println("======================================================================");
+		OutputLogger.println("==== Cluster/Trigger Verification ====================================");
+		OutputLogger.println("======================================================================");
 		
 		
 		
 		// ==========================================================
 		// ==== Obtain SSP and TI Banks =============================
 		// ==========================================================
+		
+		// Output the event number and information.
+		OutputLogger.printf("Event Number %d (%d)%n", totalEvents, event.getEventNumber());
 		
 		// Get the SSP clusters.
 		if(event.hasCollection(GenericObject.class, bankCollectionName)) {
@@ -441,6 +618,9 @@
 					} else if(tiBank.isCalibTrigger()) {
 						OutputLogger.println("Trigger type :: Cosmic");
 						activeTrigger = TriggerDiagnosticUtil.TRIGGER_COSMIC;
+					} else {
+						System.err.println("TriggerDiagnosticDriver: Skipping event; no TI trigger source found.");
+						return;
 					}
 				}
 			}
@@ -454,6 +634,12 @@
 					OutputLogger.printf("%d SSP clusters found.%n", sspClusters.size());
 				}
 			}
+		}
+		
+		// Make sure that both an SSP bank and a TI bank were found.
+		if(tiBank == null || sspBank == null) {
+			System.err.println("TriggerDiagnosticDriver :: SEVERE WARNING :: TI bank or SSP bank missing from event!");
+			return;
 		}
 		
 		
@@ -523,7 +709,6 @@
 					reconClusters.add(reconCluster);
 					OutputLogger.println(" [  verifiable  ]");
 				} else { OutputLogger.println(" [ unverifiable ]"); }
-				
 			}
 			
 			// Output the number of verifiable clusters found.
@@ -576,10 +761,10 @@
 				(pairInternalFail && printPairTriggerInternalFail) ||
 				(pairEfficiencyFail && printPairTriggerEfficiencyFail)) {
 			OutputLogger.printLog();
-		}
-		
-		
-		
+		}	
+		
+		
+				
 		// ==========================================================
 		// ==== Process Local Tracked Variables =====================
 		// ==========================================================
@@ -604,6 +789,10 @@
 			localWindowStart = Calendar.getInstance().getTimeInMillis();
 		}
 	}
+
+	public void setPrintResultsEveryNEvents(int N) {
+		printResultsEveryNEvents = N;
+	}
 	
 	public void setPrintOnClusterFailure(boolean state) {
 		printClusterFail = state;
@@ -653,8 +842,16 @@
 		energyAcceptance = window;
 	}
 	
+	public void setEnforceStrictTimeCompliance(boolean state) {
+		enforceTimeCompliance = state;
+	}
+	
 	public void setReadDAQConfig(boolean state) {
 		readDAQConfig = state;
+	}
+	
+	public void setLocalWindowThresholdMilliseconds(int localWindowThreshold) {
+	    this.localWindowThreshold = localWindowThreshold;
 	}
 	
 	/**
@@ -679,15 +876,164 @@
 		OutputLogger.println("=== Cluster Verification =============================================");
 		OutputLogger.println("======================================================================");
 		
+		
+		
+		// ==========================================================
+		// ==== Perform Cluster Matching ============================
+		// ==========================================================
+		
 		// Track the number of cluster pairs that were matched and that
 		// failed by failure type.
 		ClusterMatchEvent event = new ClusterMatchEvent();
 		
-		
-		
-		// ==========================================================
-		// ==== Produce the Cluster Position Mappings ===============
-		// ==========================================================
+		if(enforceTimeCompliance) {
+			event = matchClustersTimeCompliant(reconClusters, sspClusters, energyAcceptance, hitAcceptance);
+		} else {
+			event = matchClusters(reconClusters, sspClusters, energyAcceptance, hitAcceptance);
+		}
+		
+		// Add the event results to the global results.
+		clusterRunStats.addEvent(event, reconClusters, sspClusters);
+		clusterLocalStats.addEvent(event, reconClusters, sspClusters);
+		
+		
+		
+		// ==========================================================
+		// ==== Output Event Summary ================================
+		// ==========================================================
+		
+		// Print the valid reconstructed clusters and populate their
+		// distribution graphs.
+		OutputLogger.println();
+		OutputLogger.println("Verified Reconstructed Clusters:");
+		if(!reconClusters.isEmpty()) {
+			for(Cluster reconCluster : reconClusters) {
+				OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(reconCluster));
+			}
+		} else { OutputLogger.println("\tNone"); }
+		
+		// Print the SSP clusters and populate their distribution graphs.
+		OutputLogger.println("SSP Clusters:");
+		if(!sspClusters.isEmpty()) {
+			for(SSPCluster sspCluster : sspClusters) {
+				OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(sspCluster));
+			}
+		} else { OutputLogger.println("\tNone"); }
+		
+		// Print the matched clusters.
+		OutputLogger.println("Matched Clusters:");
+		if(event.getMatchedPairs().size() != 0) {
+			// Iterate over the matched pairs.
+			for(ClusterMatchedPair pair : event.getMatchedPairs()) {
+				// If the pair is a match, print it out.
+				if(pair.isMatch()) {
+					OutputLogger.printf("\t%s --> %s%n",
+							TriggerDiagnosticUtil.clusterToString(pair.getReconstructedCluster()),
+							TriggerDiagnosticUtil.clusterToString(pair.getSSPCluster()));
+				}
+			}
+		}
+		 else { OutputLogger.println("\tNone"); }
+		
+		// Print event statistics.
+		OutputLogger.println();
+		OutputLogger.println("Event Statistics:");
+		OutputLogger.printf("\tRecon Clusters     :: %d%n", reconClusters.size());
+		OutputLogger.printf("\tClusters Matched   :: %d%n", event.getMatches());
+		OutputLogger.printf("\tFailed (Position)  :: %d%n", event.getPositionFailures());
+		OutputLogger.printf("\tFailed (Time)      :: %d%n", event.getTimeFailures());
+		OutputLogger.printf("\tFailed (Energy)    :: %d%n", event.getEnergyFailures());
+		OutputLogger.printf("\tFailed (Hit Count) :: %d%n", event.getHitCountFailures());
+		OutputLogger.printf("\tCluster Efficiency :: %3.0f%%%n", 100.0 * event.getMatches() / reconClusters.size());
+		
+		// Note whether there was a cluster match failure.
+		if(event.isFailState() || event.getMatches() - reconClusters.size() != 0) {
+			clusterFail = true;
+		}
+		
+		
+		
+		// TEMP :: Populate the cluster diagnostic plots.
+		
+		// Populate the ALL cluster plots.
+		for(Cluster cluster : reconClusters) {
+			clusterHitPlot[RECON][ALL].fill(cluster.getCalorimeterHits().size());
+			clusterEnergyPlot[RECON][ALL].fill(cluster.getEnergy());
+			clusterTimePlot[RECON][ALL].fill(cluster.getCalorimeterHits().get(0).getTime());
+			Point position = TriggerDiagnosticUtil.getClusterPosition(cluster);
+			clusterPositionPlot[RECON][ALL].fill(position.x, position.y);
+		}
+		for(SSPCluster cluster : sspClusters) {
+			clusterHitPlot[SSP][ALL].fill(cluster.getHitCount());
+			clusterEnergyPlot[SSP][ALL].fill(cluster.getEnergy());
+			clusterTimePlot[SSP][ALL].fill(cluster.getTime());
+			clusterPositionPlot[SSP][ALL].fill(cluster.getXIndex(), cluster.getYIndex());
+		}
+		
+		// Populate the matched and failed plots.
+		for(ClusterMatchedPair pair : event.getMatchedPairs()) {
+			 if(pair.getFirstElement() != null && pair.getSecondElement() != null) {
+				double energyDiff = pair.getSecondElement().getEnergy() - pair.getFirstElement().getEnergy();
+				int hitDiff = pair.getSecondElement().getHitCount() - pair.getFirstElement().getCalorimeterHits().size();
+				energyhitDiffPlot[ALL].fill(energyDiff, hitDiff);
+			 }
+			
+			if(pair.isMatch()) {
+				if(pair.getFirstElement() != null) {
+					clusterHitPlot[RECON][MATCHED].fill(pair.getFirstElement().getCalorimeterHits().size());
+					clusterEnergyPlot[RECON][MATCHED].fill(pair.getFirstElement().getEnergy());
+					clusterTimePlot[RECON][MATCHED].fill(pair.getFirstElement().getCalorimeterHits().get(0).getTime());
+					Point position = TriggerDiagnosticUtil.getClusterPosition(pair.getFirstElement());
+					clusterPositionPlot[RECON][MATCHED].fill(position.x, position.y);
+				} if(pair.getSecondElement() != null) {
+					clusterHitPlot[SSP][MATCHED].fill(pair.getSecondElement().getHitCount());
+					clusterEnergyPlot[SSP][MATCHED].fill(pair.getSecondElement().getEnergy());
+					clusterTimePlot[SSP][MATCHED].fill(pair.getSecondElement().getTime());
+					clusterPositionPlot[SSP][MATCHED].fill(pair.getSecondElement().getXIndex(), pair.getSecondElement().getYIndex());
+				} if(pair.getFirstElement() != null && pair.getSecondElement() != null) {
+					double energyDiff = pair.getSecondElement().getEnergy() - pair.getFirstElement().getEnergy();
+					int hitDiff = pair.getSecondElement().getHitCount() - pair.getFirstElement().getCalorimeterHits().size();
+					energyhitDiffPlot[MATCHED].fill(energyDiff, hitDiff);
+				}
+			} else {
+				if(pair.getFirstElement() != null) {
+					clusterHitPlot[RECON][FAILED].fill(pair.getFirstElement().getCalorimeterHits().size());
+					clusterEnergyPlot[RECON][FAILED].fill(pair.getFirstElement().getEnergy());
+					clusterTimePlot[RECON][FAILED].fill(pair.getFirstElement().getCalorimeterHits().get(0).getTime());
+					Point position = TriggerDiagnosticUtil.getClusterPosition(pair.getFirstElement());
+					clusterPositionPlot[RECON][FAILED].fill(position.x, position.y);
+				} if(pair.getSecondElement() != null) {
+					clusterHitPlot[SSP][FAILED].fill(pair.getSecondElement().getHitCount());
+					clusterEnergyPlot[SSP][FAILED].fill(pair.getSecondElement().getEnergy());
+					clusterTimePlot[SSP][FAILED].fill(pair.getSecondElement().getTime());
+					clusterPositionPlot[SSP][FAILED].fill(pair.getSecondElement().getXIndex(), pair.getSecondElement().getYIndex());
+				} if(pair.getFirstElement() != null && pair.getSecondElement() != null) {
+					double energyDiff = pair.getSecondElement().getEnergy() - pair.getFirstElement().getEnergy();
+					int hitDiff = pair.getSecondElement().getHitCount() - pair.getFirstElement().getCalorimeterHits().size();
+					energyhitDiffPlot[FAILED].fill(energyDiff, hitDiff);
+				}
+			}
+		}
+	}
+	
+	/**
+     * Performs cluster matching between a collection of reconstructed
+	 * clusters and a collection of SSP clusters with an algorithm that
+	 * ignores the times reported for each cluster.
+	 * @param reconClusters - A collection of reconstructed clusters.
+	 * @param sspClusters - A collection of SSP clusters.
+	 * @param energyWindow - The window of allowed deviation between
+	 * the reconstructed cluster and SSP cluster energies.
+	 * @param hitWindow - The window of allowed deviation between
+	 * the reconstructed cluster and SSP cluster hit counts.
+	 * @return Returns the cluster matching results stored inside a
+	 * <code>clusterMatchEvent</code> object.
+	 */
+	private static final ClusterMatchEvent matchClusters(Collection<Cluster> reconClusters,
+			Collection<SSPCluster> sspClusters, double energyWindow, int hitWindow) {
+		// Track the number of cluster pairs that were matched and that
+		// failed by failure type.
+		ClusterMatchEvent event = new ClusterMatchEvent();
 		
 		// Create maps to link cluster position to the list of clusters
 		// that were found at that location.
@@ -726,12 +1072,6 @@
 			// Add the cluster to the list.
 			sspList.add(sspCluster);
 		}
-		
-		
-		
-		// ==========================================================
-		// ==== Perform Cluster Matching ============================
-		// ==========================================================
 		
 		// For each reconstructed cluster, attempt to match the clusters
 		// with SSP clusters at the same position.
@@ -749,17 +1089,11 @@
 			// reason of position. The remainder of the loop may be
 			// skipped, since there is nothing to check.
 			if(sspList == null || sspList.isEmpty()) {
-				clusterFail = true;
 				for(Cluster cluster : reconList) {
 					event.pairFailPosition(cluster, null);
 				}
 				continue positionLoop;
 			}
-			
-			// If there are more reconstructed clusters than there are
-			// SSP clusters, than a number equal to the difference must
-			// fail by means of positions.
-			if(sspList.size() < reconList.size()) { clusterFail = true; }
 			
 			// Get all possible permutations of SSP clusters.
 			List<List<Pair<Cluster, SSPCluster>>> permutations = getPermutations(reconList, sspList);
@@ -794,20 +1128,31 @@
 					// If either cluster in the pair is null, there
 					// are not enough clusters to perform this match.
 					if(pair.getFirstElement() == null || pair.getSecondElement() == null) {
+						// Log the result.
 						OutputLogger.printf(" [ %18s ]%n", "failure: unpaired");
-						perm.pairFailPosition(pair.getFirstElement(), pair.getSecondElement());
+						
+						// An unpaired SSP cluster does not necessarily
+						// represent a problem. Often, this just means
+						// that the SSP cluster's matching reconstructed
+						// cluster is outside the verification window.
+						if(pair.getSecondElement() == null) {
+							perm.pairFailPosition(pair.getFirstElement(), pair.getSecondElement());
+						}
+						
+						// Skip the rest of the checks.
 						continue pairLoop;
 					}
 					
 					// Check if the reconstructed cluster has an energy
 					// within the allotted threshold of the SSP cluster.
-					if(pair.getSecondElement().getEnergy() >= pair.getFirstElement().getEnergy() * (1 - energyAcceptance) &&
-							pair.getSecondElement().getEnergy() <= pair.getFirstElement().getEnergy() * (1 + energyAcceptance)) {
+					if(pair.getSecondElement().getEnergy() >= pair.getFirstElement().getEnergy() - energyWindow &&
+							pair.getSecondElement().getEnergy() <= pair.getFirstElement().getEnergy() + energyWindow) {
+						
 						// Check that the hit count of the reconstructed
 						// is within the allotted threshold of the SSP
 						// cluster.
-						if(pair.getSecondElement().getHitCount() >= pair.getFirstElement().getCalorimeterHits().size() - hitAcceptance &&
-								pair.getSecondElement().getHitCount() <= pair.getFirstElement().getCalorimeterHits().size() + hitAcceptance) {
+						if(pair.getSecondElement().getHitCount() >= pair.getFirstElement().getCalorimeterHits().size() - hitWindow &&
+								pair.getSecondElement().getHitCount() <= pair.getFirstElement().getCalorimeterHits().size() + hitWindow) {
 							// Designate the pair as a match.
 							perm.pairMatch(pair.getFirstElement(), pair.getSecondElement());
 							OutputLogger.printf(" [ %18s ]%n", "success: matched");
@@ -844,69 +1189,113 @@
 			event.addEvent(bestPerm);
 		} // End Crystal Position Loop
 		
-		// Add the event results to the global results.
-		clusterRunStats.addEvent(event, reconClusters, sspClusters);
-		clusterLocalStats.addEvent(event, reconClusters, sspClusters);
-		
-		
-		
-		// ==========================================================
-		// ==== Output Event Summary ================================
-		// ==========================================================
-		
-		// Print the valid reconstructed clusters and populate their
-		// distribution graphs.
-		OutputLogger.println();
-		OutputLogger.println("Verified Reconstructed Clusters:");
-		if(!reconClusters.isEmpty()) {
-			for(Cluster reconCluster : reconClusters) {
-				OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(reconCluster));
-			}
-		} else { OutputLogger.println("\tNone"); }
-		
-		// Print the SSP clusters and populate their distribution graphs.
-		OutputLogger.println("SSP Clusters:");
-		if(!sspClusters.isEmpty()) {
+		// Return the cluster match summary.
+		return event;
+	}
+	
+	/**
+	 * Performs cluster matching between a collection of reconstructed
+	 * clusters and a collection of SSP clusters using the strictly
+	 * time-compliant algorithm.
+	 * @param reconClusters - A collection of reconstructed clusters.
+	 * @param sspClusters - A collection of SSP clusters.
+	 * @param energyWindow - The window of allowed deviation between
+	 * the reconstructed cluster and SSP cluster energies.
+	 * @param hitWindow - The window of allowed deviation between
+	 * the reconstructed cluster and SSP cluster hit counts.
+	 * @return Returns the cluster matching results stored inside a
+	 * <code>clusterMatchEvent</code> object.
+	 */
+	private static final ClusterMatchEvent matchClustersTimeCompliant(Collection<Cluster> reconClusters,
+			Collection<SSPCluster> sspClusters, double energyWindow, int hitWindow) {
+		// Track the number of cluster pairs that were matched and that
+		// failed by failure type.
+		ClusterMatchEvent event = new ClusterMatchEvent();
+		
+		// Store the clusters which have been successfully paired.
+		Set<SSPCluster> sspMatched = new HashSet<SSPCluster>(sspClusters.size());
+		
+		// Find reconstructed/SSP cluster matched pairs.
+		reconLoop:
+		for(Cluster reconCluster : reconClusters) {
+			// Track whether a position-matched cluster was found.
+			boolean matchedPosition = false;
+			
+			// VERBOSE :: Output the cluster being matched.
+			OutputLogger.printf("Considering %s%n", TriggerDiagnosticUtil.clusterToString(reconCluster));
+			
+			// Search through the SSP clusters for a matching cluster.
+			sspLoop:
 			for(SSPCluster sspCluster : sspClusters) {
-				OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(sspCluster));
-			}
-		} else { OutputLogger.println("\tNone"); }
-		
-		// Print the matched clusters.
-		OutputLogger.println("Matched Clusters:");
-		if(event.getMatchedPairs().size() != 0) {
-			// Iterate over the matched pairs.
-			for(ClusterMatchedPair pair : event.getMatchedPairs()) {
-				// If the pair is a match, print it out.
-				if(pair.isMatch()) {
-					OutputLogger.printf("\t%s --> %s%n",
-							TriggerDiagnosticUtil.clusterToString(pair.getReconstructedCluster()),
-							TriggerDiagnosticUtil.clusterToString(pair.getSSPCluster()));
-				}
-			}
-		}
-		 else { OutputLogger.println("\tNone"); }
-		
-		// Get the number of position failures.
-		int failPosition = event.getPositionFailures();
-		if(sspClusters == null || sspClusters.isEmpty()) {
-			failPosition = (reconClusters == null ? 0 : reconClusters.size());
-		}
-		
-		// Print event statistics.
-		OutputLogger.println();
-		OutputLogger.println("Event Statistics:");
-		OutputLogger.printf("\tRecon Clusters     :: %d%n", reconClusters.size());
-		OutputLogger.printf("\tClusters Matched   :: %d%n", event.getMatches());
-		OutputLogger.printf("\tFailed (Position)  :: %d%n", failPosition);
-		OutputLogger.printf("\tFailed (Energy)    :: %d%n", event.getEnergyFailures());
-		OutputLogger.printf("\tFailed (Hit Count) :: %d%n", event.getHitCountFailures());
-		OutputLogger.printf("\tCluster Efficiency :: %3.0f%%%n", 100.0 * event.getMatches() / reconClusters.size());
-		
-		// Note whether there was a cluster match failure.
-		if(event.getMatches() - reconClusters.size() != 0) {
-			clusterFail = true;
-		}
+				// VERBOSE :: Output the SSP cluster being considered.
+				OutputLogger.printf("\t%s ", TriggerDiagnosticUtil.clusterToString(sspCluster));
+				
+				// If this cluster has been paired, skip it.
+				if(sspMatched.contains(sspCluster)) {
+					OutputLogger.printf("[ %7s; %9s ]%n", "fail", "matched");
+					continue sspLoop;
+				}
+				
+				// Matched clusters must have the same position.
+				if(TriggerDiagnosticUtil.getXIndex(reconCluster) != sspCluster.getXIndex()
+						|| TriggerDiagnosticUtil.getYIndex(reconCluster) != sspCluster.getYIndex()) {
+					OutputLogger.printf("[ %7s; %9s ]%n", "fail", "position");
+					continue sspLoop;
+				}
+				
+				// Note that a cluster was found at this position.
+				matchedPosition = true;
+				
+				// Matched clusters must have the same time-stamp.
+				if(reconCluster.getCalorimeterHits().get(0).getTime() != sspCluster.getTime()) {
+					OutputLogger.printf("[ %7s; %9s ]%n", "fail", "time");
+					continue sspLoop;
+				}
+				
+				// Clusters that pass all of the above checks are the
+				// same cluster.
+				sspMatched.add(sspCluster);
+				
+				// Check that the clusters are sufficiently close in
+				// energy to one another.
+				if(sspCluster.getEnergy() >= reconCluster.getEnergy() - energyWindow
+						&& sspCluster.getEnergy() <= reconCluster.getEnergy() + energyWindow) {
+					// If a cluster matches in energy, check that it
+					// is also sufficiently close in hit count.
+					if(sspCluster.getHitCount() >= reconCluster.getCalorimeterHits().size() - hitWindow &&
+							sspCluster.getHitCount() <= reconCluster.getCalorimeterHits().size() + hitWindow) {
+						// The cluster is a match.
+						event.pairMatch(reconCluster, sspCluster);
+						OutputLogger.printf("[ %7s; %9s ]%n", "success", "matched");
+						continue reconLoop;
+					} else {
+						event.pairFailHitCount(reconCluster, sspCluster);
+						OutputLogger.printf("[ %7s; %9s ]%n", "fail", "hit count");
+						continue reconLoop;
+					} // End hit count check.
+				} else {
+					event.pairFailEnergy(reconCluster, sspCluster);
+					OutputLogger.printf("[ %7s; %9s ]%n", "fail", "energy");
+					continue reconLoop;
+				} // End energy check.
+			}// End SSP loop.
+			
+			// If the reconstructed cluster has not been matched, check
+			// if a cluster was found at the same position. If not, then
+			// the cluster fails by reason of position.
+			if(!matchedPosition) {
+				event.pairFailPosition(reconCluster, null);
+			}
+			
+			// Otherwise, the cluster had a potential matched, but the
+			// time-stamps were off. The cluster fails by reason of time.
+			else {
+				event.pairFailTime(reconCluster, null);
+			}
+		} // End recon loop.
+		
+		// Return the populated match event.
+		return event;
 	}
 	
 	/**
@@ -1002,9 +1391,9 @@
 		OutputLogger.println("SSP Cluster " + (isSingles ? "Singles" : "Pair") + " Triggers");
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
-				OutputLogger.printf("\tTrigger %d :: %s :: %s%n",
+				OutputLogger.printf("\tTrigger %d :: %s :: %3.0f :: %s%n",
 						(triggerNum + 1), triggerPositionString(simTrigger),
-						simTrigger.toString());
+						getTriggerTime(simTrigger), simTrigger.toString());
 			}
 		}
 		if(sspTriggerList.get(0).size() + sspTriggerList.get(1).size() == 0) {
@@ -1015,9 +1404,9 @@
 		OutputLogger.println("Reconstructed Cluster " + (isSingles ? "Singles" : "Pair") + " Triggers");
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 			for(Trigger<?> simTrigger : reconTriggerList.get(triggerNum)) {
-				OutputLogger.printf("\tTrigger %d :: %s :: %s%n",
+				OutputLogger.printf("\tTrigger %d :: %s :: %3.0f :: %s%n",
 						(triggerNum + 1), triggerPositionString(simTrigger),
-						simTrigger.toString());
+						getTriggerTime(simTrigger), simTrigger.toString());
 			}
 		}
 		if(reconTriggerList.get(0).size() + reconTriggerList.get(1).size() == 0) {
@@ -1053,42 +1442,85 @@
 		
 		// Iterate over the triggers.
 		OutputLogger.println();
-		OutputLogger.println("SSP Reported Trigger --> SSP Cluster Trigger Match Status");
+		OutputLogger.println("Matching SSP Reported Triggers to SSP Simulated Triggers:");
 		for(SSPNumberedTrigger sspTrigger : sspTriggers) {
 			// Get the trigger information.
 			int triggerNum = sspTrigger.isFirstTrigger() ? 0 : 1;
-			boolean matchedTrigger = false;
+			OutputLogger.printf("\t%s%n", sspTrigger.toString());
 			
 			// Iterate over the SSP cluster simulated triggers and
 			// look for a trigger that matches.
 			matchLoop:
 			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
-				// If the current SSP trigger has already been
-				// matched, skip it.
-				if(sspTriggerSet.contains(sspTrigger)) { continue matchLoop; }
-				
-				// Otherwise, check whether the reconstructed SSP
-				// cluster trigger matches the SSP trigger.
-				if(compareTriggers(sspTrigger, simTrigger)) {
-					matchedTrigger = true;
-					sspTriggerSet.add(sspTrigger);
-					simTriggerSet.add(simTrigger);
-					event.matchedSSPPair(simTrigger, sspTrigger);
-					break matchLoop;
-				}
-				
-				OutputLogger.printf("\t%s :: Matched: %5b%n", sspTrigger.toString(), matchedTrigger);
+				// VERBOSE :: Output the trigger being considered for
+				//            matching.
+				OutputLogger.printf("\t\tTrigger %d :: %s :: %3.0f :: %s ",
+						(triggerNum + 1), triggerPositionString(simTrigger),
+						getTriggerTime(simTrigger), simTrigger.toString());
+				
+				// If the current SSP trigger has already been matched,
+				// skip it.
+				if(simTriggerSet.contains(simTrigger)) {
+					OutputLogger.printf("[ %-15s ]%n", "failed; matched");
+					continue matchLoop;
+				}
+				
+				// Check that the triggers have the same time. Triggers
+				// generated from SSP bank clusters should always align
+				// in time.
+				if(sspTrigger.getTime() != getTriggerTime(simTrigger)) {
+					OutputLogger.printf("[ %-15s ]%n", "failed; time");
+					continue matchLoop;
+				}
+				
+				// Check whether the trigger cuts match.
+				boolean[] matchedCuts = triggerCutMatch(simTrigger, sspTrigger);
+				for(int i = 0; i < matchedCuts.length; i++) {
+					if(!matchedCuts[i]) {
+						int typeIndex = isSingles ? 0 : 1;
+						OutputLogger.printf("[ %-15s ]%n", String.format("failed; %s", cutNames[typeIndex][i]));
+						continue matchLoop;
+					}
+				}
+				
+				// If all the cuts match, along with the time and the
+				// trigger number, than these triggers are a match.
+				sspTriggerSet.add(sspTrigger);
+				simTriggerSet.add(simTrigger);
+				event.matchedSSPPair(simTrigger, sspTrigger);
+				OutputLogger.printf("[ %-15s ]%n", "success");
+				break matchLoop;
+			}
+		}
+		
+		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
+				globalTriggerPlots.sawTrigger(simTrigger);
+				if(simTriggerSet.contains(simTrigger)) {
+					globalTriggerPlots.matchedTrigger(simTrigger);
+				} else {
+					globalTriggerPlots.failedTrigger(simTrigger);
+				}
 			}
 		}
 		
 		// Iterate over the unmatched simulated triggers again and the
 		// unmatched SSP reported trigger that most closely matches it.
-		simLoop:
+		OutputLogger.println();
+		OutputLogger.println("Matching Failed SSP Reported Triggers to Remaining SSP Simulated Triggers:");
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			simLoop:
 			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
+				OutputLogger.printf("\tTrigger %d :: %s :: %3.0f :: %s%n",
+						(triggerNum + 1), triggerPositionString(simTrigger),
+						getTriggerTime(simTrigger), simTrigger.toString());
+				
 				// Check whether this trigger has already been matched
 				// or not. If it has been matched, skip it.
-				if(simTriggerSet.contains(simTrigger)) { continue simLoop; }
+				if(simTriggerSet.contains(simTrigger)) {
+					OutputLogger.println("\t\tSkipping; already matched successfully");
+					continue simLoop;
+				}
 				
 				// Get the trigger time for the simulated trigger.
 				double simTime = getTriggerTime(simTrigger);
@@ -1099,18 +1531,27 @@
 				boolean[] matchedCut = null;
 				SSPNumberedTrigger bestMatch = null;
 				
+				// Store the readout for the best match.
+				String bestMatchText = null;
+				
 				// Iterate over the reported triggers to find a match.
 				reportedLoop:
 				for(SSPNumberedTrigger sspTrigger : sspTriggers) {
+					OutputLogger.printf("\t\t%s ", sspTrigger.toString());
+					
 					// If the two triggers have different times, this
 					// trigger should be skipped.
 					if(sspTrigger.getTime() != simTime) {
+						OutputLogger.printf("[ %-15s ]%n", "failed; time");
 						continue reportedLoop;
 					}
 					
 					// If this reported trigger has been matched then
 					// it should be skipped.
-					if(sspTriggerSet.contains(sspTrigger)) { continue reportedLoop; }
+					if(sspTriggerSet.contains(sspTrigger)) {
+						OutputLogger.printf("[ %-15s ]%n", "failed; matched");
+						continue reportedLoop;
+					}
 					
 					// Check each of the cuts.
 					boolean[] tempMatchedCut = triggerCutMatch(simTrigger, sspTrigger);
@@ -1119,6 +1560,7 @@
 					// than the previous best match.
 					int tempNumMatched = 0;
 					for(boolean passed : tempMatchedCut) { if(passed) { tempNumMatched++; } }
+					OutputLogger.printf("[ %-15s ]%n", String.format("maybe; %d failed", tempNumMatched));
 					
 					// If the number of matched cuts exceeds the old
 					// best result, this becomes the new best result.
@@ -1126,18 +1568,27 @@
 						numMatched = tempNumMatched;
 						matchedCut = tempMatchedCut;
 						bestMatch = sspTrigger;
+						bestMatchText = String.format("%s%n", sspTrigger.toString());
 					}
 				}
 				
 				// If there was no match found, it means that there were
 				// no triggers that were both unmatched and at the same
 				// time as this simulated trigger.
-				
 				if(bestMatch == null) {
 					if(isSingles) { singlesInternalFail = true; }
 					else { pairInternalFail = true; }
+					event.matchedSSPPair(simTrigger, bestMatch, matchedCut);
+					OutputLogger.printf("\t\tTrigger %d :: %s :: %3.0f :: %s",
+							(triggerNum + 1), triggerPositionString(simTrigger),
+							getTriggerTime(simTrigger), simTrigger.toString());
+					OutputLogger.println(" --> No Valid Match Found");
 				} else {
 					event.matchedSSPPair(simTrigger, bestMatch, matchedCut);
+					OutputLogger.printf("\t\tTrigger %d :: %s :: %3.0f :: %s",
+							(triggerNum + 1), triggerPositionString(simTrigger),
+							getTriggerTime(simTrigger), simTrigger.toString());
+					OutputLogger.println(" --> " + bestMatchText);
 				}
 			}
 		}
@@ -1156,12 +1607,31 @@
 		OutputLogger.println("Recon Cluster Trigger --> SSP Reported Trigger Match Status");
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 			for(Trigger<?> simTrigger : reconTriggerList.get(triggerNum)) {
+				// If the trigger number and type align with the event
+				// trigger type, mark that it was seen.
+				if(triggerNum == 1) {
+					if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_SINGLES_1 && isSingles) {
+						event.setSawEventType(true);
+					} else if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_PAIR_1 && !isSingles) {
+						event.setSawEventType(true);
+					}
+				} else {
+					if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_SINGLES_2 && isSingles) {
+						event.setSawEventType(true);
+					} else if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_PAIR_2 && !isSingles) {
+						event.setSawEventType(true);
+					}
+				}
 				
 				OutputLogger.printf("\tTrigger %d :: %s :: %s%n", (triggerNum + 1),
 						triggerPositionString(simTrigger), simTrigger.toString());
 				
+				// TEMP :: Populate the recon ALL pairs plots.
+				globalTriggerPlots.sawTrigger(simTrigger);
+				
 				// Iterate over the SSP reported triggers and compare
 				// them to the reconstructed cluster simulated trigger.
+				boolean matched = false;
 				matchLoop:
 				for(SSPNumberedTrigger sspTrigger : sspTriggers) {
 					OutputLogger.printf("\t\t\t%s", sspTrigger.toString());
@@ -1182,10 +1652,6 @@
 					}
 					
 					// Test each cut.
-					String[][] cutNames = {
-							{ "E_min", "E_max", "hit count", "null" },
-							{ "E_sum", "E_diff", "E_slope", "coplanar" }
-					};
 					int typeIndex = isSingles ? 0 : 1;
 					boolean[] matchedCuts = triggerCutMatch(simTrigger, sspTrigger);
 					for(int cutIndex = 0; cutIndex < matchedCuts.length; cutIndex++) {
@@ -1200,8 +1666,12 @@
 					sspTriggerSet.add(sspTrigger);
 					event.matchedReconPair(simTrigger, sspTrigger);
 					OutputLogger.print(" [ success         ]%n");
+					globalTriggerPlots.matchedTrigger(simTrigger);
+					matched = true;
 					break matchLoop;
 				}
+				
+				if(!matched) { globalTriggerPlots.failedTrigger(simTrigger); }
 			}
 		}
 		
@@ -1213,14 +1683,19 @@
 		
 		// Get the number of SSP and reconstructed cluster simulated
 		// triggers.
-		
 		int sspSimTriggers = sspTriggerList.get(0).size() + sspTriggerList.get(1).size();
 		int reconSimTriggers = reconTriggerList.get(0).size() + reconTriggerList.get(1).size();
-		int halfSimTriggers = sspSimTriggers / 2;
+		int[] sspTriggerCount = { sspTriggerList.get(0).size(), sspTriggerList.get(1).size() };
 		
 		// Print event statistics.
 		OutputLogger.println();
 		OutputLogger.println("Event Statistics:");
+		OutputLogger.printf("\tSaw Triggering Event Type  :: ");
+		if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_COSMIC || activeTrigger == TriggerDiagnosticUtil.TRIGGER_PULSER) {
+			OutputLogger.println("Unsupported for Cosmic/Pulser");
+		} else {
+			OutputLogger.println("" + event.sawEventType());
+		}
 		OutputLogger.printf("\tSSP Cluster Sim Triggers   :: %d%n", sspSimTriggers);
 		OutputLogger.printf("\tRecon Cluster Sim Triggers :: %d%n", reconSimTriggers);
 		OutputLogger.printf("\tSSP Reported Triggers      :: %d%n", sspTriggers.size());
@@ -1245,23 +1720,26 @@
 			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 				OutputLogger.printf("Trigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
 				if(sspSimTriggers == 0) {
-					OutputLogger.printf("\tCluster Energy Lower Bound :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_MIN), halfSimTriggers);
-					OutputLogger.printf("\tCluster Energy Upper Bound :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_MAX), halfSimTriggers);
-					OutputLogger.printf("\tCluster Hit Count          :: %d / %d%n", event.getCutFailures(triggerNum, HIT_COUNT), halfSimTriggers);
+					OutputLogger.printf("\tCluster Energy Lower Bound :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_MIN), sspTriggerCount[triggerNum]);
+					OutputLogger.printf("\tCluster Energy Upper Bound :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_MAX), sspTriggerCount[triggerNum]);
+					OutputLogger.printf("\tCluster Hit Count          :: %d / %d%n", event.getCutFailures(triggerNum, HIT_COUNT), sspTriggerCount[triggerNum]);
 				} else {
 					OutputLogger.printf("\tCluster Energy Lower Bound :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, ENERGY_MIN), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_MIN) / halfSimTriggers));
+							event.getCutFailures(triggerNum, ENERGY_MIN), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, ENERGY_MIN) / sspTriggerCount[triggerNum]));
 					OutputLogger.printf("\tCluster Energy Upper Bound :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, ENERGY_MAX), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_MAX) / halfSimTriggers));
+							event.getCutFailures(triggerNum, ENERGY_MAX), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, ENERGY_MAX) / sspTriggerCount[triggerNum]));
 					OutputLogger.printf("\tCluster Hit Count          :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, HIT_COUNT), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, HIT_COUNT) / halfSimTriggers));
+							event.getCutFailures(triggerNum, HIT_COUNT), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, HIT_COUNT) / sspTriggerCount[triggerNum]));
 				}
 				OutputLogger.printf("\tExcess Reported Triggers   :: %d%n", sspReportedExtras / 2);
 			}
 			
 			// Update the global trigger tracking variables.
-			triggerRunStats[0].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
-			triggerLocalStats[0].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerRunStats[0].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerLocalStats[0].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
 			efficiencyRunStats.addSinglesTriggers(activeTrigger, reconTriggerList);
 			efficiencyLocalStats.addSinglesTriggers(activeTrigger, reconTriggerList);
 			efficiencyRunStats.addEvent(activeTrigger, event);
@@ -1270,27 +1748,31 @@
 			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 				OutputLogger.println();
 				OutputLogger.printf("Trigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
-				if(sspSimTriggers == 0) {
-					OutputLogger.printf("\tPair Energy Sum            :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_SUM), halfSimTriggers);
-					OutputLogger.printf("\tPair Energy Difference     :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_DIFF), halfSimTriggers);
-					OutputLogger.printf("\tPair Energy Slope          :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_SLOPE), halfSimTriggers);
-					OutputLogger.printf("\tPair Coplanarity           :: %d / %d%n", event.getCutFailures(triggerNum, COPLANARITY), halfSimTriggers);
+				if(sspTriggerCount[triggerNum] == 0) {
+					OutputLogger.printf("\tPair Energy Sum            :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_SUM), sspTriggerCount[triggerNum]);
+					OutputLogger.printf("\tPair Energy Difference     :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_DIFF), sspTriggerCount[triggerNum]);
+					OutputLogger.printf("\tPair Energy Slope          :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_SLOPE), sspTriggerCount[triggerNum]);
+					OutputLogger.printf("\tPair Coplanarity           :: %d / %d%n", event.getCutFailures(triggerNum, COPLANARITY), sspTriggerCount[triggerNum]);
 				} else {
 					OutputLogger.printf("\tPair Energy Sum            :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, ENERGY_SUM), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_SUM) / halfSimTriggers));
+							event.getCutFailures(triggerNum, ENERGY_SUM), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, ENERGY_SUM) / sspTriggerCount[triggerNum]));
 					OutputLogger.printf("\tPair Energy Difference     :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, ENERGY_DIFF), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_DIFF) / halfSimTriggers));
+							event.getCutFailures(triggerNum, ENERGY_DIFF), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, ENERGY_DIFF) / sspTriggerCount[triggerNum]));
 					OutputLogger.printf("\tPair Energy Slope          :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, ENERGY_SLOPE), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_SLOPE) / halfSimTriggers));
+							event.getCutFailures(triggerNum, ENERGY_SLOPE), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, ENERGY_SLOPE) / sspTriggerCount[triggerNum]));
 					OutputLogger.printf("\tPair Coplanarity           :: %d / %d (%3.0f%%)%n",
-							event.getCutFailures(triggerNum, COPLANARITY), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, COPLANARITY) / halfSimTriggers));
+							event.getCutFailures(triggerNum, COPLANARITY), sspTriggerCount[triggerNum],
+							(100.0 * event.getCutFailures(triggerNum, COPLANARITY) / sspTriggerCount[triggerNum]));
 				}
 				OutputLogger.printf("\tExcess Reported Triggers   :: %d%n", sspReportedExtras / 2);
 			}
 			
 			// Update the global trigger tracking variables.
-			triggerRunStats[1].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
-			triggerLocalStats[1].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerRunStats[1].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerLocalStats[1].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
 			efficiencyRunStats.addPairTriggers(activeTrigger, reconTriggerList);
 			efficiencyLocalStats.addSinglesTriggers(activeTrigger, reconTriggerList);
 			efficiencyRunStats.addEvent(activeTrigger, event);
@@ -1298,9 +1780,12 @@
 		}
 		
 		// Note whether the was a trigger match failure.
-		if((event.getMatchedReconTriggers() - reconSimTriggers != 0) || (event.getMatchedSSPTriggers() - sspSimTriggers != 0)) {
+		if(event.getMatchedReconTriggers() - reconSimTriggers != 0) {
 			if(isSingles) { singlesEfficiencyFail = true; }
 			else { pairEfficiencyFail = true; }
+		} if(event.getMatchedSSPTriggers() - sspSimTriggers != 0) {
+			if(isSingles) { singlesInternalFail = true; }
+			else { pairInternalFail = true; }
 		}
 	}
 	
@@ -1312,6 +1797,7 @@
 		// Run the SSP clusters through the singles trigger to determine
 		// whether they pass it or not.
 		for(SSPCluster cluster : sspClusters) {
+			triggerLoop:
 			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 				// For a cluster to have formed it is assumed to have passed
 				// the cluster seed energy cuts. This can not be verified
@@ -1332,7 +1818,19 @@
 				trigger.setStateClusterEnergyHigh(passClusterHigh);
 				trigger.setStateHitCount(passHitCount);
 				
-				// Store the trigger.
+				// A trigger will only be reported by the SSP if it
+				// passes all of the enabled cuts for that trigger.
+				// Check whether this trigger meets these conditions.
+				if(singlesCutsEnabled[triggerNum][ENERGY_MIN] && !trigger.getStateClusterEnergyLow()) {
+					continue triggerLoop;
+				} if(singlesCutsEnabled[triggerNum][ENERGY_MAX] && !trigger.getStateClusterEnergyHigh()) {
+					continue triggerLoop;
+				} if(singlesCutsEnabled[triggerNum][HIT_COUNT] && !trigger.getStateHitCount()) {
+					continue triggerLoop;
+				}
+				
+				// If all the necessary checks passed, store the new
+				// trigger for verification.
 				sspSinglesTriggers.get(triggerNum).add(trigger);
 			}
 		}
@@ -1341,6 +1839,7 @@
 		// to determine whether they pass it or not.
 		for(Cluster cluster : reconClusters) {
 			// Simulate each of the cluster singles triggers.
+			triggerLoop:
 			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 				// For a cluster to have formed it is assumed to have passed
 				// the cluster seed energy cuts. This can not be verified
@@ -1360,6 +1859,17 @@
 				trigger.setStateClusterEnergyLow(passClusterLow);
 				trigger.setStateClusterEnergyHigh(passClusterHigh);
 				trigger.setStateHitCount(passHitCount);
+				
+				// A trigger will only be reported by the SSP if it
+				// passes all of the enabled cuts for that trigger.
+				// Check whether this trigger meets these conditions.
+				if(singlesCutsEnabled[triggerNum][ENERGY_MIN] && !trigger.getStateClusterEnergyLow()) {
+					continue triggerLoop;
+				} if(singlesCutsEnabled[triggerNum][ENERGY_MAX] && !trigger.getStateClusterEnergyHigh()) {
+					continue triggerLoop;
+				} if(singlesCutsEnabled[triggerNum][HIT_COUNT] && !trigger.getStateHitCount()) {
+					continue triggerLoop;
+				}
 				
 				// Store the trigger.
 				reconSinglesTriggers.get(triggerNum).add(trigger);
@@ -1417,13 +1927,13 @@
 		// Simulate the pair triggers and record the results.
 		for(Cluster[] reconPair : reconPairs) {
 			// Simulate each of the cluster pair triggers.
-			reconTriggerLoop:
+			pairTriggerLoop:
 			for(int triggerIndex = 0; triggerIndex < 2; triggerIndex++) {
 				// Check that the pair passes the time coincidence cut.
 				// If it does not, it is not a valid pair and should be
 				// destroyed.
 				if(!pairsTrigger[triggerIndex].pairTimeCoincidenceCut(reconPair)) {
-					continue reconTriggerLoop;
+					continue pairTriggerLoop;
 				}
 				
 				// For a cluster to have formed it is assumed to have passed
@@ -1460,6 +1970,25 @@
 				trigger.setStateCoplanarity(passPairCoplanarity);
 				trigger.setStateTimeCoincidence(passTimeCoincidence);
 				
+				// A trigger will only be reported by the SSP if it
+				// passes all of the enabled cuts for that trigger.
+				// Check whether this trigger meets these conditions.
+				if(pairCutsEnabled[triggerIndex][ENERGY_MIN] && !trigger.getStateClusterEnergyLow()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][ENERGY_MAX] && !trigger.getStateClusterEnergyHigh()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][HIT_COUNT] && !trigger.getStateHitCount()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + ENERGY_SUM] && !trigger.getStateEnergySum()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + ENERGY_DIFF] && !trigger.getStateEnergyDifference()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + ENERGY_SLOPE] && !trigger.getStateEnergySlope()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + COPLANARITY] && !trigger.getStateCoplanarity()) {
+					continue pairTriggerLoop;
+				}
+				
 				// Add the trigger to the list.
 				reconPairsTriggers.get(triggerIndex).add(trigger);
 			}
@@ -1509,6 +2038,25 @@
 				trigger.setStateCoplanarity(passPairCoplanarity);
 				trigger.setStateTimeCoincidence(passTimeCoincidence);
 				
+				// A trigger will only be reported by the SSP if it
+				// passes all of the enabled cuts for that trigger.
+				// Check whether this trigger meets these conditions.
+				if(pairCutsEnabled[triggerIndex][ENERGY_MIN] && !trigger.getStateClusterEnergyLow()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][ENERGY_MAX] && !trigger.getStateClusterEnergyHigh()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][HIT_COUNT] && !trigger.getStateHitCount()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + ENERGY_SUM] && !trigger.getStateEnergySum()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + ENERGY_DIFF] && !trigger.getStateEnergyDifference()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + ENERGY_SLOPE] && !trigger.getStateEnergySlope()) {
+					continue pairTriggerLoop;
+				} if(pairCutsEnabled[triggerIndex][3 + COPLANARITY] && !trigger.getStateCoplanarity()) {
+					continue pairTriggerLoop;
+				}
+				
 				// Add the trigger to the list.
 				sspPairsTriggers.get(triggerIndex).add(trigger);
 			}
@@ -1524,8 +2072,9 @@
 	private void logSettings() {
 		// Output general settings.
 		System.out.println("Cluster Verification Settings");
-		System.out.printf("\tEnergy Threshold       :: %1.2f%%%n", energyAcceptance);
-		System.out.printf("\tHit Threshold          :: %1d%n", hitAcceptance);
+		System.out.printf("\tHit Threshold          :: %1d hit(s)%n", hitAcceptance);
+		System.out.printf("\tEnergy Threshold       :: %5.3f GeV%n",  energyAcceptance);
+		System.out.println();
 		
 		// Output window settings.
 		System.out.println("FADC Timing Window Settings");
@@ -1544,28 +2093,45 @@
 			System.out.println("\tCluster verification will not be performed!");
 			performClusterVerification = false;
 		}
+		System.out.println();
 		
 		// Output the singles trigger settings.
 		for(int i = 0; i < 2; i++) {
-			System.out.printf("Singles Trigger %d Settings%n", (i + 1));
-			System.out.printf("\tCluster Energy Low     :: %.3f GeV%n", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW));
-			System.out.printf("\tCluster Energy High    :: %.3f GeV%n", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH));
-			System.out.printf("\tCluster Hit Count      :: %.0f hits%n", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW));
+			// Print the settings.
+			System.out.printf("Singles Trigger %d Settings%23s[%5b]%n", (i + 1), "", singlesTriggerEnabled[i]);
+			System.out.printf("\tCluster Energy Low     :: %.3f GeV      [%5b]%n",
+					singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW), singlesCutsEnabled[i][0]);
+			System.out.printf("\tCluster Energy High    :: %.3f GeV      [%5b]%n",
+					singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH), singlesCutsEnabled[i][1]);
+			System.out.printf("\tCluster Hit Count      :: %.0f hit(s)       [%5b]%n",
+					singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW), singlesCutsEnabled[i][2]);
+			System.out.println();
 		}
 		
 		// Output the pair trigger settings.
 		for(int i = 0; i < 2; i++) {
-			System.out.printf("Pairs Trigger %d Settings%n", (i + 1));
-			System.out.printf("\tCluster Energy Low     :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW));
-			System.out.printf("\tCluster Energy High    :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH));
-			System.out.printf("\tCluster Hit Count      :: %.0f hits%n", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW));
-			System.out.printf("\tPair Energy Sum Low    :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW));
-			System.out.printf("\tPair Energy Sum Low    :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH));
-			System.out.printf("\tPair Energy Difference :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH));
-			System.out.printf("\tPair Energy Slope      :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW));
-			System.out.printf("\tPair Energy Slope F    :: %.3f GeV / mm%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F));
-			System.out.printf("\tPair Coplanarity       :: %.0f Degrees%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_COPLANARITY_HIGH));
-			System.out.printf("\tPair Time Coincidence  :: %.0f ns%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_TIME_COINCIDENCE));
+			System.out.printf("Pairs Trigger %d Settings%25s[%5b]%n", (i + 1), "", pairTriggerEnabled[i]);
+			System.out.printf("\tCluster Energy Low     :: %.3f GeV      [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW), pairCutsEnabled[i][0]);
+			System.out.printf("\tCluster Energy High    :: %.3f GeV      [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH), pairCutsEnabled[i][1]);
+			System.out.printf("\tCluster Hit Count      :: %.0f hit(s)       [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW), pairCutsEnabled[i][2]);
+			System.out.printf("\tPair Energy Sum Low    :: %.3f GeV      [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW), pairCutsEnabled[i][3]);
+			System.out.printf("\tPair Energy Sum High   :: %.3f GeV      [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH), pairCutsEnabled[i][3]);
+			System.out.printf("\tPair Energy Difference :: %.3f GeV      [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH), pairCutsEnabled[i][4]);
+			System.out.printf("\tPair Energy Slope      :: %.3f GeV      [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW), pairCutsEnabled[i][5]);
+			System.out.printf("\tPair Energy Slope F    :: %.4f GeV / mm%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F));
+			System.out.printf("\tPair Coplanarity       :: %3.0f Degrees    [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_COPLANARITY_HIGH), pairCutsEnabled[i][6]);
+			System.out.printf("\tPair Time Coincidence  :: %2.0f ns          [%5b]%n",
+					pairsTrigger[i].getCutValue(TriggerModule.PAIR_TIME_COINCIDENCE), true);
+			System.out.println();
 		}
 	}
 	
@@ -1578,122 +2144,6 @@
 	 */
 	private final boolean isVerifiable(Cluster reconCluster) {
 		return TriggerDiagnosticUtil.isVerifiable(reconCluster, nsa, nsb, windowWidth);
-	}
-	
-	/**
-	 * Compares an SSP trigger with a simulated trigger. Note that only
-	 * certain class combinations are supported. Triggers of the type
-	 * <code>SSPSinglesTrigger</code> may be compared with triggers of
-	 * the type <code>SinglesTrigger<SSPCluster></code> and triggers of
-	 * the type <code>SSPPairTrigger</code> may be compared to either
-	 * <code>PairTrigger<SSPCluster[]></code> triggers objects.
-	 * @param bankTrigger - The SSP bank trigger.
-	 * @param simTrigger - The simulated trigger.
-	 * @return Returns <code>true</code> if the triggers are valid
-	 * matches and <code>false</code> if they are not.
-	 * @throws IllegalArgumentException Occurs if the trigger types
-	 * are not of a supported type.
-	 */
-	@SuppressWarnings("unchecked")
-	private static final boolean compareTriggers(SSPNumberedTrigger bankTrigger, Trigger<?> simTrigger) throws IllegalArgumentException {
-		// Get the classes of the arguments. This is used to check the
-		// generic type of the Trigger<?> object, and means that the
-		// "unchecked" warnings can be safely ignored.
-		Object source = simTrigger.getTriggerSource();
-		
-		// If the combination of classes is supported, pass the triggers
-		// to the appropriate handler.
-		if(bankTrigger instanceof SSPSinglesTrigger && simTrigger instanceof SinglesTrigger && source instanceof SSPCluster) {
-			return compareSSPSinglesTriggers((SSPSinglesTrigger) bankTrigger, (SinglesTrigger<SSPCluster>) simTrigger);
-		} else if(bankTrigger instanceof SSPPairTrigger && simTrigger instanceof PairTrigger && source instanceof SSPCluster[]) {
-			return compareSSPPairTriggers((SSPPairTrigger) bankTrigger, (PairTrigger<SSPCluster[]>) simTrigger);
-		}
-		
-		// Otherwise, the trigger combination is not supported. Produce
-		// and exception.
-		throw new IllegalArgumentException(String.format("Trigger type \"%s\" can not be compared to trigger type \"%s\" with source type \"%s\".",
-				bankTrigger.getClass().getSimpleName(), simTrigger.getClass().getSimpleName(), source.getClass().getSimpleName()));
-	}
-	
-	/**
-	 * Compares a trigger from the SSP bank to a trigger simulated on
-	 * an SSP cluster.
-	 * @param bankTrigger - The trigger from the SSP bank.
-	 * @param simTrigger - The trigger from the simulation.
-	 * @return Returns <code>true</code> if the triggers match and
-	 * <code>false</code> if they do not.
-	 */
-	private static final boolean compareSSPSinglesTriggers(SSPSinglesTrigger bankTrigger, SinglesTrigger<SSPCluster> simTrigger) {
-		// The bank trigger and simulated trigger must have the same
-		// time. This is equivalent to the time of the triggering cluster.
-		if(bankTrigger.getTime() != simTrigger.getTriggerSource().getTime()) {
-			return false;
-		}
-		
-		// If the time stamp is the same, check that the trigger flags
-		// are all the same. Start with cluster energy low.
-		if(bankTrigger.passCutEnergyMin() != simTrigger.getStateClusterEnergyLow()) {
-			return false;
-		}
-		
-		// Check cluster energy high.
-		if(bankTrigger.passCutEnergyMax() != simTrigger.getStateClusterEnergyHigh()) {
-			return false;
-		}
-		
-		// Check cluster hit count.
-		if(bankTrigger.passCutHitCount() != simTrigger.getStateHitCount()) {
-			return false;
-		}
-		
-		// If all of the tests are successful, the triggers match.
-		return true;
-	}
-	
-	/**
-	 * Compares a trigger from the SSP bank to a trigger simulated on
-	 * an SSP cluster.
-	 * @param bankTrigger - The trigger from the SSP bank.
-	 * @param simTrigger - The trigger from the simulation.
-	 * @return Returns <code>true</code> if the triggers match and
-	 * <code>false</code> if they do not.
-	 */
-	private static final boolean compareSSPPairTriggers(SSPPairTrigger bankTrigger, PairTrigger<SSPCluster[]> simTrigger) {
-		// Get the time of the bottom cluster in the pair.
-		int simTime = 0;
-		if(simTrigger.getTriggerSource()[0].getYIndex() < 0) {
-			simTime = simTrigger.getTriggerSource()[0].getTime();
-		} else {
-			simTime = simTrigger.getTriggerSource()[1].getTime();
-		}
-		
-		// The bank trigger and simulated trigger must have the same
-		// time. This is equivalent to the time of the triggering cluster.
-		if(bankTrigger.getTime() != simTime) { return false; }
-		
-		// If the time stamp is the same, check that the trigger flags
-		// are all the same. Start with energy sum.
-		if(bankTrigger.passCutEnergySum() != simTrigger.getStateEnergySum()) {
-			return false;
-		}
-		
-		// Check pair energy difference.
-		if(bankTrigger.passCutEnergyDifference() != simTrigger.getStateEnergyDifference()) {
-			return false;
-		}
-		
-		// Check pair energy slope.
-		if(bankTrigger.passCutEnergySlope() != simTrigger.getStateEnergySlope()) {
-			return false;
-		}
-		
-		// Check pair coplanarity.
-		if(bankTrigger.passCutCoplanarity() != simTrigger.getStateCoplanarity()) {
-			return false;
-		}
-		
-		// If all of the tests are successful, the triggers match.
-		return true;
 	}
 	
 	/**

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java	Wed Mar 25 14:43:27 2015
@@ -3,7 +3,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.hps.readout.ecal.triggerbank.SSPCluster;
+import org.hps.recon.ecal.triggerbank.SSPCluster;
 import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
 import org.lcsim.event.Cluster;
 
@@ -21,6 +21,7 @@
 	private int failPosition = 0;
 	private int failEnergy = 0;
 	private int failHitCount = 0;
+	private int failTime = 0;
 	
 	// Store all of the pairs.
 	private List<ClusterMatchedPair> pairList = new ArrayList<ClusterMatchedPair>();
@@ -46,6 +47,7 @@
 			if(cmp.isPositionFailState()) { failPosition++; }
 			if(cmp.isEnergyFailState()) { failEnergy++; }
 			if(cmp.isHitCountFailState()) { failHitCount++; }
+			if(cmp.isTimeFailState()) { failTime++; }
 		}
 	}
 	
@@ -100,6 +102,26 @@
 	}
 	
 	/**
+	 * Gets the number of cluster pairs stored in this event that are
+	 * marked with time fail states.
+	 * @return Returns the number of instances of this state as an
+	 * <code>int</code> primitive.
+	 */
+	public int getTimeFailures() {
+		return failTime;
+	}
+	
+	/**
+	 * Indicates whether at least one cluster pair in the event created
+	 * a fail state.
+	 * @return Returns <code>true</code> if not all clusters matched and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean isFailState() {
+		return (failEnergy > 0) || (failHitCount > 0) || (failTime > 0) || (failPosition > 0);
+	}
+	
+	/**
 	 * Adds a reconstructed/SSP cluster pair and marks it as having an
 	 * energy fail state.
 	 * @param reconCluster - The reconstructed cluster.
@@ -134,6 +156,17 @@
 	
 	/**
 	 * Adds a reconstructed/SSP cluster pair and marks it as having a
+	 * time fail state.
+	 * @param reconCluster - The reconstructed cluster.
+	 * @param sspCluster - The SSP cluster.
+	 */
+	public void pairFailTime(Cluster reconCluster, SSPCluster sspCluster) {
+		failTime++;
+		pairList.add(new ClusterMatchedPair(reconCluster, sspCluster, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_TIME));
+	}
+	
+	/**
+	 * Adds a reconstructed/SSP cluster pair and marks it as having a
 	 * match state.
 	 * @param reconCluster - The reconstructed cluster.
 	 * @param sspCluster - The SSP cluster.

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java	Wed Mar 25 14:43:27 2015
@@ -1,12 +1,8 @@
 package org.hps.analysis.trigger.event;
 
-import java.awt.Point;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
-import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
-import org.hps.readout.ecal.triggerbank.SSPCluster;
+import org.hps.recon.ecal.triggerbank.SSPCluster;
 import org.lcsim.event.Cluster;
 
 /**
@@ -16,27 +12,6 @@
  * @author Kyle McCarty
  */
 public class ClusterMatchStatus extends ClusterStatModule {
-	// Plot binning values.
-	private static final int TIME_BIN = 4;
-	private static final double ENERGY_BIN = 0.01;
-	private static final int TIME_BIN_HALF = TIME_BIN / 2;
-	private static final double ENERGY_BIN_HALF = ENERGY_BIN / 2;
-	
-	// Track plotting values for reconstructed and SSP clusters.
-	private Map<Integer, Integer> sspHitCountBins = new HashMap<Integer, Integer>();
-	private Map<Integer, Integer> reconHitCountBins = new HashMap<Integer, Integer>();
-	private Map<Point, Integer> sspPositionBins = new HashMap<Point, Integer>();
-	private Map<Point, Integer> reconPositionBins = new HashMap<Point, Integer>();
-	private Map<Integer, Integer> sspEnergyBins = new HashMap<Integer, Integer>();
-	private Map<Integer, Integer> reconEnergyBins = new HashMap<Integer, Integer>();
-	
-	// Track plotting values for cluster matching results.
-	private Map<Point, Integer> failPositionBins = new HashMap<Point, Integer>();
-	private Map<Point, Integer> allHenergyBins = new HashMap<Point, Integer>();
-	private Map<Point, Integer> failHenergyBins = new HashMap<Point, Integer>();
-	private Map<Integer, Integer> allTimeBins = new HashMap<Integer, Integer>();
-	private Map<Integer, Integer> failTimeBins = new HashMap<Integer, Integer>();
-	
     public void addEvent(ClusterMatchEvent event, List<Cluster> reconClusters, List<SSPCluster> sspClusters) {
     	// Update the number of reconstructed and SSP clusters
 		// that have been seen so far.
@@ -57,91 +32,6 @@
 		if(sspClusters == null || sspClusters.isEmpty()) {
 			failPosition += (reconClusters == null ? 0 : reconClusters.size());
 		}
-    	
-    	// Update the plotting information for reconstructed clusters.
-		for(Cluster cluster : reconClusters) {
-			// Update the hit count bin data.
-			Integer hitCountCount = reconHitCountBins.get(cluster.getCalorimeterHits().size());
-			if(hitCountCount == null) { reconHitCountBins.put(cluster.getCalorimeterHits().size(), 1); }
-			else { reconHitCountBins.put(cluster.getCalorimeterHits().size(), hitCountCount + 1); }
-			
-			// Update the position bin data.
-			Point clusterPosition = TriggerDiagnosticUtil.getClusterPosition(cluster);
-			Integer positionCount = reconPositionBins.get(clusterPosition);
-			if(positionCount == null) { reconPositionBins.put(clusterPosition, 1); }
-			else { reconPositionBins.put(clusterPosition, positionCount + 1); }
-			
-			// Update the energy bin data.
-			int energyBin = (int) Math.floor(cluster.getEnergy() / ENERGY_BIN);
-			Integer energyCount = reconEnergyBins.get(energyBin);
-			if(energyCount == null) { reconEnergyBins.put(energyBin, 1); }
-			else { reconEnergyBins.put(energyBin, energyCount + 1); }
-		}
-		
-    	// Update the plotting information for SSP clusters.
-		for(SSPCluster cluster : sspClusters) {
-			// Update the hit count bin data.
-			Integer hitCountCount = sspHitCountBins.get(cluster.getHitCount());
-			if(hitCountCount == null) { sspHitCountBins.put(cluster.getHitCount(), 1); }
-			else { sspHitCountBins.put(cluster.getHitCount(), hitCountCount + 1); }
-			
-			// Update the position bin data.
-			Point clusterPosition = TriggerDiagnosticUtil.getClusterPosition(cluster);
-			Integer positionCount = sspPositionBins.get(clusterPosition);
-			if(positionCount == null) { sspPositionBins.put(clusterPosition, 1); }
-			else { sspPositionBins.put(clusterPosition, positionCount + 1); }
-			
-			// Update the energy bin data.
-			int energyBin = (int) Math.floor(cluster.getEnergy() / ENERGY_BIN);
-			Integer energyCount = sspEnergyBins.get(energyBin);
-			if(energyCount == null) { sspEnergyBins.put(energyBin, 1); }
-			else { sspEnergyBins.put(energyBin, energyCount + 1); }
-		}
-		
-		// Update the plotting information for SSP/reconstructed cluster
-		// pairs.
-		pairLoop:
-		for(ClusterMatchedPair pair : event.getMatchedPairs()) {
-			// If one of the pairs is null, then it is unmatched cluster
-			// and may be skipped.
-			if(pair.getReconstructedCluster() == null || pair.getSSPCluster() == null) {
-				continue pairLoop;
-			}
-			
-			// Populate the bins for the "all" plots.
-			// Update the match time plots.
-			int timeBin = (int) Math.floor(TriggerDiagnosticUtil.getClusterTime(pair.getReconstructedCluster()) / TIME_BIN);
-			Integer timeCount = allTimeBins.get(timeBin);
-			if(timeCount == null) { allTimeBins.put(timeBin, 1); }
-			else { allTimeBins.put(timeBin, timeCount + 1); }
-			
-			// Update the energy/hit difference plots.
-			int hitBin = getHitCountDifference(pair.getSSPCluster(), pair.getReconstructedCluster());
-			int energyBin = (int) Math.floor(getEnergyPercentDifference(pair.getSSPCluster(), pair.getReconstructedCluster()) / ENERGY_BIN);
-			Point henergyBin = new Point(hitBin, energyBin);
-			Integer henergyCount = allHenergyBins.get(henergyBin);
-			if(henergyCount == null) { allHenergyBins.put(henergyBin, 1); }
-			else { allHenergyBins.put(henergyBin, henergyCount + 1); }
-			
-			// Populate the bins for the "fail" plots.
-			if(!pair.isMatch()) {
-				// Update the failed cluster position bins.
-				Point clusterPosition = TriggerDiagnosticUtil.getClusterPosition(pair.getReconstructedCluster());
-				Integer positionCount = failPositionBins.get(clusterPosition);
-				if(positionCount == null) { failPositionBins.put(clusterPosition, 1); }
-				else { failPositionBins.put(clusterPosition, positionCount + 1); }
-				
-				// Update the failed match time plots.
-				timeCount = failTimeBins.get(timeBin);
-				if(timeCount == null) { failTimeBins.put(timeBin, 1); }
-				else { failTimeBins.put(timeBin, timeCount + 1); }
-				
-				// Update the failed energy/hit difference plots.
-				henergyCount = failHenergyBins.get(henergyBin);
-				if(henergyCount == null) { failHenergyBins.put(henergyBin, 1); }
-				else { failHenergyBins.put(henergyBin, henergyCount + 1); }
-			}
-		}
     }
     
 	/**
@@ -150,21 +40,7 @@
 	 */
     @Override
 	public void clear() {
-		// Clear statistical data.
 		super.clear();
-		
-		// Clear plot collections.
-		sspHitCountBins.clear();
-		reconHitCountBins.clear();
-		sspPositionBins.clear();
-		reconPositionBins.clear();
-		sspEnergyBins.clear();
-		reconEnergyBins.clear();
-		failPositionBins.clear();
-		allHenergyBins.clear();
-		failHenergyBins.clear();
-		allTimeBins.clear();
-		failTimeBins.clear();
 	}
     
     /**
@@ -175,26 +51,4 @@
     public ClusterStatModule cloneStatModule() {
     	return new ClusterStatModule(this);
     }
-	
-	/**
-	 * Solves the equation <code>|E_ssp / E_recon|</code>.
-	 * @param sspCluster - The SSP cluster.
-	 * @param reconCluster - The reconstructed cluster.
-	 * @return Returns the solution to the equation as a <code>double
-	 * </code> primitive.
-	 */
-	private static final double getEnergyPercentDifference(SSPCluster sspCluster, Cluster reconCluster) {
-		return Math.abs((sspCluster.getEnergy() / reconCluster.getEnergy()));
-	}
-    
-	/**
-	 * Gets the difference in hit count between an SSP cluster and a
-	 * reconstructed cluster.
-	 * @param sspCluster - The SSP cluster.
-	 * @param reconCluster - The reconstructed cluster.
-	 * @return Returns the difference as an <code>int</code> primitive.
-	 */
-	private static final int getHitCountDifference(SSPCluster sspCluster, Cluster reconCluster) {
-		return sspCluster.getHitCount() - TriggerDiagnosticUtil.getHitCount(reconCluster);
-	}
 }

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java	Wed Mar 25 14:43:27 2015
@@ -2,7 +2,7 @@
 
 import org.hps.analysis.trigger.util.Pair;
 import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
-import org.hps.readout.ecal.triggerbank.SSPCluster;
+import org.hps.recon.ecal.triggerbank.SSPCluster;
 import org.lcsim.event.Cluster;
 
 /**
@@ -97,13 +97,23 @@
 	}
 	
 	/**
-	 * Indicates whether the recon/SSP pair failed to match due to a
-	 * the cluster positions not aligning.
+	 * Indicates whether the recon/SSP pair failed to match due to the
+	 * cluster positions not aligning.
 	 * @return Returns <code>true</code> if the pair match state is a
 	 * position fail state and <code>false</code> otherwise.
 	 */
 	public boolean isPositionFailState() {
 		return (state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_POSITION);
+	}
+	
+	/**
+	 * Indicates whether the recon/SSP pair failed to match due to the
+	 * cluster time-stamps not aligning.
+	 * @return Returns <code>true</code> if the pair match state is a
+	 * time fail state and <code>false</code> otherwise.
+	 */
+	public boolean isTimeFailState() {
+		return (state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_TIME);
 	}
 	
 	/**

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java	Wed Mar 25 14:43:27 2015
@@ -6,7 +6,7 @@
 import org.hps.analysis.trigger.util.Pair;
 import org.hps.analysis.trigger.util.PairTrigger;
 import org.hps.analysis.trigger.util.SinglesTrigger;
-import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger;
+import org.hps.recon.ecal.triggerbank.SSPNumberedTrigger;
 import org.hps.analysis.trigger.util.Trigger;
 import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
 

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java	Wed Mar 25 14:43:27 2015
@@ -5,7 +5,7 @@
 
 import org.hps.analysis.trigger.util.Pair;
 import org.hps.analysis.trigger.util.Trigger;
-import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger;
+import org.hps.recon.ecal.triggerbank.SSPNumberedTrigger;
 
 /**
  * Tracks trigger pairs that were matched within an event. This can also
@@ -19,10 +19,14 @@
 	private int[] sspInternalMatched = new int[2];
 	private int[] reconTriggersMatched = new int[2];
 	private int[][] triggerComp = new int[4][2];
+	private int[] unmatchedTriggers = new int[2];
 	
 	// Track the matched trigger pairs.
 	private List<TriggerMatchedPair> sspPairList = new ArrayList<TriggerMatchedPair>();
 	private List<Pair<Trigger<?>, SSPNumberedTrigger>> reconPairList = new ArrayList<Pair<Trigger<?>, SSPNumberedTrigger>>();
+	
+	// Track whether the event triggering type was seen.
+	private boolean sawEventType = false;
 	
 	/**
 	 * Gets the number of times a cut of the given cut ID failed when
@@ -34,7 +38,7 @@
 	 */
 	public int getCutFailures(int triggerNumber, int cutID) {
 		// Validate the arguments.
-		if(triggerNumber !=0 && triggerNumber != 1) {
+		if(triggerNumber != 0 && triggerNumber != 1) {
 			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
 		} if(cutID < 0 || cutID > 3) {
 			throw new IndexOutOfBoundsException(String.format("Cut ID \"%d\" is not valid.", cutID));
@@ -45,12 +49,29 @@
 	}
 	
 	/**
+	 * Gets the number of triggers for this trigger for which there
+	 * were no matches.
+	 * @param triggerNum - The trigger for which to get the value.
+	 * @return Returns the number of triggers that failed to match as
+	 * an <code>int</code>.
+	 */
+	public int getUnmatchedTriggers(int triggerNum) {
+		// Validate the arguments.
+		if(triggerNum != 0 && triggerNum != 1) {
+			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
+		}
+		
+		// Return the requested cut value.
+		return unmatchedTriggers[triggerNum];
+	}
+	
+	/**
 	 * Gets the number of reconstructed cluster triggers that were
 	 * matched successfully.
 	 * @return Returns the value as an <code>int</code> primitive.
 	 */
 	public int getMatchedReconTriggers() {
-		return reconPairList.size();
+		return reconTriggersMatched[0] + reconTriggersMatched[1];
 	}
 	
 	/**
@@ -61,7 +82,7 @@
 	 */
 	public int getMatchedReconTriggers(int triggerNumber) {
 		// Validate the arguments.
-		if(triggerNumber !=0 && triggerNumber != 1) {
+		if(triggerNumber != 0 && triggerNumber != 1) {
 			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
 		}
 		
@@ -75,7 +96,7 @@
 	 * @return Returns the value as an <code>int</code> primitive.
 	 */
 	public int getMatchedSSPTriggers() {
-		return sspPairList.size();
+		return sspInternalMatched[0] + sspInternalMatched[1];
 	}
 	
 	/**
@@ -86,7 +107,7 @@
 	 */
 	public int getMatchedSSPTriggers(int triggerNumber) {
 		// Validate the arguments.
-		if(triggerNumber !=0 && triggerNumber != 1) {
+		if(triggerNumber != 0 && triggerNumber != 1) {
 			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
 		}
 		
@@ -122,12 +143,8 @@
 	 * @param sspTrigger - The SSP bank trigger.
 	 */
 	public void matchedSSPPair(Trigger<?> simTrigger, SSPNumberedTrigger sspTrigger) {
-		// A null SSP trigger means that no match was found. This is
-		// treated as a failure due to time, which is not tracked.
-		if(sspTrigger != null) {
-			sspInternalMatched[sspTrigger.isFirstTrigger() ? 0 : 1]++;
-			sspPairList.add(new TriggerMatchedPair(simTrigger, sspTrigger, new boolean[] { true, true, true, true }));
-		}
+		sspInternalMatched[sspTrigger.isFirstTrigger() ? 0 : 1]++;
+		sspPairList.add(new TriggerMatchedPair(simTrigger, sspTrigger, new boolean[] { true, true, true, true }));
 	}
 	
 	/**
@@ -139,28 +156,31 @@
 	 * which did not.
 	 */
 	public void matchedSSPPair(Trigger<?> simTrigger, SSPNumberedTrigger sspTrigger, boolean[] cutsMatched) {
-		// Store the full cut array.
-		boolean[] cutArray = cutsMatched;
-		
-		// If the array is size 3, it is the a singles trigger. Update
-		// it to an array of size 4 for compatibility.
-		if(cutsMatched.length == 3) {
-			boolean[] tempArray = { true, true, true, true };
-			for(int cutIndex = 0; cutIndex < cutsMatched.length; cutIndex++) {
-				tempArray[cutIndex] = cutsMatched[cutIndex];
-			}
-			cutArray = tempArray;
+		// The cut values must be stored in an array of size four, but
+		// singles triggers use arrays of size 3. If the array is not
+		// of the appropriate size, resize it.
+		boolean[] cutArray;
+		if(cutsMatched == null) {
+			cutArray = new boolean[] { false, false, false, false };
+		} else if(cutsMatched.length == 4) {
+			cutArray = cutsMatched;
+		} else {
+			cutArray = new boolean[] { cutsMatched[0], cutsMatched[1], cutsMatched[2], true };
 		}
 		
 		// Add the trigger pair to the list.
 		TriggerMatchedPair triggerPair = new TriggerMatchedPair(simTrigger, sspTrigger, cutArray);
 		sspPairList.add(triggerPair);
 		
+		// If the argument cut array was null, then this trigger was
+		// not actually matched. Track this.
+		int triggerNum = triggerPair.isFirstTrigger() ? 0 : 1;
+		if(cutsMatched == null) { unmatchedTriggers[triggerNum]++; }
+		
 		// Track which cuts have failed.
 		boolean isMatched = true;
-		int triggerNum = triggerPair.isFirstTrigger() ? 0 : 1;
-		for(int cutIndex = 0; cutIndex < cutsMatched.length; cutIndex++) {
-			if(!cutsMatched[cutIndex]) {
+		for(int cutIndex = 0; cutIndex < cutArray.length; cutIndex++) {
+			if(!cutArray[cutIndex]) {
 				triggerComp[cutIndex][triggerNum]++;
 				isMatched = false;
 			}
@@ -169,4 +189,24 @@
 		// If all the cuts are true, then the trigger pair is a match.
 		if(isMatched) { sspInternalMatched[triggerNum]++; }
 	}
+	
+	/**
+	 * Indicates whether an event of the type that caused the event
+	 * readout was seen.
+	 * @return Returns <code>true</code> if an event was seen and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean sawEventType() {
+		return sawEventType;
+	}
+	
+	/**
+	 * Sets whether a simulated trigger of the type that caused the
+	 * event readout was seen.
+	 * @param state - <code>true</code> indicates that the trigger type
+	 * was seen and <code>false</code> that it was not.
+	 */
+	public void setSawEventType(boolean state) {
+		sawEventType = state;
+	}
 }

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java	Wed Mar 25 14:43:27 2015
@@ -3,7 +3,8 @@
 import java.util.List;
 
 import org.hps.analysis.trigger.util.Trigger;
-import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger;
+import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
+import org.hps.recon.ecal.triggerbank.SSPNumberedTrigger;
 
 /**
  * Tracks the trigger diagnostic statistics for trigger matching.
@@ -11,6 +12,10 @@
  * @author Kyle McCarty <[log in to unmask]>
  */
 public class TriggerMatchStatus extends TriggerStatModule {
+	// Track the number of triggers seen.
+	private int[] sspTriggersSeen = new int[2];
+	private int[] reconTriggersSeen = new int[2];
+	
 	/**
 	 * Adds the statistical data stored in a trigger comparison event
 	 * into this status tracking module.
@@ -19,8 +24,15 @@
 	 * @param sspSimTriggers - A list of simulated SSP cluster triggers.
 	 * @param sspBankTriggers - A list of SSP bank triggers.
 	 */
-	public void addEvent(TriggerMatchEvent event, List<List<? extends Trigger<?>>> reconTriggers,
+	public void addEvent(int eventType, TriggerMatchEvent event, List<List<? extends Trigger<?>>> reconTriggers,
 			List<List<? extends Trigger<?>>> sspSimTriggers, List<? extends SSPNumberedTrigger> sspBankTriggers) {
+		// Increment the event type count.
+		if(eventType == TriggerDiagnosticUtil.TRIGGER_SINGLES_1 || eventType == TriggerDiagnosticUtil.TRIGGER_PAIR_1) {
+			triggerTypesSeen[0]++;
+		} else if(eventType == TriggerDiagnosticUtil.TRIGGER_SINGLES_2 || eventType == TriggerDiagnosticUtil.TRIGGER_PAIR_2) {
+			triggerTypesSeen[1]++;
+		}
+		
 		// Check if there are more bank triggers than there are
 		// simulated SSP triggers.
 		int sspTriggerDiff = sspBankTriggers.size() - sspSimTriggers.size();
@@ -34,8 +46,16 @@
 		this.reconTriggers += reconTriggers.get(0).size() + reconTriggers.get(1).size();
 		reportedTriggers += sspBankTriggers.size();
 		
+		// Fill the specific trigger counters.
+		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			sspTriggersSeen[triggerNum] += sspSimTriggers.get(triggerNum).size();
+			reconTriggersSeen[triggerNum] += reconTriggers.get(triggerNum).size();
+		}
+		
 		// Increment the count for each cut failure type.
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			unmatchedTriggers[triggerNum] += event.getUnmatchedTriggers(triggerNum);
+			
 			for(int cutIndex = 0; cutIndex < 4; cutIndex++) {
 				triggerComp[cutIndex][triggerNum] += event.getCutFailures(triggerNum, cutIndex);
 			}
@@ -45,6 +65,16 @@
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 			sspInternalMatched[triggerNum] += event.getMatchedSSPTriggers(triggerNum);
 			reconTriggersMatched[triggerNum] += event.getMatchedReconTriggers(triggerNum);
+		}
+		
+		// Check if a trigger of the right time was found.
+		// Get the trigger number and type.
+		if(event.sawEventType()) {
+			if(eventType == TriggerDiagnosticUtil.TRIGGER_SINGLES_1 || eventType == TriggerDiagnosticUtil.TRIGGER_PAIR_1) {
+				triggerTypesFound[0]++;
+			} else if(eventType == TriggerDiagnosticUtil.TRIGGER_SINGLES_2 || eventType == TriggerDiagnosticUtil.TRIGGER_PAIR_2) {
+				triggerTypesFound[1]++;
+			}
 		}
 	}
 	
@@ -62,4 +92,52 @@
 	public TriggerStatModule cloneStatModule() {
     	return new TriggerStatModule(this);
     }
+	
+	/**
+	 * Gets the number of reconstructed triggers seen for a specific
+	 * trigger number.
+	 * @param triggerNum - The trigger number.
+	 * @return Returns the total number of reconstructed triggers seen
+	 * by the indicated trigger number. 
+	 */
+	public int getTotalReconTriggers(int triggerNum) {
+		return getSSPTriggerCount(false, triggerNum);
+	}
+	
+	/**
+	 * Gets the number of SSP bank triggers seen for a specific trigger
+	 * number.
+	 * @param triggerNum - The trigger number.
+	 * @return Returns the total number of SSP bank triggers seen by
+	 * the indicated trigger number. 
+	 */
+	public int getTotalSSPTriggers(int triggerNum) {
+		return getSSPTriggerCount(true, triggerNum);
+	}
+	
+	/**
+	 * Gets the total number of triggers seen for a specific trigger
+	 * source type and number.
+	 * @param isSSP - Whether the trigger source is SSP bank clusters.
+	 * @param triggerNum - The trigger number.
+	 * @return Returns the trigger count.
+	 */
+	private final int getSSPTriggerCount(boolean isSSP, int triggerNum) {
+		// Make sure the trigger number is valid.
+		validateTriggerNumber(triggerNum);
+		
+		// Return the triggers.
+		if(isSSP) { return sspTriggersSeen[triggerNum]; }
+		else { return reconTriggersSeen[triggerNum]; }
+	}
+	
+	/**
+	 * Produces an exception if the trigger number argument is invalid.
+	 * @param triggerNum - The trigger number to validate.
+	 */
+	private static final void validateTriggerNumber(int triggerNum) {
+		if(triggerNum < 0 || triggerNum > 2) {
+			throw new IndexOutOfBoundsException(String.format("Trigger number \"%d\" is invalid", triggerNum));
+		}
+	}
 }

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java	Wed Mar 25 14:43:27 2015
@@ -4,9 +4,9 @@
 import org.hps.analysis.trigger.util.PairTrigger;
 import org.hps.analysis.trigger.util.SinglesTrigger;
 import org.hps.analysis.trigger.util.Trigger;
-import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger;
-import org.hps.readout.ecal.triggerbank.SSPPairTrigger;
-import org.hps.readout.ecal.triggerbank.SSPSinglesTrigger;
+import org.hps.recon.ecal.triggerbank.SSPNumberedTrigger;
+import org.hps.recon.ecal.triggerbank.SSPPairTrigger;
+import org.hps.recon.ecal.triggerbank.SSPSinglesTrigger;
 
 /**
  * Stores a pair of one simulated trigger and one SSP bank trigger that
@@ -80,7 +80,11 @@
 	 * object.
 	 */
 	public Class<?> getSimulatedTriggerType() {
-		return getFirstElement().getTriggerSource().getClass();
+		if(getFirstElement() != null) {
+			return getFirstElement().getTriggerSource().getClass();
+		} else {
+			return null;
+		}
 	}
 	
 	/**
@@ -99,7 +103,11 @@
 	 * first trigger and <code>false</code> otherwise.
 	 */
 	public boolean isFirstTrigger() {
-		return getSecondElement().isFirstTrigger();
+		if(getSecondElement() != null) {
+			return getSecondElement().isFirstTrigger();
+		} else {
+			return getFirstElement().getTriggerNumber() == 0 ? true : false;
+		}
 	}
 	
 	/**
@@ -108,7 +116,7 @@
 	 * triggers and <code>false</code> otherwise.
 	 */
 	public boolean isPairTrigger() {
-		if(getFirstElement() instanceof PairTrigger && getSecondElement() instanceof SSPPairTrigger) {
+		if(getFirstElement() instanceof PairTrigger || getSecondElement() instanceof SSPPairTrigger) {
 			return true;
 		} else {
 			return false;
@@ -122,7 +130,7 @@
 	 * second trigger and <code>false</code> otherwise.
 	 */
 	public boolean isSecondTrigger() {
-		return getSecondElement().isSecondTrigger();
+		return !isFirstTrigger();
 	}
 	
 	/**
@@ -131,7 +139,7 @@
 	 * triggers and <code>false</code> otherwise.
 	 */
 	public boolean isSinglesTrigger() {
-		if(getFirstElement() instanceof SinglesTrigger && getSecondElement() instanceof SSPSinglesTrigger) {
+		if(getFirstElement() instanceof SinglesTrigger || getSecondElement() instanceof SSPSinglesTrigger) {
 			return true;
 		} else {
 			return false;

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java	Wed Mar 25 14:43:27 2015
@@ -15,7 +15,9 @@
 	protected int[] sspInternalMatched = new int[2];
 	protected int[] reconTriggersMatched = new int[2];
 	protected int[][] triggerComp = new int[4][2];
-	
+	protected int[] unmatchedTriggers = new int[2];
+	protected int[] triggerTypesSeen = new int[2];
+	protected int[] triggerTypesFound = new int[2];
 	
 	/**
 	 * Instantiates a <code>TriggerStatModule</code> with no statistics
@@ -40,6 +42,13 @@
 		for(int i = 0; i < reconTriggersMatched.length; i++) {
 			reconTriggersMatched[i] = base.reconTriggersMatched[i];
 		}
+		for(int i = 0; i < triggerTypesSeen.length; i++) {
+			triggerTypesSeen[i] = base.triggerTypesSeen[i];
+			triggerTypesFound[i] = base.triggerTypesFound[i];
+		}
+		for(int i = 0; i < unmatchedTriggers.length; i++) {
+			unmatchedTriggers[i] = base.unmatchedTriggers[i];
+		}
 		for(int i = 0; i < triggerComp.length; i++) {
 			for(int j = 0; j < triggerComp[i].length; j++) {
 				triggerComp[i][j] = base.triggerComp[i][j];
@@ -82,6 +91,40 @@
 	}
 	
 	/**
+	 * Gets the number of events that were readout due to a trigger
+	 * of the indicated type.
+	 * @param triggerType - The type of trigger.
+	 * @return Returns the number of events readout because of the
+	 * trigger type as an <code>int</code>.
+	 */
+	public int getEventsOfType(int triggerNum) {
+		// Make sure that the trigger type is defined.
+		if(triggerNum < 0 || triggerNum > 1) {
+			throw new IndexOutOfBoundsException(String.format("Trigger number \"%d\" is not valid.", triggerNum));
+		}
+		
+		// Return the number of events that were trigger by this type.
+		return triggerTypesSeen[triggerNum];
+	}
+	
+	/**
+	 * Gets the number of events that were readout due to a trigger
+	 * of the indicated type where a reconstructed cluster simulated
+	 * trigger of that type existed.
+	 * @param triggerType - The type of trigger.
+	 * @return Returns the number of events as an <code>int</code>.
+	 */
+	public int getEventsOfTypeSeen(int triggerNum) {
+		// Make sure that the trigger type is defined.
+		if(triggerNum < 0 || triggerNum > 1) {
+			throw new IndexOutOfBoundsException(String.format("Trigger number \"%d\" is not valid.", triggerNum));
+		}
+		
+		// Return the number of events that were trigger by this type.
+		return triggerTypesFound[triggerNum];
+	}
+	
+	/**
 	 * Gets the number of SSP bank triggers that were reported in excess
 	 * of the number of simulated SSP triggers seen.
 	 * @return Returns the value as an <code>int</code> primitive.
@@ -163,4 +206,21 @@
 	public int getSSPBankTriggerCount() {
 		return reportedTriggers;
 	}
+	
+	/**
+	 * Gets the number of triggers for this trigger for which there
+	 * were no matches.
+	 * @param triggerNum - The trigger for which to get the value.
+	 * @return Returns the number of triggers that failed to match as
+	 * an <code>int</code>.
+	 */
+	public int getUnmatchedTriggers(int triggerNum) {
+		// Validate the arguments.
+		if(triggerNum != 0 && triggerNum != 1) {
+			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
+		}
+		
+		// Return the requested cut value.
+		return unmatchedTriggers[triggerNum];
+	}
 }

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java	Wed Mar 25 14:43:27 2015
@@ -1,6 +1,6 @@
 package org.hps.analysis.trigger.util;
 
-import org.hps.readout.ecal.TriggerModule;
+import org.hps.recon.ecal.triggerbank.TriggerModule;
 
 public class PairTrigger<E> extends SinglesTrigger<E> {
 	// Define the supported trigger cuts.
@@ -65,7 +65,7 @@
 	 * <code>false</code> otherwise.
 	 */
 	public boolean getStateEnergyDifference() {
-		return getCutState(PAIR_ENERGY_SUM_HIGH);
+		return getCutState(PAIR_ENERGY_DIFFERENCE_HIGH);
 	}
 	
 	/**

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java	Wed Mar 25 14:43:27 2015
@@ -1,6 +1,6 @@
 package org.hps.analysis.trigger.util;
 
-import org.hps.readout.ecal.TriggerModule;
+import org.hps.recon.ecal.triggerbank.TriggerModule;
 
 public class SinglesTrigger<E> extends Trigger<E> {
 	// Define the supported trigger cuts.

Modified: java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java
 =============================================================================
--- java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java	(original)
+++ java/branches/prod/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java	Wed Mar 25 14:43:27 2015
@@ -2,7 +2,7 @@
 
 import java.awt.Point;
 
-import org.hps.readout.ecal.triggerbank.SSPCluster;
+import org.hps.recon.ecal.triggerbank.SSPCluster;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.Cluster;
 
@@ -19,7 +19,8 @@
 	public static final byte CLUSTER_STATE_FAIL_POSITION  = 1;
 	public static final byte CLUSTER_STATE_FAIL_ENERGY    = 2;
 	public static final byte CLUSTER_STATE_FAIL_HIT_COUNT = 3;
-	public static final byte CLUSTER_STATE_FAIL_UNKNOWN   = 4;
+	public static final byte CLUSTER_STATE_FAIL_TIME      = 4;
+	public static final byte CLUSTER_STATE_FAIL_UNKNOWN   = 5;
 	
 	// Trigger match cut IDs.
 	public static final int SINGLES_ENERGY_MIN = 0;

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectCollection.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectCollection.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectCollection.java	Wed Mar 25 14:43:27 2015
@@ -8,6 +8,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 
+import org.hps.conditions.api.ConditionsObject.DefaultConditionsObjectComparator;
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.conditions.database.TableMetaData;
 
@@ -201,7 +202,37 @@
      * @param comparator The comparator to use for the sort.
      * @return A sorted list of the objects.
      */
+    @SuppressWarnings("unchecked")
     public AbstractConditionsObjectCollection<ObjectType> sorted(Comparator<ObjectType> comparator) {
-        throw new UnsupportedOperationException("This method is not implemented.");
+        List<ObjectType> objects = new ArrayList<ObjectType>(this);
+        Collections.sort(objects, comparator);
+        AbstractConditionsObjectCollection<ObjectType> collection = null;
+        try {
+            collection = (AbstractConditionsObjectCollection<ObjectType>) getClass().newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        collection.addAll(objects);
+        return collection;
+    }
+    
+    public void sort() {
+        AbstractConditionsObjectCollection<ObjectType> sortedCollection = sorted();
+        this.clear();
+        this.addAll(sortedCollection);
+    }
+    
+    public AbstractConditionsObjectCollection<ObjectType> sorted() {
+        List<ObjectType> objects = new ArrayList<ObjectType>(this);
+        Collections.sort(objects, new DefaultConditionsObjectComparator());
+        AbstractConditionsObjectCollection<ObjectType> collection = null;
+        try {
+            // FIXME: This is kind of ugly.
+            collection = (AbstractConditionsObjectCollection<ObjectType>) getClass().newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        collection.addAll(objects);
+        return collection;
     }
 }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObject.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObject.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObject.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,6 @@
 package org.hps.conditions.api;
+
+import java.util.Comparator;
 
 /**
  * This is an ORM interface for accessing conditions database information by
@@ -63,4 +65,16 @@
      * @return True if record is new.
      */
     public boolean isNew();
+    
+    static class DefaultConditionsObjectComparator implements Comparator<ConditionsObject> {
+        public int compare(ConditionsObject o1, ConditionsObject o2) {
+            if (o1.getRowId() < o2.getRowId()) {
+                return -1;
+            } else if (o1.getRowId() > o2.getRowId()) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }        
+    }
 }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java	Wed Mar 25 14:43:27 2015
@@ -68,4 +68,8 @@
      * @return A sorted list of the objects.
      */
     public AbstractConditionsObjectCollection<ObjectType> sorted(Comparator<ObjectType> comparator);
+    
+    public void sort();
+    
+    public AbstractConditionsObjectCollection<ObjectType> sorted();
 }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/database/ConditionsObjectConverter.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/database/ConditionsObjectConverter.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/database/ConditionsObjectConverter.java	Wed Mar 25 14:43:27 2015
@@ -76,6 +76,11 @@
         
         // Get the TableMetaData from the table name.
         TableMetaData tableMetaData = databaseConditionsManager.findTableMetaData(name);    
+        
+        // Throw an exception if the table name does not map to a known type.
+        if (tableMetaData == null) {
+            throw new RuntimeException(new ConditionsObjectException("No table information found for name: " + name));
+        }
         
         // Get the ConditionsRecordCollection with the run number assignments.
         ConditionsRecordCollection conditionsRecords = databaseConditionsManager.findConditionsRecords(name);

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java	Wed Mar 25 14:43:27 2015
@@ -12,8 +12,10 @@
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.ArrayList;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -62,7 +64,7 @@
 public final class DatabaseConditionsManager extends ConditionsManagerImplementation {
 
     // Initialize logger.
-    private static Logger logger = LogUtil.create(DatabaseConditionsManager.class.getName(), new DefaultLogFormatter(), Level.INFO);
+    private static Logger logger = LogUtil.create(DatabaseConditionsManager.class.getName(), new DefaultLogFormatter(), Level.FINE);
 
     // Global registry of conditions converters.
     private ConverterRegistry converters = ConverterRegistry.create();
@@ -131,6 +133,33 @@
             registerConditionsConverter(converter);
         }
         addConditionsListener(svtSetup);
+    }    
+       
+    /**
+     * Get the static instance of this class.
+     * @return The static instance of the manager.
+     */
+    public synchronized static DatabaseConditionsManager getInstance() {
+
+        logger.finer("getting conditions manager instance");
+        
+        // Is there no manager installed yet?
+        if (!ConditionsManager.isSetup() || !(ConditionsManager.defaultInstance() instanceof DatabaseConditionsManager)) {
+            logger.finer("creating new instance");
+            // Create a new instance if necessary, which will install it globally as the default.
+            new DatabaseConditionsManager();
+        }
+
+        // Get the instance back from the default conditions system and check that the type is correct now.
+        ConditionsManager manager = ConditionsManager.defaultInstance();
+        if (!(manager instanceof DatabaseConditionsManager)) {
+            logger.severe("default conditions manager has wrong type");
+            throw new RuntimeException("Default conditions manager has the wrong type: " + ConditionsManager.defaultInstance().getClass().getName());
+        }
+        
+        logger.finer("returning conditions manager instance");
+
+        return (DatabaseConditionsManager) manager;
     }
     
     /**
@@ -145,35 +174,10 @@
     }
     
     /**
-     * Get the static instance of this class.
-     * @return The static instance of the manager.
-     */
-    public static DatabaseConditionsManager getInstance() {
-
-        logger.fine("setting up new conditions manager instance");
-        
-        // Is there no manager installed yet?
-        if (!ConditionsManager.isSetup()) {
-            // Create a new instance if necessary.
-            new DatabaseConditionsManager();
-        }
-
-        // Get the instance back from the default conditions system and check that the type is correct.
-        ConditionsManager manager = ConditionsManager.defaultInstance();
-        if (!(manager instanceof DatabaseConditionsManager)) {
-            throw new RuntimeException("The default ConditionsManager has the wrong type.");
-        }
-        
-        logger.fine("instance initialized succesfully");
-
-        return (DatabaseConditionsManager) manager;
-    }
-    
-    /**
      * Open the database connection.
      * @return True if a connection was opened; false if using an existing connection.
      */
-    public boolean openConnection() {
+    public synchronized boolean openConnection() {
         boolean openedConnection = false;
         if (!isConnected) {
             // Do the connection parameters need to be figured out automatically?
@@ -207,7 +211,7 @@
     /**
      * Close the database connection.
      */
-    public void closeConnection() {
+    public synchronized void closeConnection() {
         logger.fine("closing connection");
         if (connection != null) {
             try {
@@ -228,7 +232,7 @@
      * based on the flag.  Otherwise, it should be left open.
      * @param connectionOpened True to close the connection; false to leave it open.
      */
-    public void closeConnection(boolean connectionOpened) {
+    public synchronized void closeConnection(boolean connectionOpened) {
         if (connectionOpened) {
             closeConnection();
         }
@@ -265,7 +269,7 @@
      * needs to be updated.
      */
     @Override
-    public void setDetector(String detectorName, int runNumber) throws ConditionsNotFoundException {
+    public synchronized void setDetector(String detectorName, int runNumber) throws ConditionsNotFoundException {
 
         logger.finest("setDetector " + detectorName + " with run number " + runNumber);
         
@@ -367,7 +371,7 @@
      * @param tableName The name of the table.
      * @return The next collection ID.
      */
-    public int getNextCollectionID(String tableName) {
+    public synchronized int getNextCollectionID(String tableName) {
         boolean openedConnection = openConnection();
         ResultSet resultSet = selectQuery("SELECT MAX(collection_id)+1 FROM " + tableName);
         int collectionId = 1;
@@ -533,7 +537,7 @@
      * This method can be called to "freeze" the conditions system so that
      * any subsequent updates to run number or detector name will be ignored.
      */
-    public void freeze() {
+    public synchronized void freeze() {
         if (getDetector() != null && getRun() != -1) {
             isFrozen = true;
             logger.config("conditions system is frozen");
@@ -545,7 +549,7 @@
     /**
      * Un-freeze the conditions system so that updates will be received again.
      */
-    public void unfreeze() {
+    public synchronized void unfreeze() {
         isFrozen = false;
         logger.info("conditions system unfrozen");
     }
@@ -701,6 +705,39 @@
     public boolean isInitialized() {
         return isInitialized;
     }
+    
+    /**
+     * Get the set of unique conditions tags from the conditions table.
+     * @return The list of unique conditions tags.
+     */
+    public Set<String> getTags() {
+        logger.fine("getting list of available conditions tags");
+        boolean openedConnection = openConnection();
+        Set<String> tags = new LinkedHashSet<String>();
+        ResultSet rs = selectQuery("select distinct(tag) from conditions where tag is not null order by tag");
+        try {
+            while (rs.next()) {
+                tags.add(rs.getString(1));
+            }
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+        try {
+            rs.close();
+        } catch (SQLException e) {
+            logger.log(Level.WARNING, "error closing ResultSet", e);
+        }
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("found unique conditions tags ...");
+        for (String tag : tags) {
+            buffer.append(tag + " ");
+        }
+        buffer.setLength(buffer.length() - 1);
+        buffer.append('\n');
+        logger.fine(buffer.toString());        
+        closeConnection(openedConnection);
+        return tags;
+    }
 
     /*
      *******************************
@@ -713,7 +750,7 @@
      * configuration and loading of conditions onto the Detector.
      */
     private void initialize(String detectorName, int runNumber) throws ConditionsNotFoundException {
-        
+                
         logger.config("initializing with detector " + detectorName + " and run " + runNumber);
         
         // Is not configured yet?

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableMetaData.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableMetaData.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableMetaData.java	Wed Mar 25 14:43:27 2015
@@ -1,12 +1,15 @@
 package org.hps.conditions.database;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsObject;
+import org.hps.conditions.api.ConditionsObjectCollection;
 
 /**
  * <p>
@@ -30,6 +33,7 @@
     protected Class<? extends ConditionsObject> objectClass;
     protected Class<? extends AbstractConditionsObjectCollection<?>> collectionClass;
     protected Set<String> fieldNames = new LinkedHashSet<String>();
+    protected Map<String, Class<?>> fieldTypes;
 
     /**
      * The fully qualified constructor.
@@ -37,19 +41,53 @@
      * @param objectClass The type of object for the data mapping.
      * @param collectionClass The type of collection for the data mapping.
      */
-    public TableMetaData(String key, String tableName, Class<? extends ConditionsObject> objectClass, Class<? extends AbstractConditionsObjectCollection<?>> collectionClass) {
+    /*
+    public TableMetaData(
+            String key, 
+            String tableName, 
+            Class<? extends ConditionsObject> objectClass, 
+            Class<? extends AbstractConditionsObjectCollection<?>> collectionClass,
+            Map<String, Class<?>> fieldTypes) {
+       
         this.key = key;
         this.tableName = tableName;
         this.objectClass = objectClass;
         this.collectionClass = collectionClass;
+        this.fieldTypes = fieldTypes;
     }
+    */
     
-    public TableMetaData(String key, String tableName, Class<? extends ConditionsObject> objectClass, Class<? extends AbstractConditionsObjectCollection<?>> collectionClass, Set<String> fieldNames) {
+    public TableMetaData(
+            String key, 
+            String tableName, 
+            Class<? extends ConditionsObject> objectClass, 
+            Class<? extends AbstractConditionsObjectCollection<?>> collectionClass, 
+            Set<String> fieldNames,
+            Map<String, Class<?>> fieldTypes) {
+        if (key == null) {
+            throw new IllegalArgumentException("key is null");
+        }
+        if (tableName == null) {
+            throw new IllegalArgumentException("tableName is null");
+        }
+        if (objectClass == null) {
+            throw new IllegalArgumentException("objectClass is null");
+        }
+        if (fieldNames == null) {
+            throw new IllegalArgumentException("fieldNames is null");
+        }
+        if (collectionClass == null) {
+            throw new IllegalArgumentException("collectionClass is null");
+        }
+        if (fieldTypes == null) {
+            throw new IllegalArgumentException("fieldTypes is null");
+        }
         this.key = key;
         this.tableName = tableName;
         this.objectClass = objectClass;
         this.collectionClass = collectionClass;
         this.fieldNames = fieldNames;
+        this.fieldTypes = fieldTypes;
     }
     
     /**
@@ -77,15 +115,11 @@
     }
 
     /**
-     * Add a field.
-     * @param name The name of the field.
+     * Get the type of the field called <code>fieldName</code>.
+     * @return The type of the field.
      */
-    void addField(String name) {
-        fieldNames.add(name);
-    }
-    
-    void addFields(List<String> names) {
-        fieldNames.addAll(names);
+    public Class<?> getFieldType(String fieldName) {
+        return fieldTypes.get(fieldName);
     }
 
     /**
@@ -106,15 +140,11 @@
     }
     
     static public List<TableMetaData> findByObjectType(List<TableMetaData> tableMetaDataList, Class<? extends ConditionsObject> objectType) {
-        System.out.println("findByObjectType - " + objectType.getCanonicalName());
         List<TableMetaData> list = new ArrayList<TableMetaData>();
         for (TableMetaData tableMetaData : tableMetaDataList) {
-            System.out.println("comparing to " + tableMetaData.getObjectClass().getCanonicalName());
             if (tableMetaData.getObjectClass().equals(objectType)) {
-                System.out.println("found match");
+
                 list.add(tableMetaData);
-            } else {
-                System.out.println("does not match");
             }
         }
         return list;

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableRegistry.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableRegistry.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/database/TableRegistry.java	Wed Mar 25 14:43:27 2015
@@ -1,8 +1,11 @@
 package org.hps.conditions.database;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
@@ -42,13 +45,32 @@
     static TableRegistry create() {
         TableRegistry registry = new TableRegistry();
         for (Class<? extends ConditionsObject> objectType : ConditionsObjectUtilities.findConditionsObjectTypes()) {
+            
+            // Get the collection type.
             Class<? extends AbstractConditionsObjectCollection<?>> collectionType = 
                     ConditionsObjectUtilities.getCollectionType(objectType);
+            
+            // Get the list of field names.
             Set<String> fieldNames = ConditionsObjectUtilities.getFieldNames(objectType);            
-            for (String name : ConditionsObjectUtilities.getTableNames(objectType)) {    
-                
+            
+            // Create map of fields to their types.
+            Map<String, Class<?>> fieldTypes = new HashMap<String, Class<?>>();
+            for (Method method : objectType.getMethods()) {
+                if (!method.getReturnType().equals(Void.TYPE)) {
+                    for (Annotation annotation : method.getAnnotations()) {
+                        if (annotation.annotationType().equals(Field.class)) {                            
+                            Field field = (Field) annotation;
+                            for (String fieldName : field.names()) {
+                                fieldTypes.put(fieldName, method.getReturnType());
+                            }
+                        }
+                    }
+                }
+            }
+                        
+            for (String name : ConditionsObjectUtilities.getTableNames(objectType)) {                    
                 // Create a meta data mapping for each table name in the class description.
-                TableMetaData data = new TableMetaData(name, name, objectType, collectionType, fieldNames);
+                TableMetaData data = new TableMetaData(name, name, objectType, collectionType, fieldNames, fieldTypes);
                 registry.put(name, data);                               
                 registry.objectTypeMap.add(objectType, data);
                 registry.collectionTypeMap.add(collectionType, data);                  

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalBadChannel.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalBadChannel.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalBadChannel.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,6 @@
 package org.hps.conditions.ecal;
+
+import java.util.Comparator;
 
 import org.hps.conditions.api.AbstractConditionsObject;
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
@@ -16,14 +18,30 @@
 public final class EcalBadChannel extends AbstractConditionsObject {
 
     public static class EcalBadChannelCollection extends AbstractConditionsObjectCollection<EcalBadChannel> {
+        
+        public AbstractConditionsObjectCollection<EcalBadChannel> sorted() {
+            return sorted(new ChannelIdComparator());
+        }
+                
+        class ChannelIdComparator implements Comparator<EcalBadChannel> {
+            public int compare(EcalBadChannel o1, EcalBadChannel o2) {
+                if (o1.getChannelId() < o2.getChannelId()) {
+                    return -1;
+                } else if (o1.getChannelId() > o2.getChannelId()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        }        
     }
-
+    
     /**
-     * Get the channel ID of the bad channel.
-     * @return The channel ID of the bad channel.
+     * Get the ECAL channel ID.
+     * @return The ECAL channel ID.
      */
     @Field(names = {"ecal_channel_id"})
     public int getChannelId() {
-        return getFieldValue("ecal_channel_id");
+        return getFieldValue("ecal_channel_id");    
     }
-}
+}

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalCalibration.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalCalibration.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalCalibration.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,6 @@
 package org.hps.conditions.ecal;
+
+import java.util.Comparator;
 
 import org.hps.conditions.api.AbstractConditionsObject;
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
@@ -21,8 +23,24 @@
 public final class EcalCalibration extends AbstractConditionsObject {
 
     public static class EcalCalibrationCollection extends AbstractConditionsObjectCollection<EcalCalibration> {
+        
+        public AbstractConditionsObjectCollection<EcalCalibration> sorted() {
+            return sorted(new ChannelIdComparator());
+        }
+                
+        class ChannelIdComparator implements Comparator<EcalCalibration> {
+            public int compare(EcalCalibration o1, EcalCalibration o2) {
+                if (o1.getChannelId() < o2.getChannelId()) {
+                    return -1;
+                } else if (o1.getChannelId() > o2.getChannelId()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        }
     }
-    
+               
     public EcalCalibration() {
     }
     
@@ -33,12 +51,12 @@
     }
 
     /**
-     * Get the channel ID.
-     * @return The channel ID.
+     * Get the ECAL channel ID.
+     * @return The ECAL channel ID.
      */
     @Field(names = {"ecal_channel_id"})
     public int getChannelId() {
-        return getFieldValue("ecal_channel_id");
+        return getFieldValue("ecal_channel_id");    
     }
 
     /**

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java	Wed Mar 25 14:43:27 2015
@@ -1,10 +1,7 @@
 package org.hps.conditions.ecal;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import org.hps.conditions.api.AbstractConditionsObject;
@@ -137,20 +134,6 @@
         }
     }
     
-    private static final class ChannelIdComparator implements Comparator<EcalChannel> {
-        
-        public int compare(EcalChannel c1, EcalChannel c2) {
-            if (c1.getChannelId() < c2.getChannelId()) {
-                return -1;
-            } else if (c1.getChannelId() > c2.getChannelId()) {
-                return 1;
-            } else {
-                return 0;
-            }
-        }
-        
-    }
-
     DaqId createDaqId() {
         return new DaqId(new int[] { getCrate(), getSlot(), getChannel() });
     }
@@ -254,16 +237,22 @@
          */
         public EcalChannel findDaq(long id) {
             return daqMap.get(id);
-        }
-        
-        /**
-         * Get a list of channels sorted by channel ID.
-         * @return A list of channels sorted by channel ID.
-         */
-        public List<EcalChannel> sortedByChannelId() {
-            List<EcalChannel> channelList = new ArrayList<EcalChannel>(this);
-            Collections.sort(channelList, new ChannelIdComparator());
-            return channelList;
+        }        
+                   
+        public AbstractConditionsObjectCollection<EcalChannel> sorted() {
+            return sorted(new ChannelIdComparator());
+        }
+            
+        class ChannelIdComparator implements Comparator<EcalChannel> {        
+            public int compare(EcalChannel c1, EcalChannel c2) {
+                if (c1.getChannelId() < c2.getChannelId()) {
+                    return -1;
+                } else if (c1.getChannelId() > c2.getChannelId()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }            
         }
     }
 

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalGain.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalGain.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalGain.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,6 @@
 package org.hps.conditions.ecal;
+
+import java.util.Comparator;
 
 import org.hps.conditions.api.AbstractConditionsObject;
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
@@ -17,8 +19,26 @@
 public final class EcalGain extends AbstractConditionsObject {
 
     public static class EcalGainCollection extends AbstractConditionsObjectCollection<EcalGain> {
+        
+        public AbstractConditionsObjectCollection<EcalGain> sorted() {
+            return sorted(new ChannelIdComparator());
+        }
+                
+        class ChannelIdComparator implements Comparator<EcalGain> {
+            public int compare(EcalGain o1, EcalGain o2) {
+                if (o1.getChannelId() < o2.getChannelId()) {
+                    return -1;
+                } else if (o1.getChannelId() > o2.getChannelId()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+            
+        }
     }
-
+    
+    
     /**
      * Get the gain value in units of MeV/ADC count.
      * @return The gain value.

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLed.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLed.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLed.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,6 @@
 package org.hps.conditions.ecal;
+
+import java.util.Comparator;
 
 import org.hps.conditions.api.AbstractConditionsObject;
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
@@ -6,6 +8,7 @@
 import org.hps.conditions.database.Field;
 import org.hps.conditions.database.MultipleCollectionsAction;
 import org.hps.conditions.database.Table;
+import org.hps.conditions.ecal.EcalGain.EcalGainCollection.ChannelIdComparator;
 
 /**
  * A conditions class for representing the setup of the LED system in the ECAL
@@ -20,6 +23,22 @@
      * Generic collection class for these objects.
      */
     public static class EcalLedCollection extends AbstractConditionsObjectCollection<EcalLed> {
+        public AbstractConditionsObjectCollection<EcalLed> sorted() {
+            return sorted(new ChannelIdComparator());
+        }
+                
+        class ChannelIdComparator implements Comparator<EcalLed> {
+            public int compare(EcalLed o1, EcalLed o2) {
+                if (o1.getEcalChannelId() < o2.getEcalChannelId()) {
+                    return -1;
+                } else if (o1.getEcalChannelId() > o2.getEcalChannelId()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+            
+        }
     }
 
     /**

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLedCalibration.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLedCalibration.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalLedCalibration.java	Wed Mar 25 14:43:27 2015
@@ -15,6 +15,9 @@
      * Generic collection class for these objects.
      */
     public static class EcalLedCalibrationCollection extends AbstractConditionsObjectCollection<EcalLedCalibration> {
+    }
+    
+    public EcalLedCalibration() {        
     }
     
     public EcalLedCalibration(int channelId, double mean, double rms) {

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalTimeShift.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalTimeShift.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/ecal/EcalTimeShift.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,6 @@
 package org.hps.conditions.ecal;
+
+import java.util.Comparator;
 
 import org.hps.conditions.api.AbstractConditionsObject;
 import org.hps.conditions.api.AbstractConditionsObjectCollection;
@@ -6,6 +8,7 @@
 import org.hps.conditions.database.Field;
 import org.hps.conditions.database.MultipleCollectionsAction;
 import org.hps.conditions.database.Table;
+import org.hps.conditions.ecal.EcalCalibration.EcalCalibrationCollection.ChannelIdComparator;
 
 /**
  * This class represents a time shift calibration value for an ECAL channel.
@@ -19,6 +22,21 @@
      * A collection of {@link EcalTimeShift} objects.
      */
     public static class EcalTimeShiftCollection extends AbstractConditionsObjectCollection<EcalTimeShift> {
+        public AbstractConditionsObjectCollection<EcalTimeShift> sorted() {
+            return sorted(new ChannelIdComparator());
+        }
+                
+        class ChannelIdComparator implements Comparator<EcalTimeShift> {
+            public int compare(EcalTimeShift o1, EcalTimeShift o2) {
+                if (o1.getChannelId() < o2.getChannelId()) {
+                    return -1;
+                } else if (o1.getChannelId() > o2.getChannelId()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        }
     }
 
     /**

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtChannel.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtChannel.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtChannel.java	Wed Mar 25 14:43:27 2015
@@ -24,10 +24,10 @@
         Map<Integer, T> channelMap = new HashMap<Integer, T>();
 
         /**
-         * Add a channel of type extending {@link AbstractSvtChannel} to the
-         * channel map
+         *  Add a channel of type extending {@link AbstractSvtChannel} to the
+         *  channel map
          * 
-         * @param A channel of a type extending {@link AbstractSvtChannel}
+         *  @param A channel of a type extending {@link AbstractSvtChannel}
          */
         public boolean add(T channel) {
 
@@ -42,29 +42,29 @@
         }
 
         /**
-         * Find a channel of type extending {@link AbstractSvtChannel} using the
-         * channel ID
+         *  Find a channel of type extending {@link AbstractSvtChannel} using the
+         *  channel ID
          * 
-         * @param channelID
-         * @return An SVT channel of type extending {@link AbstractSvtChannel}
+         *  @param channelID
+         *  @return An SVT channel of type extending {@link AbstractSvtChannel}
          */
         public T findChannel(int channelID) {
             return channelMap.get(channelID);
         }
 
         /**
-         * Find the collection of channels of type extending
-         * {@link AbstractSvtChannel} that match a DAQ pair.
+         *  Find the collection of channels of type extending
+         *  {@link AbstractSvtChannel} that match a DAQ pair.
          * 
-         * @param pair The DAQ pair.
-         * @return The channels matching the DAQ pair or null if not found.
+         *  @param pair The DAQ pair.
+         *  @return The channels matching the DAQ pair or null if not found.
          */
         public abstract Collection<T> find(Pair<Integer, Integer> pair);
 
         /**
-         * Convert this object to a human readable string.
+         *  Convert this object to a human readable string.
          * 
-         * @return This object converted to a string.
+         *  @return This object converted to a string.
          */
         public String toString() {
             StringBuffer buff = new StringBuffer();
@@ -76,9 +76,9 @@
     }
 
     /**
-     * Get the channel ID.
+     *  Get the channel ID.
      * 
-     * @return The channel ID.
+     *  @return The SVT channel ID.
      */
     @Field(names = {"channel_id"})
     public int getChannelID() {
@@ -86,12 +86,30 @@
     }
 
     /**
-     * Get the channel number. This is different from the ID.
+     *  Get the channel number (0-639). This is different from the ID.
      * 
-     * @return The channel number.
+     *  @return The channel number.
      */
     @Field(names = {"channel"})
     public int getChannel() {
         return getFieldValue("channel");
     }
+    
+    /**
+     *  Set the channel ID.
+     * 
+     *  @param channelID : The SVT channel ID
+     */
+    public void setChannelID(int channelID) { 
+        this.setFieldValue("channel_id", channelID);
+    }
+    
+    /**
+     *  Set the channel number (0-639). This is different from the ID.
+     * 
+     *  @param channel : The channel number
+     */
+    public void setChannel(int channel) { 
+        this.setFieldValue("channel", channel);
+    }
 }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtDaqMapping.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtDaqMapping.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/AbstractSvtDaqMapping.java	Wed Mar 25 14:43:27 2015
@@ -14,8 +14,6 @@
  */
 public abstract class AbstractSvtDaqMapping extends AbstractConditionsObject {
 
-    public static abstract class AbstractSvtDaqMappingCollection<T extends AbstractSvtDaqMapping> extends AbstractConditionsObjectCollection<T> {
-
         /**
          * Flag values for top or bottom half.
          */
@@ -27,6 +25,9 @@
          */
         public static final String AXIAL = "A";
         public static final String STEREO = "S";
+
+        public static abstract class AbstractSvtDaqMappingCollection<T extends AbstractSvtDaqMapping> extends AbstractConditionsObjectCollection<T> {
+
 
         /**
          * Get a DAQ pair for the given {@link HpsSiSensor}
@@ -48,18 +49,68 @@
 
     }
 
+    /**
+     *  Get the SVT half (TOP or BOTTOM) that the sensor belongs to.
+     *  
+     *  @return SVT half (TOP or BOTTOM)
+     */
     @Field(names = {"svt_half"})
     public String getSvtHalf() {
         return getFieldValue("svt_half");
     }
 
+    /**
+     *  Get the SVT sensor layer number (1-10 for test run and 1-12 for
+     *  engineering run).
+     *  
+     *  @return SVT sensor layer number
+     */
     @Field(names = {"layer"})
     public int getLayerNumber() {
         return getFieldValue("layer");
     }
 
+    
+    /**
+     *  Get the orientation of an SVT sensor (AXIAL or STEREO).
+     * 
+     *  @param orientation : Orientation of an SVT sensor (AXIAL or STEREO) 
+     */
     @Field(names = {"orientation"})
     public String getOrientation() {
         return getFieldValue("orientation");
     }
+    
+    /**
+     *  Set the SVT half (TOP or BOTTOM) that the sensor belongs to.
+     *  
+     *   @param svtHalf : SVT half (TOP or BOTTOM)
+     */
+    public void setSvtHalf(String svtHalf) { 
+        if (!svtHalf.equals(AbstractSvtDaqMapping.TOP_HALF) && !svtHalf.equals(AbstractSvtDaqMapping.BOTTOM_HALF)) 
+            throw new RuntimeException("[ " + this.getClass().getSimpleName() + " ]: Invalid value of SVT half.");
+        this.setFieldValue("svt_half", svtHalf);
+        
+    }
+    
+    /**
+     *  Set the SVT sensor layer number (1-10 for test run and 1-12 for
+     *  engineering run).
+     *  
+     *  @param layer : SVT sensor layer number
+     */
+    public void setLayerNumber(int layer) { 
+        this.setFieldValue("layer", layer);
+    }
+    
+    /**
+     *  Set the orientation of an SVT sensor (AXIAL or STEREO).
+     * 
+     *  @param orientation : Orientation of an SVT sensor (AXIAL or STEREO) 
+     */
+    public void setOrientation(String orientation) { 
+        if (!orientation.equals(AbstractSvtDaqMapping.AXIAL) && !orientation.equals(AbstractSvtDaqMapping.STEREO))
+            throw new RuntimeException("[ " + this.getClass().getSimpleName() + " ]: Invalid orientation of sensor.");
+        this.setFieldValue("orientation", orientation);
+    }
 }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/CalibrationHandler.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/CalibrationHandler.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/CalibrationHandler.java	Wed Mar 25 14:43:27 2015
@@ -1,8 +1,14 @@
 package org.hps.conditions.svt;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import org.xml.sax.helpers.DefaultHandler;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
+
+import org.lcsim.util.log.DefaultLogFormatter;
+import org.lcsim.util.log.LogUtil;
 
 import org.hps.conditions.svt.SvtChannel.SvtChannelCollection;
 import org.hps.conditions.svt.SvtCalibration.SvtCalibrationCollection;
@@ -14,6 +20,11 @@
  *  @author Omar Moreno <[log in to unmask]>
  */
 class CalibrationHandler extends DefaultHandler {
+    
+    
+    // Initialize the logger
+    private static Logger logger = LogUtil.create(SvtConditionsLoader.class.getSimpleName(), 
+            new DefaultLogFormatter(), Level.INFO);
 
     // List of SVT channels
     private SvtChannelCollection svtChannels;
@@ -39,12 +50,17 @@
     // Noise sample ID (0-5)
     int noiseSampleID = 0; 
     
+    // Flag denoting whether the calibrations of a given channel should be
+    // loaded into the conditions DB.  If a channel is found to be missing
+    // baseline or noise values, is will be marked invalid.
+    boolean isValidChannel = false;
+    
     /**
      *  Default Constructor
      */
     public CalibrationHandler() {
-        svtChannels = DatabaseConditionsManager.getInstance()
-                .getSvtConditions().getChannelMap();
+       svtChannels = (SvtChannelCollection) DatabaseConditionsManager.getInstance()
+               .getCachedConditions(SvtChannelCollection.class, "svt_channels").getCachedData();
     }
 
     /**
@@ -63,15 +79,15 @@
         switch (qName) {
             case "Feb":
                 febID = Integer.parseInt(attributes.getValue("id"));
-                System.out.println("FEB ID: " + febID);
                 break;
             case "Hybrid":
                 hybridID = Integer.parseInt(attributes.getValue("id"));
-                System.out.println("Hybrid ID: " + hybridID);
+                logger.info("Processing calibrations for FEB " + febID + " Hybrid " + hybridID);
                 break;
             case "channel":
                 channel = Integer.parseInt(attributes.getValue("id"));
                 calibration = new SvtCalibration(svtChannels.findChannelID(febID, hybridID, channel));
+                isValidChannel = false;
                 break;
             case "baseline":
                 baselineSampleID = Integer.parseInt(attributes.getValue("id"));
@@ -96,13 +112,16 @@
         
         switch (qName) { 
             case "channel":
+                if (!isValidChannel) break;
                 calibrations.add(calibration);
                 break;
             case "baseline":
-                calibration.setPedestal(baselineSampleID, Double.parseDouble(content)); 
+                calibration.setPedestal(baselineSampleID, Double.parseDouble(content));
+                isValidChannel = true;
                 break;
             case "noise": 
                 calibration.setNoise(baselineSampleID, Double.parseDouble(content)); 
+                isValidChannel = true;
                 break;
         }
     }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtChannel.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtChannel.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtChannel.java	Wed Mar 25 14:43:27 2015
@@ -20,12 +20,38 @@
 @Converter(multipleCollectionsAction = MultipleCollectionsAction.LAST_CREATED)
 public final class SvtChannel extends AbstractSvtChannel {
 
+    /**
+     * 
+     */
+    public SvtChannel() { 
+    }
+    
+    /**
+     *  Constructor 
+     *
+     *  @param channelID : The SVT channel ID
+     *  @param febID : The Front End Board (FEB) ID (0-9)
+     *  @param febHybridID : The hybrid ID (0-3)
+     *  @param channel : The channel number (0-639)
+     */
+    public SvtChannel(int channelID, int febID, int febHybridID, int channel) { 
+        if (!this.isValidFeb(febID) 
+                || !this.isValidFebHybridID(febHybridID) 
+                || !this.isValidPhysicalChannel(channel)) { 
+            throw new RuntimeException("Invalid FEB ID, FEB hybrid ID or physical channel number is being used.");
+        }
+        this.setChannelID(channelID);
+        this.setFebID(febID);
+        this.setFebHybridID(febHybridID);
+        this.setChannel(channel);
+    }
+    
     public static class SvtChannelCollection extends AbstractSvtChannel.AbstractSvtChannelCollection<SvtChannel> {
         /**
-         * Find channels that match a DAQ pair (FEB ID, FEB Hybrid ID).
+         *  Find channels that match a DAQ pair (FEB ID, FEB Hybrid ID).
          * 
-         * @param pair : The DAQ pair consiting of a FEB ID and FEB Hybrid ID.
-         * @return The channels matching the DAQ pair or null if ?not found.
+         *  @param pair : The DAQ pair consiting of a FEB ID and FEB Hybrid ID.
+         *  @return The channels matching the DAQ pair or null if ?not found.
          */
         @Override
         public Collection<SvtChannel> find(Pair<Integer, Integer> pair) {
@@ -41,13 +67,13 @@
         }
 
         /**
-         * Get the SVT channel ID associated with a given FEB ID/Hybrid ID/physical channel.
+         *  Get the SVT channel ID associated with a given FEB ID/Hybrid ID/physical channel.
          *
-         * @param febID : The FEB ID
-         * @param febHybridID : The FEB hybrid ID
-         * @param channel : The physical channel number
-         * @return The SVT channel ID
-         * @throws {@link RuntimeException} if the channel ID can't be found
+         *  @param febID : The FEB ID
+         *  @param febHybridID : The FEB hybrid ID
+         *  @param channel : The physical channel number
+         *  @return The SVT channel ID
+         *  @throws {@link RuntimeException} if the channel ID can't be found
          */
         public int findChannelID(int febID, int febHybridID, int channel) {
             for (SvtChannel svtChannel : this) {
@@ -61,9 +87,9 @@
     }
 
     /**
-     * Get the FEB ID associated with this SVT channel ID.
+     *  Get the FEB ID associated with this SVT channel ID.
      * 
-     * @return The FEB ID.
+     *  @return The FEB ID.
      */
     @Field(names = { "feb_id" })
     public int getFebID() {
@@ -71,9 +97,9 @@
     }
 
     /**
-     * Get the FEB hybrid ID associated with this SVT channel ID.
+     *  Get the FEB hybrid ID associated with this SVT channel ID.
      * 
-     * @return The FEB hybrid ID.
+     *  @return The FEB hybrid ID.
      */
     @Field(names = { "feb_hybrid_id" })
     public int getFebHybridID() {
@@ -81,8 +107,58 @@
     }
 
     /**
-     * Implementation of equals.
-     * @return True if the object equals this one; false if not.
+     *  Set the FEB ID associated with this SVT channel ID.
+     * 
+     *  @param febID : The FEB ID
+     */
+    public void setFebID(int febID) { 
+        this.setFieldValue("feb_id", febID);
+    }
+    
+    /**
+     *  Set the FEB hybrid ID associated with this SVT channel ID.
+     * 
+     *  @param febHybridID : The FEB hybrid ID
+     */
+    public void setFebHybridID(int febHybridID) {
+        this.setFieldValue("feb_hybrid_id", febHybridID);
+    }
+    
+    /**
+     *  Checks if a FEB ID is valid
+     * 
+     *  @param febID : The Front End Board (FEB) ID
+     *  @return True if the FEB ID lies within the range 0-9, false otherwise
+     */
+    public boolean isValidFeb(int febID) { 
+        return (febID >= 0 && febID <= 9) ? true : false; 
+    }
+    
+    /**
+     *  Checks if a Front End Board hybrid ID is valid
+     *  
+     *  @param febHybridID : The hybrid ID 
+     *  @return True if the hybrid ID lies within the range 0-3, false otherwise
+     */
+    public boolean isValidFebHybridID(int febHybridID) { 
+        return (febHybridID >= 0 && febHybridID <= 3) ? true : false; 
+    }
+    
+    /**
+     *  Checks if a physical channel number is valid
+     *  
+     *  @param channel : The physical channel number
+     *  @return True if the channel number lies within the range 0-639, false 
+     *          otherwise
+     */
+    public boolean isValidPhysicalChannel(int channel) { 
+        return (channel >= 0 && channel <= 639) ? true : false; 
+    }
+    
+    /**
+     *  Implementation of equals.
+     *  
+     *  @return True if the object equals this one; false if not.
      */
     public boolean equals(Object o) {
         if (o == null)

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDaqMapping.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDaqMapping.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDaqMapping.java	Wed Mar 25 14:43:27 2015
@@ -14,9 +14,32 @@
  * @author Omar Moreno <[log in to unmask]>
  */
 @Table(names = {"svt_daq_map"})
-@Converter(multipleCollectionsAction = MultipleCollectionsAction.ERROR)
+@Converter(multipleCollectionsAction = MultipleCollectionsAction.LAST_CREATED)
 public class SvtDaqMapping extends AbstractSvtDaqMapping {
+    
+    /**
+     *  Constants describing the side of a sensor
+     */
+    public static final String ELECTRON = "ELECTRON";
+    public static final String POSITRON = "POSITRON";
 
+    /**
+     *  Default Constructor 
+     */
+    public SvtDaqMapping() { 
+    }
+   
+    /**
+     *  Constructor 
+     *
+     *  @param febID : The Front End Board (FEB) ID (0-9)
+     *  @param febHybridID : The FEB hybrid ID (0-3)
+     */
+    public SvtDaqMapping(int febID, int febHybridID) { 
+        this.setFebID(febID);
+        this.setFebHybridID(febHybridID);
+    }
+    
     public static class SvtDaqMappingCollection extends AbstractSvtDaqMappingCollection<SvtDaqMapping> {
 
         /**
@@ -100,18 +123,62 @@
         }
     }
 
+    /**
+     *  Get the Front End Board (FEB) ID.
+     *
+     *  @return The FEB ID
+     */
     @Field(names = {"feb_id"})
     public int getFebID() {
         return getFieldValue("feb_id");
     }
 
+    /**
+     *  Get the Front End Board (FEB) hybrid ID.
+     *  
+     *  @param The FEB hybrid ID
+     */
     @Field(names = {"feb_hybrid_id"})
     public int getFebHybridID() {
         return getFieldValue("feb_hybrid_id");
     }
 
+    /**
+     *  Get the side of the sensor (ELECTRON or POSITRON).
+     *  
+     *  @param sensor side (ELECTRON or POSITRON)
+     */
     @Field(names = {"side"})
     public String getSide() {
         return getFieldValue("side");
     }
+    
+    /**
+     *  Set the Front End Board (FEB) ID.
+     *  
+     *  @param febID : FEB ID
+     */
+    public void setFebID(int febID) { 
+        this.setFieldValue("feb_id", febID);
+    }
+    
+    /**
+     *  Set the Front End Board (FEB) hybrid ID.
+     *  
+     *  @param febHybridID : FEB hybrid ID
+     */
+    public void setFebHybridID(int febHybridID) { 
+        this.setFieldValue("feb_hybrid_id", febHybridID);
+    }
+    
+    /**
+     *  Set the side of the sensor (ELECTRON or POSITRON).
+     *  
+     *  @param side : sensor side (ELECTRON or POSITRON)
+     */
+    public void setSide(String side) {
+        if (!side.equals(SvtDaqMapping.ELECTRON) && !side.equals(SvtDaqMapping.POSITRON)) 
+            throw new RuntimeException("[ " + this.getClass().getSimpleName() + " ]: Invalid value for sensor side.");
+        this.setFieldValue("side", side);
+    }
 }

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java	Wed Mar 25 14:43:27 2015
@@ -112,9 +112,9 @@
 
             // Set the orientation of the sensor
             String orientation = daqMap.getOrientation(daqPair);
-            if (orientation != null && orientation.contentEquals(SvtDaqMappingCollection.AXIAL)) {
+            if (orientation != null && orientation.contentEquals(SvtDaqMapping.AXIAL)) {
                 sensor.setAxial(true);
-            } else if (orientation != null && orientation.contains(SvtDaqMappingCollection.STEREO)) {
+            } else if (orientation != null && orientation.contains(SvtDaqMapping.STEREO)) {
                 sensor.setStereo(true);
             }
 
@@ -194,9 +194,9 @@
 
             // Set the orientation of the sensor
             String orientation = daqMap.getOrientation(daqPair);
-            if (orientation != null && orientation.contentEquals(TestRunSvtDaqMappingCollection.AXIAL)) {
+            if (orientation != null && orientation.contentEquals(TestRunSvtDaqMapping.AXIAL)) {
                 sensor.setAxial(true);
-            } else if (orientation != null && orientation.contains(TestRunSvtDaqMappingCollection.STEREO)) {
+            } else if (orientation != null && orientation.contains(TestRunSvtDaqMapping.STEREO)) {
                 sensor.setStereo(true);
             }
 

Modified: java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtChannel.java
 =============================================================================
--- java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtChannel.java	(original)
+++ java/branches/prod/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtChannel.java	Wed Mar 25 14:43:27 2015
@@ -11,7 +11,7 @@
 import org.hps.util.Pair;
 
 @Table(names = {"test_run_svt_channels"})
-@Converter(multipleCollectionsAction = MultipleCollectionsAction.ERROR)
+@Converter(multipleCollectionsAction = MultipleCollectionsAction.LAST_CREATED)
 public final class TestRunSvtChannel extends AbstractSvtChannel {
 
     public static class TestRunSvtChannelCollection extends AbstractSvtChannel.AbstractSvtChannelCollection<TestRunSvtChannel> {

Modified: java/branches/prod/conditions/src/test/java/org/hps/conditions/EngRunConditionsTest.java
 =============================================================================
--- java/branches/prod/conditions/src/test/java/org/hps/conditions/EngRunConditionsTest.java	(original)
+++ java/branches/prod/conditions/src/test/java/org/hps/conditions/EngRunConditionsTest.java	Wed Mar 25 14:43:27 2015
@@ -109,7 +109,7 @@
             
             ecalConditions = conditionsManager.getCachedConditions(EcalConditions.class, "ecal_conditions").getCachedData();
             Set<EcalChannelConstants> channelConstants = new LinkedHashSet<EcalChannelConstants>();
-            for (EcalChannel channel : ecalConditions.getChannelCollection().sortedByChannelId()) {
+            for (EcalChannel channel : ecalConditions.getChannelCollection().sorted()) {
                 channelConstants.add(ecalConditions.getChannelConstants(channel));
             }
             assertEquals("Wrong number of channel constants.", nChannels, channelConstants.size());

Modified: java/branches/prod/detector-data/detectors/HPS-ECalCommissioning-v2/detector.properties
 =============================================================================
--- java/branches/prod/detector-data/detectors/HPS-ECalCommissioning-v2/detector.properties	(original)
+++ java/branches/prod/detector-data/detectors/HPS-ECalCommissioning-v2/detector.properties	Wed Mar 25 14:43:27 2015
@@ -1 +1 @@
-name: HPS-ECalCommissioning-v3
+name: HPS-ECalCommissioning-v2

Modified: java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java
 =============================================================================
--- java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java	(original)
+++ java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java	Wed Mar 25 14:43:27 2015
@@ -92,9 +92,16 @@
     private double fixedGain = -1;
     private boolean constantTriggerWindow = true;
     private boolean addNoise = false;
-    private double pePerMeV = 2.0; //photoelectrons per MeV, used to calculate noise
+   
+    // 32.8 p.e./MeV = New detector in 2014
+    // 2 p.e./MeV = Test Run detector
+    private double pePerMeV = 32.8; //photoelectrons per MeV, used to calculate noise
+    
     //switch between test run and 2014 definitions of gain constants
-    private boolean use2014Gain = true;
+    // true = ONLY simulation studies in 2014
+    // false = Test Run data/simulations and 2014+ Detector's real data 
+    private boolean use2014Gain = false;
+    
     //switch between three pulse shape functions
     private PulseShape pulseShape = PulseShape.ThreePole;
 
@@ -448,9 +455,11 @@
                 //add preamp noise and photoelectron Poisson noise in quadrature
                 double noise;
                 if (use2014Gain) {
-                    noise = Math.sqrt(Math.pow(channelData.getCalibration().getNoise() * channelData.getGain().getGain() * ECalUtils.gainFactor * ECalUtils.ecalReadoutPeriod, 2) + hit.getRawEnergy() / (ECalUtils.lightYield * ECalUtils.quantumEff * ECalUtils.surfRatio));
+                    noise = Math.sqrt(Math.pow(channelData.getCalibration().getNoise() * channelData.getGain().getGain() * ECalUtils.gainFactor * ECalUtils.ecalReadoutPeriod, 2) 
+                    		+ hit.getRawEnergy() / (ECalUtils.lightYield * ECalUtils.quantumEff * ECalUtils.surfRatio));
                 } else {
-                    noise = Math.sqrt(Math.pow(channelData.getCalibration().getNoise() * channelData.getGain().getGain() * ECalUtils.MeV, 2) + hit.getRawEnergy() * ECalUtils.MeV / pePerMeV);
+                    noise = Math.sqrt(Math.pow(channelData.getCalibration().getNoise() * channelData.getGain().getGain() * ECalUtils.MeV, 2) 
+                    		+ hit.getRawEnergy() * ECalUtils.MeV / pePerMeV);
                 }
                 energyAmplitude += RandomGaussian.getGaussian(0, noise);
             }

Modified: java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCPrimaryTriggerDriver.java
 =============================================================================
--- java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCPrimaryTriggerDriver.java	(original)
+++ java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCPrimaryTriggerDriver.java	Wed Mar 25 14:43:27 2015
@@ -9,6 +9,7 @@
 import java.util.Queue;
 
 import org.hps.recon.ecal.ECalUtils;
+import org.hps.recon.ecal.triggerbank.TriggerModule;
 import org.lcsim.event.Cluster;
 import org.lcsim.event.EventHeader;
 import org.lcsim.util.aida.AIDA;
@@ -703,4 +704,14 @@
         topClusterQueue.remove();
         botClusterQueue.remove();
     }
+    
+    /**
+     * Sets all cut values for the trigger using a string argument with
+     * the format "clusterMin clusterMax hitMin sumMin sumMax diffMax
+     * slopeMin slopeF coplanarMax timeDiffMax".
+     * @param cuts - The cut string.
+     */
+    public void setCuts(String cuts) {
+    	triggerModule.setCutValues(false, cuts);
+    }
 }

Modified: java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/SinglesTriggerDriver.java
 =============================================================================
--- java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/SinglesTriggerDriver.java	(original)
+++ java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/SinglesTriggerDriver.java	Wed Mar 25 14:43:27 2015
@@ -5,6 +5,7 @@
 
 import java.util.List;
 
+import org.hps.recon.ecal.triggerbank.TriggerModule;
 import org.lcsim.event.Cluster;
 import org.lcsim.event.EventHeader;
 import org.lcsim.util.aida.AIDA;
@@ -21,8 +22,7 @@
  */
 public class SinglesTriggerDriver extends TriggerDriver {
     // Cut Values
-    private TriggerModule triggerModule = new TriggerModule(2.0, 0.125,
-    		6.600, 0.200, 6.600, 0.000, 13.200, 6.600, 0.0, 360, 0.0055);
+    private TriggerModule triggerModule = new TriggerModule();
     
     // LCIO Collection Names
     private String clusterCollectionName = "EcalClusters";
@@ -69,6 +69,9 @@
      */
     @Override
     protected boolean triggerDecision(EventHeader event) {
+    	// Track whether triggering cluster was seen.
+    	boolean passTrigger = false;
+    	
         // Check that there is a cluster object collection.
         if(event.hasCollection(Cluster.class, clusterCollectionName)) {
             // Get the list of hits.
@@ -92,6 +95,9 @@
                     continue triggerLoop;
                 }
                 
+                // A trigger was seen. Note it.
+                passTrigger = true;
+                
                 // Get the x and y indices.
                 int ix = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix");
                 int iy = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy");
@@ -105,9 +111,8 @@
             }
         }
         
-        // If there were either no passing hits or not hits at all,
-        // then there is also no trigger.
-        return false;
+        // Return whether a triggering cluster was seen.
+        return passTrigger;
     }
     
     /**
@@ -162,4 +167,13 @@
     public void setClusterCollectionName(String clusterCollectionName) {
         this.clusterCollectionName = clusterCollectionName;
     }
+    
+    /**
+     * Sets all cut values for the trigger using a string argument with
+     * the format "Emin Emax Nmin".
+     * @param cuts - The cut string.
+     */
+    public void setCuts(String cuts) {
+    	triggerModule.setCutValues(true, cuts);
+    }
 }

Modified: java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TestRunTriggerDriver.java
 =============================================================================
--- java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TestRunTriggerDriver.java	(original)
+++ java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TestRunTriggerDriver.java	Wed Mar 25 14:43:27 2015
@@ -2,7 +2,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
+
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.lcsim.event.Cluster;
 import org.lcsim.event.EventHeader;
 

Modified: java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TriggerDriver.java
 =============================================================================
--- java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TriggerDriver.java	(original)
+++ java/branches/prod/ecal-readout-sim/src/main/java/org/hps/readout/ecal/TriggerDriver.java	Wed Mar 25 14:43:27 2015
@@ -9,8 +9,8 @@
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
-
+
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.lcsim.event.EventHeader;
 import org.lcsim.lcio.LCIOWriter;
 

Modified: java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalPedestalCalculator.java
 =============================================================================
--- java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalPedestalCalculator.java	(original)
+++ java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalPedestalCalculator.java	Wed Mar 25 14:43:27 2015
@@ -7,6 +7,8 @@
 import java.io.IOException;
 import java.sql.SQLException;
 import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 
 import org.hps.conditions.api.ConditionsObjectException;
 import org.hps.conditions.api.ConditionsRecord;
@@ -90,22 +92,31 @@
         userInput=cc.readLine("Enter filename prefix, or just press RETURN ...");
         if (userInput==null || userInput.length()==0 || userInput=="") {
             String home=System.getenv().get("HOME");
-            outputFilePrefix = home+"/EcalPedestalCalculator_"+runNumber+"_";
+            outputFilePrefix = home+"/EcalPedestals/EcalPedestalCalculator_"+runNumber+"_";
         } else {
             outputFilePrefix = userInput;
         }
+        
+        String date = new SimpleDateFormat("yyyy-MM-dd-hh-mm").format(new Date());
+        outputFilePrefix += date+"_";
 
         if (writeFileForDAQ) writeFileForDAQ(outputFilePrefix);
         if (writeFileForDB)  writeFileForDB(outputFilePrefix);
         
-        System.err.println("\n\n***************************************************************\n");
+        System.out.println("\n\n***************************************************************\n");
         userInput=cc.readLine(String.format("Enter 'YES' to write conditions database for run range [%s,%s] ...",runNumber,runNumberMax));
         System.out.println("***********"+userInput+"********");
+        boolean uploadToDB=false;
         if (userInput!=null && userInput.equals("YES")) {
             userInput=cc.readLine("Really?");
             if (userInput!=null && userInput.equals("YES")) {
-                uploadToDB();
-            }
+                uploadToDB=true;
+            }
+        }
+        if (uploadToDB) {
+            uploadToDB();
+        } else {
+            System.out.println("!!!!!!!!!!!!!!!!!!!!!!! Not Writing Database !!!!!!!!!!!!!!!!!!!!!!!!!!");
         }
     }
 

Modified: java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java
 =============================================================================
--- java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java	(original)
+++ java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java	Wed Mar 25 14:43:27 2015
@@ -1,6 +1,7 @@
 package org.hps.recon.ecal;
 
-import java.awt.List;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.util.ArrayList;
 import java.util.Map;
 
@@ -8,6 +9,8 @@
 import org.hps.conditions.ecal.EcalChannel;
 import org.hps.conditions.ecal.EcalChannelConstants;
 import org.hps.conditions.ecal.EcalConditions;
+import org.hps.recon.ecal.daqconfig.ConfigurationManager;
+import org.hps.recon.ecal.daqconfig.FADCConfig;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.GenericObject;
@@ -22,10 +25,8 @@
  * objects with energy information.
  *
  * @author Sho Uemura <[log in to unmask]>
- * @author Jeremy McCormick <[log in to unmask]>
  * @author Andrea Celentano <[log in to unmask]>
  * @author Nathan Baltzell <[log in to unmask]>
- *
  *
  * baltzell:  New in 2015:  (default behavior is still unchanged)
  *
@@ -36,9 +37,6 @@
  * 
  * Pedestal subtracting clipped pulses more correctly for all Modes.
  * 
- * Changed threshold cut for Mode-1 to >= instead of > to emulate SSP instead of
- * FADC firmware for trigger diagnostics.
- *
  * Implemented finding multiple peaks for Mode-1.
  * 
  * Implemented conversion of Mode-1 to Mode-7 with high-resolution timing.
@@ -53,6 +51,8 @@
     private boolean constantGain = false;
     private double gain;
     private boolean use2014Gain = true;
+    private boolean useDAQConfig = false;
+    private FADCConfig config = null;
 
     /*
      * The time for one FADC sample (units = ns).
@@ -66,7 +66,7 @@
      * 
      * The default value of 12 is what we used for most of the 2014 run.
      */
-    private double leadingEdgeThreshold=12;
+    private double leadingEdgeThreshold = 12;
     
     /*
      * Integration range after (NSA) and before (NSB) threshold crossing.  Units=ns,
@@ -75,8 +75,8 @@
      * 
      * The default values of 20/100 are what we had during the entire 2014 run.
      */
-    private int NSB=20;
-    private int NSA=100;
+    private int NSB = 20;
+    private int NSA = 100;
   
     /*
      * The number of samples in the FADC readout window.  Needed in order to
@@ -87,48 +87,89 @@
      * the old behavior which assumed integration range was constant.
      * 
      */
-    private int windowSamples=-1;
+    private int windowSamples = -1;
     
     /*
      * The maximum number of peaks to be searched for.
      */
-    private int nPeak=3;
+    private int nPeak = 3;
    
     /*
      * Convert Mode-1 into Mode-7, else Mode-3.
      */
-    private boolean mode7=false;
+    private boolean mode7 = false;
 
 
     private EcalConditions ecalConditions = null;
 
     public EcalRawConverter() {
-    }
-
+    	// Track changes in the DAQ configuration.
+    	ConfigurationManager.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				// If the DAQ configuration should be used, load the
+				// relevant settings into the driver.
+				if(useDAQConfig) {
+					// Get the FADC configuration.
+					config = ConfigurationManager.getInstance().getFADCConfig();
+					
+					// Load the settings.
+					NSB = config.getNSB();
+					NSA = config.getNSA();
+					windowSamples = config.getWindowWidth() / 4;
+					
+					// Get the number of peaks.
+					if(config.getMode() == 1) {
+						nPeak = Integer.MAX_VALUE;
+					} else {
+						nPeak = config.getMaxPulses();
+					}
+					
+					// Print the FADC configuration.
+					System.out.println();
+					System.out.println();
+					System.out.printf("NSA            :: %d ns%n", NSA);
+					System.out.printf("NSB            :: %d ns%n", NSB);
+					System.out.printf("Window Samples :: %d clock-cycles%n", windowSamples);
+					System.out.printf("Max Peaks      :: %d peaks%n", nPeak);
+					System.out.println("======================================================================");
+					System.out.println("=== FADC Pulse-Processing Settings ===================================");
+					System.out.println("======================================================================");
+					config.printConfig();
+				}
+			}
+    	});
+    }
+  
     public void setLeadingEdgeThreshold(double thresh) {
         leadingEdgeThreshold=thresh;
     }
+    
     public void setNSA(int nsa) {
         if (NSA%nsPerSample !=0 || NSA<0) {
             throw new RuntimeException("NSA must be multiples of 4ns and non-negative.");
         }
         NSA=nsa;
     }
+    
     public void setNSB(int nsb) {
         if (NSB%nsPerSample !=0 || NSB<0) {
             throw new RuntimeException("NSB must be multiples of 4ns and non-negative.");
         }
         NSB=nsb;
     }
+    
     public void setWindowSamples(int windowSamples) {
         this.windowSamples=windowSamples;
     }
+    
     public void setNPeak(int nPeak) {
         if (nPeak<1 || nPeak>3) {
             throw new RuntimeException("Npeak must be 1, 2, or 3.");
         }
         this.nPeak=nPeak;
     }
+    
     public void setMode7(boolean mode7)
     {
         this.mode7=mode7;
@@ -150,13 +191,24 @@
     public void setUseTimeWalkCorrection(boolean useTimeWalkCorrection) {
         this.useTimeWalkCorrection=useTimeWalkCorrection;
     }
+    
+    public void setUseDAQConfig(boolean state) {
+    	useDAQConfig = state;
+    }
 
     /*
      * This should probably be deprecated.  It just integrates the entire window.
      */
     public int sumADC(RawTrackerHit hit) {
         EcalChannelConstants channelData = findChannel(hit.getCellID());
-        double pedestal = channelData.getCalibration().getPedestal();
+        double pedestal;
+        if(useDAQConfig) {
+    		//EcalChannel channel = ecalConditions.getChannelCollection().findGeometric(hit.getCellID());
+    		pedestal = config.getPedestal(hit.getCellID());
+        } else {
+        	pedestal = channelData.getCalibration().getPedestal();
+        }
+        
         int sum = 0;
         short samples[] = hit.getADCValues();
         for (int isample = 0; isample < samples.length; ++isample) {
@@ -182,11 +234,13 @@
      * Choose whether to use static pedestal from database or running pedestal from mode-7.
      */
     public double getSingleSamplePedestal(EventHeader event,long cellID) {
+    	if(useDAQConfig) {
+    		//EcalChannel channel = ecalConditions.getChannelCollection().findGeometric(cellID);
+    		return config.getPedestal(cellID);
+    	}
         if (useRunningPedestal && event!=null) {
             if (event.hasItem("EcalRunningPedestals")) {
-                Map<EcalChannel, Double> runningPedMap=
-                        (Map<EcalChannel, Double>)
-                        event.get("EcalRunningPedestals");
+                Map<EcalChannel, Double> runningPedMap = (Map<EcalChannel, Double>) event.get("EcalRunningPedestals");
                 EcalChannel chan = ecalConditions.getChannelCollection().findGeometric(cellID);
                 if (!runningPedMap.containsKey(chan)){
                     System.err.println("************** Missing Pedestal");
@@ -228,7 +282,7 @@
    
     
     /*
-     * Emulate the FADC250 firmware emulation Mode-1 waveform to a Mode-3/7 pulse,
+     * Emulate the FADC250 firmware in conversion of Mode-1 waveform to a Mode-3/7 pulse,
      * given a time for threshold crossing.
      */
     public double[] convertWaveformToPulse(RawTrackerHit hit,int thresholdCrossing,boolean mode7) {
@@ -245,12 +299,17 @@
             lastSample  = thresholdCrossing + NSA/nsPerSample - 1;
         }
         
-        // mode-7 min and max:
+        // mode-7's minimum/pedestal (average of first 4 samples):
         double minADC=0;
+        for (int jj=0; jj<4; jj++) minADC += samples[jj];
+        // does the firmware's conversion of min to int occur before or after time calculation?  undocumented.
+        minADC=(int)(minADC/4); 
+        
+        // mode-7's max pulse height:
         double maxADC=0;
         int sampleMaxADC=0;
         
-        // pulse integral:
+        // mode-3/7's pulse integral:
         short sumADC = 0;
         
         for (int jj=firstSample; jj<=lastSample; jj++) {
@@ -261,11 +320,8 @@
             // integrate pulse:
             sumADC += samples[jj];
            
-            // compute "minimum" at beginning of window:
-            if (jj<4) minADC+=samples[jj];
-          
             // find pulse maximum:
-            if (jj>firstSample && jj<samples.length-5) {
+            if (jj>firstSample && jj<samples.length-5) { // The "5" here is a firmware constant.
                 if (samples[jj+1]<samples[jj]) {
                     sampleMaxADC=jj;
                     maxADC=samples[jj];
@@ -273,15 +329,13 @@
             }
         }
        
-        // minADC is the average of first four samples:
-        minADC/=4;
-      
         // pulse time with 4ns resolution:
         double pulseTime=thresholdCrossing*nsPerSample;
         
         // calculate Mode-7 high-resolution time:
         if (mode7) {
             if (thresholdCrossing < 4) {
+                // special case where firmware sets max to zero and time to 4ns time.
                 maxADC=0;
             }
             else if (maxADC>0) {
@@ -293,10 +347,12 @@
                 double a1 = maxADC;
                 double slope = (a1-a0)/(t1-t0);
                 double halfMax = (maxADC+minADC)/2;
+                // this is not rigorously firmware-correct, need to find halfMax-crossing.
                 double tmpTime = t1 - (a1 - halfMax) / slope;
                 if (slope>0 && tmpTime>0) {
                     pulseTime = tmpTime;
                 }
+                // else another special firmware case
             }
         }
         
@@ -317,55 +373,64 @@
      * TODO: Generate GenericObject (and corresponding LCRelation) to store min and max
      * to fully emulate mode-7.  This is less important for now.
      *
-     * NOTE: March 7, 2015. Threshold crossing requirement currently emulates the current
-     * SSP firmware (>=) instead of FADC firmware (>) to aid trigger studies.  Firmware will
-     * be changed to make them identical.  But for now this code prefers SSP over FADC.
-     */
-    public ArrayList <CalorimeterHit> HitDtoA(EventHeader event,RawTrackerHit hit) {
-     
+     */
+    public ArrayList <CalorimeterHit> HitDtoA(EventHeader event, RawTrackerHit hit) {
         final long cellID = hit.getCellID();
         final short samples[] = hit.getADCValues();
-        if (samples.length==0) return null;
+        if(samples.length == 0) return null;
         
         // threshold is pedestal plus threshold configuration parameter:
-        final int absoluteThreshold = (int)(getSingleSamplePedestal(event,cellID)+leadingEdgeThreshold);
-       
+        final int absoluteThreshold;
+        if(useDAQConfig) {
+        	//EcalChannel channel = ecalConditions.getChannelCollection().findGeometric(hit.getCellID());
+        	//int leadingEdgeThreshold = ConfigurationManager.getInstance().getFADCConfig().getThreshold(channel.getChannelId());
+        	int leadingEdgeThreshold = config.getThreshold(cellID);
+        	absoluteThreshold = (int) (getSingleSamplePedestal(event, cellID) + leadingEdgeThreshold);
+        } else {
+        	absoluteThreshold = (int) (getSingleSamplePedestal(event, cellID) + leadingEdgeThreshold);
+        }
+        
         ArrayList <Integer> thresholdCrossings = new ArrayList<Integer>();
         
         // special case, first sample is above threshold:
-        if (samples[0] >= absoluteThreshold) { // SSP/FADC firmware discrepancy.
+        if (samples[0] > absoluteThreshold) {
             thresholdCrossings.add(0);
         } 
-
+        
         // search for threshold crossings:
-        for (int ii = 1; ii < samples.length; ++ii) {
-            if ( samples[ii]   >=  absoluteThreshold &&
-                 samples[ii-1] <   absoluteThreshold) // SSP/FADC firmware discrepancy.
-            {
+        for(int ii = 1; ii < samples.length; ++ii) {
+            if ( samples[ii]   >  absoluteThreshold && 
+                 samples[ii-1] <= absoluteThreshold) {
+                
                 // found one:
                 thresholdCrossings.add(ii);
 
                 // search for next threshold crossing begins at end of this pulse:
-                ii += NSA/nsPerSample-1; 
+                if(useDAQConfig && ConfigurationManager.getInstance().getFADCConfig().getMode() == 1) {
+                    // special case, emulating SSP:
+                	ii += 8;
+                } else {
+                    // "normal" case, emulating FADC250:
+                	ii += NSA/nsPerSample - 1;
+                }
 
                 // firmware limit on # of peaks:
                 if (thresholdCrossings.size() >= nPeak) break;
             }
         }
-
+        
         // make hits
-        ArrayList <CalorimeterHit> newHits=new ArrayList<CalorimeterHit>();
-        for (int thresholdCrossing : thresholdCrossings) {
-           
+        ArrayList <CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
+        for(int thresholdCrossing : thresholdCrossings) {
             // do pulse integral:
-            final double[] data = convertWaveformToPulse(hit,thresholdCrossing,mode7);
+            final double[] data = convertWaveformToPulse(hit, thresholdCrossing, mode7);
             double time = data[0];
             double sum = data[1];
             final double min = data[2]; // TODO: stick min and max in a GenericObject with an 
             final double max = data[3]; // LCRelation to finish mode-7 emulation
             
             // do pedestal subtraction:
-            sum -= getPulsePedestal(event,cellID,samples.length,thresholdCrossing);
+            sum -= getPulsePedestal(event, cellID, samples.length, thresholdCrossing);
           
             // do gain scaling:
             double energy = adcToEnergy(sum, cellID);
@@ -421,7 +486,7 @@
         // Get the channel data.
         EcalChannelConstants channelData = findChannel(id);
         int amplitude;
-        double pedestal = getPulsePedestal(null,id,windowSamples,(int)hit.getTime()/nsPerSample);
+        double pedestal = getPulsePedestal(null, id, windowSamples, (int) hit.getTime() / nsPerSample);
         if (constantGain) {
             amplitude = (int) Math.round((hit.getRawEnergy() / ECalUtils.MeV) / gain + pedestal);
         } else {
@@ -438,15 +503,18 @@
 
         // Get the channel data.
         EcalChannelConstants channelData = findChannel(cellID);
-
-        if (use2014Gain) {
+        
+        if(useDAQConfig) {
+        	//float gain = ConfigurationManager.getInstance().getFADCConfig().getGain(ecalConditions.getChannelCollection().findGeometric(cellID));
+        	return config.getGain(cellID) * adcSum * ECalUtils.MeV;
+        }  else if(use2014Gain) {
             if (constantGain) {
                 return adcSum * ECalUtils.gainFactor * ECalUtils.ecalReadoutPeriod;
             } else {
                 return channelData.getGain().getGain() * adcSum * ECalUtils.gainFactor * ECalUtils.ecalReadoutPeriod; // should not be used for the moment (2014/02)
             }
         } else {
-            if (constantGain) {
+            if(constantGain) {
                 return gain * adcSum * ECalUtils.MeV;
             } else {
                 return channelData.getGain().getGain() * adcSum * ECalUtils.MeV; //gain is defined as MeV/integrated ADC

Modified: java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java
 =============================================================================
--- java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java	(original)
+++ java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java	Wed Mar 25 14:43:27 2015
@@ -6,6 +6,7 @@
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.conditions.ecal.EcalChannelConstants;
 import org.hps.conditions.ecal.EcalConditions;
+import org.hps.recon.ecal.daqconfig.ConfigurationManager;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.GenericObject;
@@ -17,10 +18,6 @@
 import org.lcsim.util.Driver;
 
 /**
- *
- * @version $Id: HPSEcalRawConverterDriver.java,v 1.2 2012/05/03 00:17:54
- * phansson Exp $
- * baltzell
  * 
  * 
  * baltzell New in 2015:  (default behavior is unchanged)
@@ -52,6 +49,7 @@
     private boolean runBackwards = false;
     private boolean useTimestamps = false;
     private boolean useTruthTime = false;
+    private boolean useDAQConfig = false;
 
     private boolean emulateFirmware = false;
     
@@ -59,77 +57,243 @@
         converter = new EcalRawConverter();
     }
 
+    /**
+     * Set to <code>true</code> to use "2014" gain formula:<br/>
+     * <pre>channelGain * adcSum * gainFactor * readoutPeriod</pre>
+     * <p>
+     * This formula is deprecated and should generally not be used. 
+     * 
+     * @param use2014Gain True to use 2014 gain formulation.
+     */
     public void setUse2014Gain(boolean use2014Gain) {
         converter.setUse2014Gain(use2014Gain);
     }
 
+    /**
+     * Set to <code>true</code> to apply time walk correction from {@link EcalTimeWalk#correctTimeWalk(double, double)}.
+     * <p>
+     * This is only applicable to Mode-3 data.
+     * 
+     * @param useTimeWalkCorrection True to apply time walk correction.
+     */
     public void setUseTimeWalkCorrection(boolean useTimeWalkCorrection) {
         converter.setUseTimeWalkCorrection(useTimeWalkCorrection);
     }
+    
+    /**
+     * Set to <code>true</code> to use a running pedestal calibration from mode 7 data.
+     * <p>
+     * The running pedestal values are retrieved from the event collection "EcalRunningPedestals"
+     * which is a <code>Map</code> between {@link org.hps.conditions.ecal.EcalChannel} objects
+     * are their average pedestal.
+     * 
+     * @param useRunningPedestal True to use a running pedestal value.
+     */
     public void setUseRunningPedestal(boolean useRunningPedestal) {
         converter.setUseRunningPedestal(useRunningPedestal);
     }
 
+    /**
+     * Set to <code>true</code> to generate a {@link org.lcsim.event.CalorimeterHit} 
+     * collection which is a conversion from energy to raw signals.
+     * 
+     * @param runBackwards True to run the procedure backwards.
+     */
     public void setRunBackwards(boolean runBackwards) {
         this.runBackwards = runBackwards;
     }
 
+    /**
+     * Set to <code>true</code> to drop hits that are mapped to a hard-coded 
+     * bad FADC configuration from the Test Run.
+     * 
+     * @param dropBadFADC True to drop hits mapped to a bad FADC.
+     */
     public void setDropBadFADC(boolean dropBadFADC) {
         this.dropBadFADC = dropBadFADC;
     }
 
+    /**
+     * Set a minimum energy threshold in GeV for created {@link org.lcsim.event.CalorimeterHit}
+     * objects to be written into the output collection.
+     * @param threshold The minimum energy threshold in GeV.
+     */
     public void setThreshold(double threshold) {
         this.threshold = threshold;
     }
 
+    /**
+     * Set to <code>true</code> to use Mode-7 emulation in calculations.
+     * False is Mode-3.
+     * 
+     * @param mode7 True to use Mode-7 emulation in calculations.
+     */
     public void setEmulateMode7(boolean mode7) {
         converter.setMode7(mode7);
     }
+    
+    /**
+     * Set to <code>true</code> to emulate firmware conversion of Mode-1 to Mode-3/7 data.
+     * 
+     * @param emulateFirmware True to use firmware emulation.
+     */
     public void setEmulateFirmware(boolean emulateFirmware) {
         this.emulateFirmware = emulateFirmware;
     }
+    
+    /**
+     * Set the leading-edge threshold in ADC counts, relative to pedestal, for pulse-finding 
+     * and time determination.
+     * <p>
+     * Used to convert Mode-1 readout into Mode-3 or Mode-7 data that is usable by clustering.
+     * 
+     * @param threshold The leading edge threshold in ADC counts.
+     */
     public void setLeadingEdgeThreshold(double threshold) {
         converter.setLeadingEdgeThreshold(threshold);
     }
+    
+    /**
+     * Set the number of samples in the FADC readout window.
+     * <p>
+     * This is needed in order to properly pedestal-correct clipped pulses for mode-3 and mode-7.  
+     * It is ignored for mode-1 input, since this data already includes the number of samples.
+     * <p>
+     * A non-positive number disables pulse-clipped pedestals and reverts to the old behavior which 
+     * assumed that the integration range was constant.
+     * 
+     * @param windowSamples The number of samples in the FADC readout window.
+     */
     public void setWindowSamples(int windowSamples) {
         converter.setWindowSamples(windowSamples);
     }
+    
+    /**
+     * Set the integration range in nanoseconds after the threshold crossing. 
+     * <p>
+     * These numbers must be multiples of 4 nanoseconds.
+     * <p>
+     * This value is used for pulse integration in Mode-1, and pedestal subtraction in all modes.
+     * 
+     * @param nsa The number of nanoseconds after the threshold crossing.
+     * @see #setNsb(int)
+     */
     public void setNsa(int nsa) {
         converter.setNSA(nsa);
     }
+    
+    /**
+     * Set the integration range in nanoseconds before the threshold crossing.
+     * <p>
+     * These numbers must be multiples of 4 nanoseconds.
+     * <p>
+     * This value is used for pulse integration in Mode-1, and pedestal subtraction in all modes.
+     * 
+     * @param nsb The number of nanoseconds after the threshold crossing.
+     * @see #setNsa(int)
+     */
     public void setNsb(int nsb) {
         converter.setNSB(nsb);
     }
+    
+    /**
+     * Set the maximum number of peaks to search for in the signal, 
+     * which must be between 1 and 3, inclusive.
+     * @param nPeak The maximum number of peaks to search for in the signal.
+     */
     public void setNPeak(int nPeak) {
         converter.setNPeak(nPeak);
     }
     
+    /**
+     * Set a constant gain factor in the converter for all channels.
+     * @param gain The constant gain value.
+     */
     public void setGain(double gain) {
         converter.setGain(gain);
     }
 
+    /**
+     * Set the {@link org.lcsim.event.CalorimeterHit} collection name,
+     * which is used as input in "normal" mode and output when running
+     * "backwards".
+     * 
+     * @param ecalCollectionName The <code>CalorimeterHit</code> collection name.
+     * @see #runBackwards
+     */
     public void setEcalCollectionName(String ecalCollectionName) {
         this.ecalCollectionName = ecalCollectionName;
     }
 
+    /**
+     * Set the raw collection name which is used as output in "normal" mode
+     * and input when running "backwards".
+     * <p>
+     * Depending on the Driver configuration, this could be a collection
+     * of {@link org.lcsim.event.RawTrackerHit} objects for Mode-1
+     * or {@link org.lcsim.event.RawCalorimeterHit} objects for Mode-3
+     * or Mode-7.
+     * 
+     * @param rawCollectionName The raw collection name.
+     */
     public void setRawCollectionName(String rawCollectionName) {
         this.rawCollectionName = rawCollectionName;
     }
 
+    /**
+     * Set to <code>true</code> to ignore data from channels that
+     * are flagged as "bad" in the conditions system.
+     * @param apply True to ignore bad channels.
+     */
     public void setApplyBadCrystalMap(boolean apply) {
         this.applyBadCrystalMap = apply;
     }
 
+    /**
+     * Set to <code>true</code> to turn on debug output.
+     * @param debug True to turn on debug output.
+     */
     public void setDebug(boolean debug) {
         this.debug = debug;
     }
 
+    /**
+     * Set to <code>true</code> to use timestamp information from the ECal or trigger.
+     * @param useTimestamps True to use timestamp information.
+     */
+    // FIXME: What does this actually do?  What calculations does it affect?  
     public void setUseTimestamps(boolean useTimestamps) {
         this.useTimestamps = useTimestamps;
     }
 
+    /**
+     * Set to <code>true</code> to use MC truth information.
+     * @param useTruthTime True to use MC truth information.
+     */
+    // FIXME: What does this actually do?  What calculations does it affect?  
     public void setUseTruthTime(boolean useTruthTime) {
         this.useTruthTime = useTruthTime;
+    }
+    
+    /**
+     * Sets whether the driver should use the DAQ configuration from
+     * EvIO file for its parameters. If activated, the converter will
+     * obtain gains, thresholds, pedestals, the window size, and the
+     * pulse integration window from the EvIO file. This will replace
+     * and overwrite any manually defined settings.<br/>
+     * <br/>
+     * Note that if this setting is active, the driver will not output
+     * any data until a DAQ configuration has been read from the data
+     * stream.
+     * @param state - <code>true</code> indicates that the configuration
+     * should be read from the DAQ data in an EvIO file. Setting this
+     * to <code>false</code> will cause the driver to use its regular
+     * manually-defined settings and pull gains and pedestals from the
+     * conditions database.
+     */
+    public void setUseDAQConfig(boolean state) {
+    	useDAQConfig = state;
+    	converter.setUseDAQConfig(state);
     }
 
     @Override
@@ -185,6 +349,12 @@
 
     @Override
     public void process(EventHeader event) {
+    	// Do not process the event if the DAQ configuration should be
+    	// used for value, but is not initialized.
+    	if(useDAQConfig && !ConfigurationManager.isInitialized()) {
+    		return;
+    	}
+    	
         final int SYSTEM_TRIGGER = 0;
         final int SYSTEM_TRACKER = 1;
         final int SYSTEM_ECAL = 2;

Modified: java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java
 =============================================================================
--- java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java	(original)
+++ java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java	Wed Mar 25 14:43:27 2015
@@ -249,7 +249,7 @@
                 }
             }
         } else {
-            getLogger().severe("The input hit collection " + this.inputHitCollectionName + " is missing from the event.");
+            getLogger().info("The input hit collection " + this.inputHitCollectionName + " is missing from the event.");
             if (this.raiseErrorNoHitCollection) {
                 throw new RuntimeException("The expected input hit collection is missing from the event.");
             }

Modified: java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterDriver.java
 =============================================================================
--- java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterDriver.java	(original)
+++ java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterDriver.java	Wed Mar 25 14:43:27 2015
@@ -1,4 +1,11 @@
 package org.hps.recon.ecal.cluster;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import org.hps.recon.ecal.daqconfig.ConfigurationManager;
+import org.hps.recon.ecal.daqconfig.GTPConfig;
+import org.lcsim.event.EventHeader;
 
 /**
  * Class <code>GTPOnlineClusterDriver</code> allows parameters for the
@@ -7,16 +14,47 @@
  * @author Kyle McCarty <[log in to unmask]>
  */
 public class GTPOnlineClusterDriver extends ClusterDriver {
-    // Store the clustering algorithm.
     private final GTPOnlineClusterer gtp;
+    private boolean useDAQConfig = false;
     
     /**
      * Instantiates a new clustering algorithm using the readout
      * variant of the GTP clustering algorithm.
      */
     public GTPOnlineClusterDriver() {
+    	// Instantiate the clusterer.
         clusterer = ClustererFactory.create("GTPOnlineClusterer");
         gtp = (GTPOnlineClusterer) clusterer;
+        
+        // Track the DAQ configuration status.
+        ConfigurationManager.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				// If DAQ configuration settings should be used, then
+				// update the clusterer.
+				if(useDAQConfig) {
+					// Get the GTP settings.
+					GTPConfig config = ConfigurationManager.getInstance().getGTPConfig();
+					
+					// Send the DAQ configuration settings to the clusterer.
+					gtp.setSeedLowThreshold(config.getSeedEnergyCutConfig().getLowerBound());
+					gtp.setWindowAfter(config.getTimeWindowAfter());
+					gtp.setWindowBefore(config.getTimeWindowBefore());
+					
+					// Print the updated settings.
+					logSettings();
+				}
+			}
+        });
+    }
+    
+    @Override
+    public void process(EventHeader event) {
+    	// Only process an event if either the DAQ configuration is not
+    	// in use or if it has been initialized.
+    	if((useDAQConfig && ConfigurationManager.isInitialized()) || !useDAQConfig) {
+    		super.process(event);
+    	}
     }
     
     /**
@@ -25,19 +63,7 @@
     @Override
     public void startOfData() {
     	// VERBOSE :: Output the driver settings.
-    	if(gtp.isVerbose()) {
-			// Print the cluster driver header.
-			System.out.println();
-			System.out.println();
-			System.out.println("======================================================================");
-			System.out.println("=== GTP Readout Clusterer Settings ===================================");
-			System.out.println("======================================================================");
-			
-			// Output the driver settings.
-			System.out.printf("Seed Energy Threshold :: %.3f GeV%n", gtp.getSeedLowThreshold());
-			System.out.printf("Time Window (Before)  :: %.0f ns%n", gtp.getWindowBefore());
-			System.out.printf("Time Window (After)   :: %.0f ns%n", gtp.getWindowAfter());
-    	}
+    	if(gtp.isVerbose()) { logSettings(); }
     }
     
     /**
@@ -80,4 +106,31 @@
     public void setVerbose(boolean verbose) {
         gtp.setVerbose(verbose);
     }
+    
+    /**
+     * Sets whether GTP settings should be drawn from the EvIO data
+     * DAQ configuration or read from the steering file.
+     * @param state - <code>true</code> means that DAQ configuration
+     * will be used and <code>false</code> that it will not.
+     */
+    public void setUseDAQConfig(boolean state) {
+    	useDAQConfig = state;
+    }
+    
+    /**
+     * Outputs the current GTP settings to the terminal.
+     */
+    private void logSettings() {
+		// Print the cluster driver header.
+		System.out.println();
+		System.out.println();
+		System.out.println("======================================================================");
+		System.out.println("=== GTP Readout Clusterer Settings ===================================");
+		System.out.println("======================================================================");
+		
+		// Output the driver settings.
+		System.out.printf("Seed Energy Threshold :: %.3f GeV%n", gtp.getSeedLowThreshold());
+		System.out.printf("Time Window (Before)  :: %.0f ns%n", gtp.getWindowBefore());
+		System.out.printf("Time Window (After)   :: %.0f ns%n", gtp.getWindowAfter());
+    }
 }

Modified: java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java
 =============================================================================
--- java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java	(original)
+++ java/branches/prod/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java	Wed Mar 25 14:43:27 2015
@@ -158,30 +158,37 @@
                 // Iterate over the other hits and if the are within
                 // the clustering spatiotemporal window, compare their
                 // energies.
+                hitLoop:
                 for(CalorimeterHit hit : hitList) {
-                    // Do not perform the comparison if the hit is the
-                    // current potential seed.
-                    if(hit != seed) {
-                        // Check if the hit is within the spatiotemporal
-                        // clustering window.
-                        if(withinTimeVerificationWindow(seed, hit) && withinSpatialWindow(seed, hit)) {
-                            // Check if the hit invalidates the potential
-                            // seed.
-                            if(isValidSeed(seed, hit)) {
-                                // Make sure that the hit is also within
-                                // the hit add window; this may not be
-                                // the same as the verification window
-                                // if the asymmetric window is active.
-                                if(withinTimeClusteringWindow(seed, hit)) {
-                                    protoCluster.addHit(hit);
-                                }
+                	// Negative energy hits are never valid. Skip them.
+                	if(hit.getCorrectedEnergy() < 0) {
+                		continue hitLoop;
+                	}
+                	
+                	// Do not compare the potential seed hit to itself.
+                	if(hit == seed) {
+                		continue hitLoop;
+                	}
+                	
+                    // Check if the hit is within the spatiotemporal
+                    // clustering window.
+                    if(withinTimeVerificationWindow(seed, hit) && withinSpatialWindow(seed, hit)) {
+                        // Check if the hit invalidates the potential
+                        // seed.
+                        if(isValidSeed(seed, hit)) {
+                            // Make sure that the hit is also within
+                            // the hit add window; this may not be
+                            // the same as the verification window
+                            // if the asymmetric window is active.
+                            if(withinTimeClusteringWindow(seed, hit)) {
+                                protoCluster.addHit(hit);
                             }
-                            
-                            // If it is not, then skip the rest of the
-                            // loop; the potential seed is not really
-                            // a seed.
-                            else { continue seedLoop; }
                         }
+                        
+                        // If it is not, then skip the rest of the
+                        // loop; the potential seed is not really
+                        // a seed.
+                        else { continue seedLoop; }
                     }
                 }
                 
@@ -308,20 +315,37 @@
      * <code>false</code> otherwise.
      */
     private boolean withinSpatialWindow(CalorimeterHit seed, CalorimeterHit hit) {
-        // Get the x-indices of each hit.
-        int six = seed.getIdentifierFieldValue("ix");
-        int hix = hit.getIdentifierFieldValue("ix");
-        
-        // Check that the x indices are either the same or within a
-        // range of one of one another.
-        if((six == hix) || (six + 1 == hix) || (six - 1 == hix)) {
-            // Get the y-indices of each hit.
-            int siy = seed.getIdentifierFieldValue("iy");
-            int hiy = hit.getIdentifierFieldValue("iy");
+        // Get the y-indices of each hit.
+        int siy = seed.getIdentifierFieldValue("iy");
+        int hiy = hit.getIdentifierFieldValue("iy");
+        
+        // Ensure that the y-indices are either the same or are within
+        // one of one another.
+        if((siy == hiy) || (siy + 1 == hiy) || (siy - 1 == hiy)) {
+            // Get the x-indices of each hit.
+            int six = seed.getIdentifierFieldValue("ix");
+            int hix = hit.getIdentifierFieldValue("ix");
             
-            // Ensure that the y-indices are either the same or are
-            // within one of one another.
-            return (siy == hiy) || (siy + 1 == hiy) || (siy - 1 == hiy);
+            // If the x-indices are the same or within one of each other
+            // then the crystals are within the spatial window of one
+            // another.
+            if((six == hix) || (six + 1 == hix) || (six - 1 == hix)) {
+                return true;
+            }
+            
+            // Otherwise, check for the special case where ix = 1 is
+            // considered to be adjacent to ix = -1 rather than the
+            // expected ix = 0. (ix = 0 does not exist.)
+            else {
+            	// ix = -1 is adjacent to ix = 1 and vice versa.
+                if((six == -1 && hix == 1) || (six == 1 && hix == -1)) {
+                	return true;
+                }
+                
+                // Any other combination that reaches this point is not
+                // a valid combination.
+                else { return false; }
+            }
         }
         
         // If the x-index comparison fails, return false.

Modified: java/branches/prod/evio/pom.xml
 =============================================================================
--- java/branches/prod/evio/pom.xml	(original)
+++ java/branches/prod/evio/pom.xml	Wed Mar 25 14:43:27 2015
@@ -20,6 +20,10 @@
             <groupId>org.hps</groupId>
             <artifactId>hps-tracking</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.hps</groupId>
+            <artifactId>hps-record-util</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
@@ -29,6 +33,8 @@
                 <configuration>
                     <excludes>
                         <exclude>org/hps/evio/LCSimEngRunEventBuilderTest.java</exclude>
+                        <exclude>org/hps/evio/SvtEvioReaderTest.java</exclude>
+                        <exclude>org/hps/evio/TestRunSvtEvioReaderTest.java</exclude>
                     </excludes>
                     <trimStackTrace>true</trimStackTrace>
                 </configuration>

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java	Wed Mar 25 14:43:27 2015
@@ -5,39 +5,47 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.jlab.coda.jevio.BaseStructure;
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.SSPData;
+import org.hps.recon.ecal.triggerbank.TIData;
+import org.hps.record.epics.EpicsEvioProcessor;
+import org.hps.record.epics.EpicsScalarData;
+import org.hps.record.evio.EvioEventUtilities;
+import org.hps.record.scalars.ScalarData;
+import org.hps.record.scalars.ScalarsEvioProcessor;
 import org.jlab.coda.jevio.EvioEvent;
-
 import org.lcsim.event.EventHeader;
 
-import org.hps.readout.ecal.daqconfig.EvioDAQParser;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.SSPData;
-import org.hps.readout.ecal.triggerbank.TIData;
-import org.hps.record.evio.EvioEventUtilities;
-
-
 /**
- * Build LCSim events from EVIO data.
+ * This is the {@link org.hps.record.LCSimEventBuilder} implementation for the Engineering Run and the Commissioning Run.
+ * <p>
+ * It has several modifications from the Test Run builder including different values for certain bank tags.
+ * <p>
+ * Additionally, this builder will write DAQ config information, EPICS control data, and scalar bank data into the output LCSim events if these banks are present in the EVIO data.
  *
  * @author Sho Uemura <[log in to unmask]>
  * @author Jeremy McCormick <[log in to unmask]>
  */
 public class LCSimEngRunEventBuilder extends LCSimTestRunEventBuilder {
-    
+
     TriggerConfigEvioReader triggerConfigReader = null;
-    
+
+    EpicsEvioProcessor epicsProcessor = new EpicsEvioProcessor();
+    EpicsScalarData epicsData;
+
+    ScalarsEvioProcessor scalarProcessor = new ScalarsEvioProcessor();
+    ScalarData scalarData;
+
     public LCSimEngRunEventBuilder() {
         ecalReader.setTopBankTag(0x25);
         ecalReader.setBotBankTag(0x27);
-        svtReader = new SvtEvioReader(); 
-        sspCrateBankTag = 0x2E; //A.C. modification after Sergey's confirmation
+        svtReader = new SvtEvioReader();
+        sspCrateBankTag = 0x2E; // A.C. modification after Sergey's confirmation
         sspBankTag = 0xe10c;
         intBanks = new ArrayList<IntBankDefinition>();
-        intBanks.add(new IntBankDefinition(SSPData.class, new int[]{sspCrateBankTag, sspBankTag}));
-        intBanks.add(new IntBankDefinition(TIData.class, new int[]{sspCrateBankTag, 0xe10a}));
+        intBanks.add(new IntBankDefinition(SSPData.class, new int[] { sspCrateBankTag, sspBankTag }));
+        intBanks.add(new IntBankDefinition(TIData.class, new int[] { sspCrateBankTag, 0xe10a }));
         // ecalReader = new ECalEvioReader(0x25, 0x27);
-        
         triggerConfigReader = new TriggerConfigEvioReader();
     }
 
@@ -53,6 +61,37 @@
     }
 
     @Override
+    public void readEvioEvent(EvioEvent evioEvent) {
+        super.readEvioEvent(evioEvent);
+
+        // Create EPICS data if this is an EPICS control event.
+        if (EvioEventUtilities.isEpicsEvent(evioEvent)) {
+            createEpicsScalarData(evioEvent);
+        }
+    }
+
+    /**
+     * Create and cache an {@link org.hps.record.epics.EpicsScalarData} object.
+     * @param evioEvent The EVIO event data.
+     */
+    void createEpicsScalarData(EvioEvent evioEvent) {
+        epicsProcessor.process(evioEvent);
+        epicsData = epicsProcessor.getEpicsScalarData();
+    }
+
+    /**
+     * Write EVIO scalar data into the LCSim event, if it exists.
+     * @param evioEvent The EVIO event data.
+     * @param lcsimEvent The output LCSim event.
+     */
+    void writeScalarData(EvioEvent evioEvent, EventHeader lcsimEvent) {
+        scalarProcessor.process(evioEvent);
+        if (scalarProcessor.getScalarData() != null) {
+            scalarProcessor.getScalarData().write(lcsimEvent);
+        }
+    }
+
+    @Override
     public EventHeader makeLCSimEvent(EvioEvent evioEvent) {
         if (!EvioEventUtilities.isPhysicsEvent(evioEvent)) {
             throw new RuntimeException("Not a physics event: event tag " + evioEvent.getHeader().getTag());
@@ -60,10 +99,10 @@
 
         // Create a new LCSimEvent.
         EventHeader lcsimEvent = getEventData(evioEvent);
-     
-        // Put DAQ Configuration info into lcsimEvent
-        triggerConfigReader.getDAQConfig(evioEvent,lcsimEvent);
-        
+
+        // Put DAQ Configuration info into lcsimEvent.
+        triggerConfigReader.getDAQConfig(evioEvent, lcsimEvent);
+
         // Make RawCalorimeterHit collection, combining top and bottom section
         // of ECal into one list.
         try {
@@ -72,16 +111,23 @@
             Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "Error making ECal hits", e);
         }
 
-        // Make SVT RawTrackerHits
+        // FIXME: This is commented out for now while SVT is not integrated.
+        // Make SVT RawTrackerHits.
         // try {
         // svtReader.makeHits(evioEvent, lcsimEvent);
         // } catch (Exception e) {
-        // Logger.getLogger(this.getClass().getName()).log(Level.SEVERE,
-        // "Error making SVT hits", e);
+        // Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "Error making SVT hits", e);
         // }
+
+        // Write the current EPICS data into this event.
+        if (epicsData != null) {
+            epicsData.write(lcsimEvent);
+            epicsData = null;
+        }
+
+        // Write scalars into the event, if they exist in this EVIO data.
+        writeScalarData(evioEvent, lcsimEvent);
+
         return lcsimEvent;
     }
-
-    
-        
-}
+}

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/LCSimTestRunEventBuilder.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/LCSimTestRunEventBuilder.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/LCSimTestRunEventBuilder.java	Wed Mar 25 14:43:27 2015
@@ -5,8 +5,9 @@
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
+
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.hps.record.LCSimEventBuilder;
 import org.hps.record.evio.EvioEventConstants;
 import org.hps.record.evio.EvioEventUtilities;
@@ -27,8 +28,8 @@
  */
 public class LCSimTestRunEventBuilder implements LCSimEventBuilder, ConditionsListener {
 
-    ECalEvioReader ecalReader = null;
-    AbstractSvtEvioReader svtReader = null;
+    protected ECalEvioReader ecalReader = null;
+    protected AbstractSvtEvioReader svtReader = null;
     protected long time = 0; //most recent event time (ns), taken from prestart and end events, and trigger banks (if any)
     protected int sspCrateBankTag = 0x1; //bank ID of the crate containing the SSP
     protected int sspBankTag = 0xe106; //SSP bank's tag

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/SVTHitWriter.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/SVTHitWriter.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/SVTHitWriter.java	Wed Mar 25 14:43:27 2015
@@ -1,29 +1,28 @@
 package org.hps.evio;
+
+import static org.hps.evio.EventConstants.SVT_BANK_NUMBER;
+import static org.hps.evio.EventConstants.SVT_BANK_TAG;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+//===> import org.hps.conditions.deprecated.SvtUtils;
+import org.hps.readout.svt.FpgaData;
+import org.hps.readout.svt.HPSSVTConstants;
+import org.hps.readout.svt.SVTData;
 import org.jlab.coda.jevio.DataType;
 import org.jlab.coda.jevio.EventBuilder;
 import org.jlab.coda.jevio.EvioBank;
 import org.jlab.coda.jevio.EvioException;
+import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.detector.tracker.silicon.HpsTestRunSiSensor;
-import org.lcsim.detector.tracker.silicon.SiSensor;
-import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.LCRelation;
 import org.lcsim.event.RawTrackerHit;
 import org.lcsim.geometry.Subdetector;
 import org.lcsim.lcio.LCIOConstants;
-//===> import org.hps.conditions.deprecated.SvtUtils;
-import org.hps.readout.svt.FpgaData;
-import org.hps.readout.svt.HPSSVTConstants;
-import org.hps.readout.svt.SVTData;
-
-import static org.hps.evio.EventConstants.SVT_BANK_NUMBER;
-import static org.hps.evio.EventConstants.SVT_BANK_TAG;
 
 /**
  *

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/TestRunSvtEvioReader.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/TestRunSvtEvioReader.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/TestRunSvtEvioReader.java	Wed Mar 25 14:43:27 2015
@@ -2,15 +2,12 @@
 
 import java.util.List;
 
+import org.hps.util.Pair;
 import org.jlab.coda.jevio.BaseStructure;
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.detector.tracker.silicon.HpsTestRunSiSensor;
 import org.lcsim.event.RawTrackerHit;
-import org.lcsim.event.base.BaseRawTrackerHit;
 import org.lcsim.geometry.Subdetector;
-import org.hps.util.Pair;
-
-import static org.hps.evio.EventConstants.TEST_RUN_SVT_BANK_TAG;
 
 /**
  *	Test run SVT EVIO reader used to convert SVT bank integer data to LCIO

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java	Wed Mar 25 14:43:27 2015
@@ -161,7 +161,9 @@
         if (event.hasCollection(SimCalorimeterHit.class, ecalCollectionName) && !event.get(SimCalorimeterHit.class, ecalCollectionName).isEmpty()) {
             mcParticles = event.getMCParticles();
             ecalHits = event.get(SimCalorimeterHit.class, ecalCollectionName);
-            trackerHits = event.get(SimTrackerHit.class, trackerCollectionName);
+            if (event.hasCollection(SimTrackerHit.class, trackerCollectionName)) {
+                trackerHits = event.get(SimTrackerHit.class, trackerCollectionName);
+            }
             if (event.hasCollection(SimTrackerHit.class, ecalScoringPlaneHitsCollectionName)) {
                 ecalScoringPlaneHits = event.get(SimTrackerHit.class, ecalScoringPlaneHitsCollectionName);
             }
@@ -170,7 +172,9 @@
             if (event.hasCollection(MCParticle.class)) {
                 triggerMCParticles = event.getMCParticles();
                 triggerECalHits = event.get(SimCalorimeterHit.class, ecalCollectionName);
-                triggerTrackerHits = event.get(SimTrackerHit.class, trackerCollectionName);
+                if (event.hasCollection(SimTrackerHit.class, trackerCollectionName)) {
+                    triggerTrackerHits = event.get(SimTrackerHit.class, trackerCollectionName);
+                }
                 if (event.hasCollection(SimTrackerHit.class, ecalScoringPlaneHitsCollectionName)) {
                     triggerECalScoringPlaneHits = event.get(SimTrackerHit.class, ecalScoringPlaneHitsCollectionName);
                 }

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/TriggerConfigEvioReader.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/TriggerConfigEvioReader.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/TriggerConfigEvioReader.java	Wed Mar 25 14:43:27 2015
@@ -5,7 +5,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.hps.readout.ecal.daqconfig.EvioDAQParser;
+import org.hps.recon.ecal.daqconfig.EvioDAQParser;
 import org.jlab.coda.jevio.BaseStructure;
 import org.jlab.coda.jevio.EvioEvent;
 import org.lcsim.event.EventHeader;

Modified: java/branches/prod/evio/src/main/java/org/hps/evio/TriggerDataWriter.java
 =============================================================================
--- java/branches/prod/evio/src/main/java/org/hps/evio/TriggerDataWriter.java	(original)
+++ java/branches/prod/evio/src/main/java/org/hps/evio/TriggerDataWriter.java	Wed Mar 25 14:43:27 2015
@@ -1,8 +1,9 @@
 package org.hps.evio;
 
 import java.util.List;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
+
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.jlab.coda.jevio.BaseStructure;
 import org.jlab.coda.jevio.DataType;
 import org.jlab.coda.jevio.EventBuilder;

Modified: java/branches/prod/monitoring-app/pom.xml
 =============================================================================
--- java/branches/prod/monitoring-app/pom.xml	(original)
+++ java/branches/prod/monitoring-app/pom.xml	Wed Mar 25 14:43:27 2015
@@ -111,6 +111,10 @@
         </dependency>
         <dependency>
             <groupId>org.hps</groupId>
+            <artifactId>hps-ecal-recon</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hps</groupId>
             <artifactId>hps-monitoring-drivers</artifactId>
         </dependency>       
         <dependency>
@@ -130,5 +134,20 @@
             <artifactId>jlfgr</artifactId>
             <version>1.0</version>
         </dependency>
+        <dependency>
+            <groupId>org.freehep</groupId>
+            <artifactId>freehep-jaida-remote</artifactId>
+            <version>3.4.11</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.jdom</groupId>
+                    <artifactId>jdom</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-math</groupId>
+                    <artifactId>commons-math</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
-</project>
+</project>

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/AbstractFieldsPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/AbstractFieldsPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/AbstractFieldsPanel.java	Wed Mar 25 14:43:27 2015
@@ -11,6 +11,7 @@
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
+import javax.swing.JComponent;
 import javax.swing.JFormattedTextField;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -136,7 +137,7 @@
         c.gridy = currY;
         c.insets = insets;
         c.anchor = GridBagConstraints.WEST;
-        JLabel waitModeLabel = new JLabel(name + ":");
+        JLabel waitModeLabel = new JLabel(name);
         waitModeLabel.setHorizontalAlignment(JLabel.LEFT);
         add(waitModeLabel, c);
 
@@ -278,6 +279,33 @@
         return configurationModel;
     }
     
+    /**
+     * Add a labeled JComponent to the panel.
+     * @param name The label text.
+     * @param component The component to add.
+     */
+    void addComponent(String name, JComponent component) {
+                
+        // Add the label.
+        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);
+
+        // Add the component.
+        c = new GridBagConstraints();
+        c.gridx = 1;
+        c.gridy = currY;
+        c.insets = insets;
+        c.anchor = GridBagConstraints.EAST;
+        add(component, c);
+
+        ++currY;
+    }
+    
     boolean accept(PropertyChangeEvent evt) {
         if (evt.getPropertyName().equals("ancestor")) {
             return false;

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Commands.java	Wed Mar 25 14:43:27 2015
@@ -34,14 +34,25 @@
     // Save a screenshot
     static final String SAVE_SCREENSHOT = "saveScreenshot";
     
-    // Save the plots
+    // Plotting actions
     static final String SAVE_PLOTS = "savePlots";
     static final String CLEAR_PLOTS = "resetPlots";
+    static final String SAVE_SELECTED_PLOTS = "saveSelectedPlots";
     
+    // Exit the application.
     static final String EXIT = "exit";
+           
+    // Log to file or standard print stream.
+    static final String LOG_TO_FILE = "logToFile";
+    static final String LOG_TO_TERMINAL = "logToTerminal";
     
-    static final String LOG_LEVEL_FILTER_CHANGED = "logLevelFilterChanged";
-        
+    static final String LOG_LEVEL_FILTER_CHANGED = "logLevelFilterChanged";    
+    
+    static final String CONDITIONS_TAG_CHANGED = "conditionsTagChanged";
+    
+    static final String START_AIDA_SERVER = "startAIDAServer";
+    static final String STOP_AIDA_SERVER = "stopAIDAServer";
+    
     ////////////////////////////////////////////    
     static final String BLOCKING_CHANGED = "blockingChanged";
     static final String CHOOSE_COMPACT_FILE = "chooseCompactFile";
@@ -59,10 +70,7 @@
     static final String FREEZE_CONDITIONS_CHANGED = "freezeConditionsChanged";
     
     static final String LOG_LEVEL_CHANGED = "logLevelChanged";
-    static final String LOG_TO_FILE = "logToFile";
-    static final String LOG_TO_FILE_CHANGED = "logToFileChanged";
-    static final String LOG_TO_TERMINAL = "logToTerminal";
-
+    
     static final String PROCESSING_STAGE_CHANGED = "processingStageChanged";    
     static final String SAVE_LOG_TABLE = "saveLogTable";            
     static final String SELECT_LOG_FILE = "logToFile";

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionSettingsPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionSettingsPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionSettingsPanel.java	Wed Mar 25 14:43:27 2015
@@ -1,20 +1,4 @@
 package org.hps.monitoring.application;
-
-import static org.hps.monitoring.application.Commands.BLOCKING_CHANGED;
-import static org.hps.monitoring.application.Commands.VERBOSE_CHANGED;
-import static org.hps.monitoring.application.Commands.WAIT_MODE_CHANGED;
-import static org.hps.monitoring.application.model.ConfigurationModel.BLOCKING_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.CHUNK_SIZE_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.ET_NAME_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.HOST_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.PORT_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.PRESCALE_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.QUEUE_SIZE_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.STATION_NAME_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.STATION_POSITION_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.VERBOSE_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.WAIT_MODE_PROPERTY;
-import static org.hps.monitoring.application.model.ConfigurationModel.WAIT_TIME_PROPERTY;
 
 import java.awt.GridBagLayout;
 import java.awt.Insets;
@@ -72,11 +56,11 @@
         portField.addPropertyChangeListener("value", this);
 
         blockingCheckBox = addCheckBox("Blocking", false, true);
-        blockingCheckBox.setActionCommand(BLOCKING_CHANGED);
+        blockingCheckBox.setActionCommand(Commands.BLOCKING_CHANGED);
         blockingCheckBox.addActionListener(this);
 
         verboseCheckBox = addCheckBox("Verbose", false, true);
-        verboseCheckBox.setActionCommand(VERBOSE_CHANGED);
+        verboseCheckBox.setActionCommand(Commands.VERBOSE_CHANGED);
         verboseCheckBox.addActionListener(this);
 
         stationNameField = addField("Station Name", 10);
@@ -92,7 +76,7 @@
         stationPositionField.addPropertyChangeListener("value", this);
 
         waitModeComboBox = addComboBox("Wait Mode", waitModes);
-        waitModeComboBox.setActionCommand(WAIT_MODE_CHANGED);
+        waitModeComboBox.setActionCommand(Commands.WAIT_MODE_CHANGED);
         waitModeComboBox.addActionListener(this);
 
         waitTimeField = addField("Wait Time [microseconds]", 8);
@@ -130,29 +114,29 @@
             configurationModel.removePropertyChangeListener(this);
             try {
                 Object value = evt.getNewValue();
-                if (evt.getPropertyName().equals(ET_NAME_PROPERTY)) {
+                if (evt.getPropertyName().equals(ConfigurationModel.ET_NAME_PROPERTY)) {
                     etNameField.setText((String) value);
-                } else if (evt.getPropertyName().equals(HOST_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.HOST_PROPERTY)) {
                     hostField.setText((String) value);
-                } else if (evt.getPropertyName().equals(PORT_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.PORT_PROPERTY)) {
                     portField.setText(value.toString());
-                } else if (evt.getPropertyName().equals(BLOCKING_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.BLOCKING_PROPERTY)) {
                     blockingCheckBox.setSelected((Boolean) value);
-                } else if (evt.getPropertyName().equals(VERBOSE_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.VERBOSE_PROPERTY)) {
                     verboseCheckBox.setSelected((Boolean) value);
-                } else if (evt.getPropertyName().equals(STATION_NAME_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.STATION_NAME_PROPERTY)) {
                     stationNameField.setText((String) value);
-                } else if (evt.getPropertyName().equals(CHUNK_SIZE_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.CHUNK_SIZE_PROPERTY)) {
                     chunkSizeField.setText(value.toString());
-                } else if (evt.getPropertyName().equals(QUEUE_SIZE_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.QUEUE_SIZE_PROPERTY)) {
                     queueSizeField.setText(value.toString());
-                } else if (evt.getPropertyName().equals(STATION_POSITION_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.STATION_POSITION_PROPERTY)) {
                     stationPositionField.setText(value.toString());
-                } else if (evt.getPropertyName().equals(WAIT_MODE_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.WAIT_MODE_PROPERTY)) {
                     waitModeComboBox.setSelectedItem(((Mode) value).name());
-                } else if (evt.getPropertyName().equals(WAIT_TIME_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.WAIT_TIME_PROPERTY)) {
                     waitTimeField.setText(value.toString());
-                } else if (evt.getPropertyName().equals(PRESCALE_PROPERTY)) {
+                } else if (evt.getPropertyName().equals(ConfigurationModel.PRESCALE_PROPERTY)) {
                     prescaleField.setText(value.toString());
                 }
             } finally {
@@ -166,18 +150,9 @@
      */
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
-        
         if (!accept(evt)) {
             return;
-        }
-        
-        //System.out.println("ConnectionSettingsPanel.propertyChange");
-        //System.out.println("  src: " + evt.getSource());
-        //System.out.println("  propName: " + evt.getPropertyName());
-        //System.out.println("  oldValue: " + evt.getOldValue());
-        //System.out.println("  newValue: " + evt.getNewValue());
-        //System.out.println("  propValue: " + evt.getPropagationId());
-        
+        }               
         Object source = evt.getSource();
         configurationModel.removePropertyChangeListener(this);
         try {
@@ -210,11 +185,11 @@
      */
     @Override
     public void actionPerformed(ActionEvent e) {
-        if (WAIT_MODE_CHANGED.equals(e.getActionCommand())) {
+        if (Commands.WAIT_MODE_CHANGED.equals(e.getActionCommand())) {
             configurationModel.setWaitMode(Mode.valueOf((String) waitModeComboBox.getSelectedItem()));
-        } else if (BLOCKING_CHANGED.equals(e.getActionCommand())) {
+        } else if (Commands.BLOCKING_CHANGED.equals(e.getActionCommand())) {
             configurationModel.setBlocking(blockingCheckBox.isSelected());
-        } else if (VERBOSE_CHANGED.equals(e.getActionCommand())) {
+        } else if (Commands.VERBOSE_CHANGED.equals(e.getActionCommand())) {
             configurationModel.setVerbose(verboseCheckBox.isSelected());
         }
     }

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/ConnectionStatusPanel.java	Wed Mar 25 14:43:27 2015
@@ -45,7 +45,6 @@
         setMinimumSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
 
         setLayout(new GridBagLayout());
-        // setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
 
         GridBagConstraints c = new GridBagConstraints();
         c.weightx = c.weighty = 1.0;

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/DatePanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/DatePanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/DatePanel.java	Wed Mar 25 14:43:27 2015
@@ -9,7 +9,7 @@
  */
 class DatePanel extends FieldPanel {
 
-    private SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM-dd-yyyy HH:mm:ss");
+    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
     DatePanel(String fieldName, String defaultValue, int size, boolean editable) {
         super(fieldName, defaultValue, size, editable);

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/EventButtonsPanel.java	Wed Mar 25 14:43:27 2015
@@ -80,10 +80,21 @@
             resumeButton.setEnabled(false);
             connectButton.setActionCommand(Commands.CONNECT);
             connectButton.setIcon(disconnectedIcon);
-        } else {
+            connectButton.setToolTipText("Start new session");
+            connectButton.setEnabled(true);
+        } else if (status.equals(ConnectionStatus.DISCONNECTING)) {
+            nextButton.setEnabled(false);
+            pauseButton.setEnabled(false);
+            resumeButton.setEnabled(false);
+            connectButton.setEnabled(false);
+        } else if (status.equals(ConnectionStatus.CONNECTED)) {
+            nextButton.setEnabled(false);            
             pauseButton.setEnabled(true);
+            resumeButton.setEnabled(false);
             connectButton.setActionCommand(Commands.DISCONNECT);
             connectButton.setIcon(connectedIcon);
+            connectButton.setToolTipText("Disconnect from session");
+            connectButton.setEnabled(true);
         }
     }       
     

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

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/JobSettingsPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/JobSettingsPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/JobSettingsPanel.java	Wed Mar 25 14:43:27 2015
@@ -1,7 +1,4 @@
 package org.hps.monitoring.application;
-
-import static org.hps.monitoring.application.Commands.*;
-import static org.hps.monitoring.application.model.ConfigurationModel.*;
 
 import java.awt.GridBagLayout;
 import java.awt.Insets;
@@ -18,11 +15,13 @@
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
 import javax.swing.filechooser.FileFilter;
 
 import org.hps.monitoring.application.model.ConfigurationModel;
 import org.hps.monitoring.application.model.SteeringType;
 import org.hps.monitoring.application.util.ResourceUtil;
+import org.hps.record.enums.ProcessingStage;
 import org.jdom.Document;
 import org.jdom.Element;
 import org.jdom.JDOMException;
@@ -32,22 +31,26 @@
  * This is the GUI panel for setting job parameters. It is connected to the global configuration via
  * a {@link org.hps.monitoring.model.ConfigurationModel} object.
  */
+// FIXME: Combo boxes should use explicit types.
 class JobSettingsPanel extends AbstractFieldsPanel {
 
     private JComboBox<?> steeringResourcesComboBox;
     private JTextField steeringFileField;
     private JComboBox<?> steeringTypeComboBox;
+    private JComboBox<ProcessingStage> processingStageComboBox;
     private JComboBox<String> detectorNameComboBox;
     private JTextField detectorAliasField;
+    private JComboBox<String> conditionsTagComboBox;
     private JComboBox<String> eventBuilderComboBox;
     private JTextField userRunNumberField;
     private JCheckBox freezeConditionsCheckBox;    
     private JTextField maxEventsField;
     private JCheckBox disconnectOnErrorCheckBox;
-    private JCheckBox disconnectOnEndRunCheckBox;
-    private JTextField logFileNameField;
+    private JCheckBox disconnectOnEndRunCheckBox;    
     private JComboBox<?> logLevelComboBox;
     private JCheckBox logToFileCheckbox;
+    private JTextField logFileNameField;
+    private JTextField aidaServerNameField;
            
     // The package where steering resources must be located.
     static final String STEERING_PACKAGE = "org/hps/steering/monitoring/";
@@ -68,17 +71,22 @@
     /**
      * Class constructor.
      */
-    JobSettingsPanel() {
-
-        super(new Insets(4, 2, 2, 4), true);
-        
+    JobSettingsPanel(ConfigurationModel model) {
+
+        super(new Insets(5, 3, 3, 5), true);
+        
+        setBorder(new EmptyBorder(10, 10, 10, 10));
+                
         setLayout(new GridBagLayout());
+        
+        // Listen on changes to the configuration which will then be automatically pushed to the GUI.
+        model.addPropertyChangeListener(this);
 
         steeringResourcesComboBox = addComboBoxMultiline("Steering File Resource", ResourceUtil.findSteeringResources(STEERING_PACKAGE));
-        steeringResourcesComboBox.setActionCommand(STEERING_RESOURCE_CHANGED);
+        steeringResourcesComboBox.setActionCommand(Commands.STEERING_RESOURCE_CHANGED);
         steeringResourcesComboBox.addActionListener(this);
         
-        steeringFileField = addField("Steering File", 35);
+        steeringFileField = addField("Steering File", 50);
         steeringFileField.addPropertyChangeListener("value", this);
         
         JButton steeringFileButton = addButton("Select Steering File");
@@ -86,15 +94,20 @@
         steeringFileButton.addActionListener(this);
         
         steeringTypeComboBox = addComboBox("Steering Type", new String[] { SteeringType.RESOURCE.name(), SteeringType.FILE.name() });
-        steeringTypeComboBox.setActionCommand(STEERING_TYPE_CHANGED);
+        steeringTypeComboBox.setActionCommand(Commands.STEERING_TYPE_CHANGED);
         steeringTypeComboBox.addActionListener(this);
         
+        processingStageComboBox = new JComboBox<ProcessingStage>(ProcessingStage.values());
+        addComponent("Processing Stage", processingStageComboBox);
+        processingStageComboBox.setActionCommand(Commands.PROCESSING_STAGE_CHANGED);
+        processingStageComboBox.addActionListener(this);
+        
         detectorNameComboBox = addComboBox("Detector Name", ResourceUtil.findDetectorNames());
-        detectorNameComboBox.setActionCommand(DETECTOR_NAME_CHANGED);
+        detectorNameComboBox.setActionCommand(Commands.DETECTOR_NAME_CHANGED);
         detectorNameComboBox.addActionListener(this);
         
         detectorAliasField = addField("Detector Resources Directory", "", 35, true);
-        detectorAliasField.setActionCommand(DETECTOR_ALIAS_CHANGED);
+        detectorAliasField.setActionCommand(Commands.DETECTOR_ALIAS_CHANGED);
         detectorAliasField.addPropertyChangeListener("value", this);
         detectorAliasField.addActionListener(this);
         
@@ -102,15 +115,23 @@
         compactXmlButton.setActionCommand(Commands.CHOOSE_COMPACT_FILE);
         compactXmlButton.addActionListener(this);
 
-        userRunNumberField = addField("User Run Number", "", 10, false);
+        userRunNumberField = addField("User Run Number", "", 10, true);
         userRunNumberField.addPropertyChangeListener("value", this);
-        userRunNumberField.setActionCommand(USER_RUN_NUMBER_CHANGED);
+        userRunNumberField.setActionCommand(Commands.USER_RUN_NUMBER_CHANGED);
         userRunNumberField.setEnabled(true);
         userRunNumberField.setEditable(true);
-        
+                
+        conditionsTagComboBox = addComboBox("Conditions Tag", ResourceUtil.getConditionsTags());
+        conditionsTagComboBox.addItem("");
+        conditionsTagComboBox.setSelectedItem("");
+        conditionsTagComboBox.setActionCommand(Commands.CONDITIONS_TAG_CHANGED);
+        conditionsTagComboBox.addActionListener(this);
+        conditionsTagComboBox.setEditable(false);
+        conditionsTagComboBox.setEnabled(true);
+                
         freezeConditionsCheckBox = addCheckBox("Freeze detector conditions", false, true);
         freezeConditionsCheckBox.addActionListener(this);
-        freezeConditionsCheckBox.setActionCommand(FREEZE_CONDITIONS_CHANGED);
+        freezeConditionsCheckBox.setActionCommand(Commands.FREEZE_CONDITIONS_CHANGED);
         
         maxEventsField = addField("Max Events", "-1", 10, false);
         maxEventsField.addPropertyChangeListener("value", this);
@@ -119,15 +140,15 @@
         
         eventBuilderComboBox = addComboBox("LCSim Event Builder", ResourceUtil.findEventBuilderClassNames());
         eventBuilderComboBox.setSize(24, eventBuilderComboBox.getPreferredSize().height);
-        eventBuilderComboBox.setActionCommand(EVENT_BUILDER_CHANGED);
+        eventBuilderComboBox.setActionCommand(Commands.EVENT_BUILDER_CHANGED);
         eventBuilderComboBox.addActionListener(this);
         
         disconnectOnErrorCheckBox = addCheckBox("Disconnect on error", false, true);
-        disconnectOnErrorCheckBox.setActionCommand(DISCONNECT_ON_ERROR_CHANGED);
+        disconnectOnErrorCheckBox.setActionCommand(Commands.DISCONNECT_ON_ERROR_CHANGED);
         disconnectOnErrorCheckBox.addActionListener(this);
 
         disconnectOnEndRunCheckBox = addCheckBox("Disconnect on end run", false, true);
-        disconnectOnEndRunCheckBox.setActionCommand(DISCONNECT_ON_END_RUN_CHANGED);
+        disconnectOnEndRunCheckBox.setActionCommand(Commands.DISCONNECT_ON_END_RUN_CHANGED);
         disconnectOnEndRunCheckBox.addActionListener(this);
 
         logLevelComboBox = addComboBox("Log Level", LOG_LEVELS);
@@ -136,11 +157,12 @@
                                             
         logToFileCheckbox = addCheckBox("Log to File", false, false);
         logToFileCheckbox.setEnabled(false);
-        logToFileCheckbox.setActionCommand(LOG_TO_FILE_CHANGED);
-        logToFileCheckbox.addActionListener(this);
-
-        logFileNameField = addField("Log File", "", "Full path to log file.", 30, false);
-        logFileNameField.addPropertyChangeListener("value", this);
+
+        logFileNameField = addField("Log File Name", "", "Full path to log file", 50, false);
+        logFileNameField.setEditable(false);
+        
+        aidaServerNameField = addField("AIDA Server Name", "", "Name of AIDA server", 30, true);
+        aidaServerNameField.addPropertyChangeListener("value", this);
     }
 
     @Override
@@ -152,8 +174,6 @@
      * Attaches the ActionListener from the main app to specific GUI components in this class.
      */
     public void addActionListener(ActionListener listener) {
-        logFileNameField.addActionListener(listener);
-        logToFileCheckbox.addActionListener(listener);
         steeringResourcesComboBox.addActionListener(listener);
         freezeConditionsCheckBox.addActionListener(listener);
     }
@@ -171,6 +191,7 @@
             try {
                 checkSteeringFile(file);
                 configurationModel.setSteeringFile(file.getCanonicalPath());
+                configurationModel.setSteeringType(SteeringType.FILE);
             } catch (IOException | JDOMException e) {
                 throw new RuntimeException("Error parsing the selected steering file.", e);
             }
@@ -234,53 +255,52 @@
 
     @Override
     public void actionPerformed(ActionEvent event) {
-
-        //System.out.println("JobSettingsPanel.actionPerformed - " + event.getActionCommand());
-        //System.out.println("  source: " + event.getSource());
-
         try {
             configurationModel.removePropertyChangeListener(this);
+            String command = event.getActionCommand();
             if (event.getActionCommand().equals(Commands.CHOOSE_STEERING_FILE)) {
                 chooseSteeringFile();
             } else if (event.getActionCommand().equals(Commands.CHOOSE_COMPACT_FILE)) {
                 chooseCompactFile();
-            } else if (DISCONNECT_ON_ERROR_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.DISCONNECT_ON_ERROR_CHANGED.equals(command)) {
                 configurationModel.setDisconnectOnError(disconnectOnErrorCheckBox.isSelected());
-            } else if (DISCONNECT_ON_END_RUN_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.DISCONNECT_ON_END_RUN_CHANGED.equals(command)) {
                 configurationModel.setDisconnectOnEndRun(disconnectOnEndRunCheckBox.isSelected());
-            } else if (STEERING_TYPE_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.STEERING_TYPE_CHANGED.equals(command)) {
                 configurationModel.setSteeringType(SteeringType.valueOf((String) steeringTypeComboBox.getSelectedItem()));
-            } else if (STEERING_RESOURCE_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.STEERING_RESOURCE_CHANGED.equals(command)) {
                 configurationModel.setSteeringResource((String) steeringResourcesComboBox.getSelectedItem());
-            } else if (LOG_TO_FILE_CHANGED.equals(event.getActionCommand())) {
-                configurationModel.setLogToFile(logToFileCheckbox.isSelected());
-            } else if (LOG_LEVEL_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.LOG_LEVEL_CHANGED.equals(command)) {
                 configurationModel.setLogLevel(Level.parse((String) logLevelComboBox.getSelectedItem()));
-            } else if (EVENT_BUILDER_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.EVENT_BUILDER_CHANGED.equals(command)) {
                 configurationModel.setEventBuilderClassName((String) eventBuilderComboBox.getSelectedItem());
-            } else if (DETECTOR_NAME_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.DETECTOR_NAME_CHANGED.equals(command)) {
                 try {
                     configurationModel.setDetectorName((String) detectorNameComboBox.getSelectedItem());
                 } catch (Exception exception) {
                     exception.printStackTrace();
                 }
-            } else if (FREEZE_CONDITIONS_CHANGED.equals(event.getActionCommand())) {
-                if (configurationModel.hasPropertyKey(USER_RUN_NUMBER_PROPERTY) && configurationModel.getUserRunNumber() != null) {
+            } else if (Commands.FREEZE_CONDITIONS_CHANGED.equals(command)) {
+                if (configurationModel.hasPropertyKey(ConfigurationModel.USER_RUN_NUMBER_PROPERTY) && configurationModel.getUserRunNumber() != null) {
                     configurationModel.setFreezeConditions(freezeConditionsCheckBox.isSelected());
                 } else {
                     throw new IllegalArgumentException("Conditions system may only be frozen if there is a valid user run number.");
                 }
-            } else if (DETECTOR_ALIAS_CHANGED.equals(event.getActionCommand())) {
+            } else if (Commands.DETECTOR_ALIAS_CHANGED.equals(command)) {
                 configurationModel.setDetectorName(detectorAliasField.getText());
-            }
+            } else if (Commands.CONDITIONS_TAG_CHANGED.equals(command)) {
+                configurationModel.setConditionsTag((String) conditionsTagComboBox.getSelectedItem());
+            } else if (Commands.PROCESSING_STAGE_CHANGED.equals(command)) {
+                configurationModel.setProcessingStage((ProcessingStage) processingStageComboBox.getSelectedItem());
+            } 
         } finally {
             configurationModel.addPropertyChangeListener(this);
         }
     }
 
     /**
-     * Updates the configuration with changes from the GUI component values. The changes from the
-     * GUI are distinguishable by their component object.
+     * Updates the configuration with changes from the GUI component values. 
+     * The changes from the GUI are distinguishable by their component object.
      */
     @Override
     public void propertyChange(PropertyChangeEvent evt) {                            
@@ -289,23 +309,18 @@
             Object source = evt.getSource();            
             if (source == steeringFileField) {
                 configurationModel.setSteeringFile(steeringFileField.getText());
-            } else if (source == logFileNameField) {
-                configurationModel.setLogFileName(logFileNameField.getText());
             } else if (source == userRunNumberField) {
                 // Is run number being reset to null or empty?
                 if (userRunNumberField.getText() == null || userRunNumberField.getText().isEmpty()) {
-                    // System.out.println("resetting user run number back to null");
                     // Update the model to null user run number and do not freeze the conditions system.
                     configurationModel.setUserRunNumber(null);
                     configurationModel.setFreezeConditions(false);
                 } else {
                     try {
-                        // System.out.println("setting new user run number " + evt.getNewValue());
                         // Parse the run number. Need to catch errors because it might be an invalid string.
                         int userRunNumber = Integer.parseInt(userRunNumberField.getText());
                         configurationModel.setUserRunNumber(userRunNumber);
                         configurationModel.setFreezeConditions(true);
-                        System.out.println("successfully set run number to userRunNumber");
                     } catch (NumberFormatException e) {
                         System.out.println("bad number format so ignoring user run number " + evt.getNewValue());
                         userRunNumberField.setText((String) evt.getOldValue());
@@ -314,8 +329,26 @@
                 }
             } else if (source == maxEventsField) {
                 configurationModel.setMaxEvents(Long.parseLong(maxEventsField.getText()));
-                System.out.println("setMaxEvents - " + configurationModel.getMaxEvents());
-            }
+                //System.out.println("setMaxEvents - " + configurationModel.getMaxEvents());
+            } else if (source == aidaServerNameField) {
+                configurationModel.setAIDAServerName(aidaServerNameField.getText());
+            } else if (evt.getPropertyName().equals(ConfigurationModel.LOG_TO_FILE_PROPERTY)) {
+                // This is getting the log to file prop change from the ConfigurationModel to update a read only component.
+                Boolean logToFile = (Boolean) evt.getNewValue();
+                if (logToFile != null) {
+                    logToFileCheckbox.setSelected(logToFile);
+                }
+            } else if (evt.getPropertyName().equals(ConfigurationModel.LOG_FILE_NAME_PROPERTY)) {
+                // This is getting the log file name prop change from the ConfigurationModel to update a read only component.
+                String logFileName = (String) evt.getNewValue();
+                if (logFileName != null && logFileName.length() > 0) {
+                    logFileNameField.setText(logFileName);
+                } else {
+                    logFileNameField.setText("");
+                }
+            } else if (evt.getPropertyName().equals(ConfigurationModel.CONDITIONS_TAG_PROPERTY)) {
+                conditionsTagComboBox.setSelectedItem(evt.getNewValue()); 
+            } 
         } finally {
             configurationModel.addPropertyChangeListener(this);
         }
@@ -328,57 +361,56 @@
     private class JobSettingsChangeListener implements PropertyChangeListener {
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
-            //System.out.println("JobSettingsChangeListener.propertyChange");
-            //System.out.println("  src: " + evt.getSource());
-            //System.out.println("  propName: " + evt.getPropertyName());
-            //System.out.println("  oldValue: " + evt.getOldValue());
-            //System.out.println("  newValue: " + evt.getNewValue());
-            //System.out.println("  propId: " + evt.getPropagationId());
             if (evt.getSource() instanceof ConfigurationModel) {
                 Object value = evt.getNewValue();
+                String property = evt.getPropertyName();
                 configurationModel.removePropertyChangeListener(this);
                 try {
-                    if (evt.getPropertyName().equals(DETECTOR_NAME_PROPERTY)) {
+                    if (property.equals(ConfigurationModel.DETECTOR_NAME_PROPERTY)) {
                         detectorNameComboBox.setSelectedItem((String) value);
-                    } else if (evt.getPropertyName().equals(DETECTOR_ALIAS_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.DETECTOR_ALIAS_PROPERTY)) {
                         detectorAliasField.setText((String) value);
-                    } else if (evt.getPropertyName().equals(DISCONNECT_ON_ERROR_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.DISCONNECT_ON_ERROR_PROPERTY)) {
                         disconnectOnErrorCheckBox.setSelected((Boolean) value);
-                    } else if (evt.getPropertyName().equals(DISCONNECT_ON_END_RUN_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.DISCONNECT_ON_END_RUN_PROPERTY)) {
                         disconnectOnEndRunCheckBox.setSelected((Boolean) value);
-                    } else if (evt.getPropertyName().equals(EVENT_BUILDER_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.EVENT_BUILDER_PROPERTY)) {
                         eventBuilderComboBox.setSelectedItem((String) value);
-                    } else if (evt.getPropertyName().equals(LOG_FILE_NAME_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.LOG_FILE_NAME_PROPERTY)) {
                         logFileNameField.setText((String) value);
-                    } else if (evt.getPropertyName().equals(LOG_LEVEL_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.LOG_LEVEL_PROPERTY)) {
                         logLevelComboBox.setSelectedItem(value.toString());
-                    } else if (evt.getPropertyName().equals(LOG_TO_FILE_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.LOG_TO_FILE_PROPERTY)) {
                         logToFileCheckbox.setSelected((Boolean) value);
-                    } else if (evt.getPropertyName().equals(STEERING_TYPE_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.STEERING_TYPE_PROPERTY)) {
                         steeringTypeComboBox.setSelectedIndex(((SteeringType) value).ordinal());
-                    } else if (evt.getPropertyName().equals(STEERING_FILE_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.STEERING_FILE_PROPERTY)) {
                         if (value != null) {
-                            steeringFileField.setText((String) value);
+                            steeringFileField.setText((String) evt.getNewValue());
                         } else {
                             // A null value here is actually okay and means this field should be reset to have no value.
                             steeringFileField.setText(null);
                         }
-                    } else if (evt.getPropertyName().equals(STEERING_RESOURCE_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.STEERING_RESOURCE_PROPERTY)) {
                         steeringResourcesComboBox.setSelectedItem(value);
-                    } else if (evt.getPropertyName().equals(USER_RUN_NUMBER_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.USER_RUN_NUMBER_PROPERTY)) {
                         if (value != null) {
                             userRunNumberField.setText(Integer.toString((int) value));
                         } else {
                             userRunNumberField.setText(null);
                         }
-                    } else if (evt.getPropertyName().equals(FREEZE_CONDITIONS_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.FREEZE_CONDITIONS_PROPERTY)) {
                         if (value != null) {
                             freezeConditionsCheckBox.setSelected((Boolean) value);
                         }
-                    } else if (evt.getPropertyName().equals(MAX_EVENTS_PROPERTY)) {
+                    } else if (property.equals(ConfigurationModel.MAX_EVENTS_PROPERTY)) {
                         if (value != null) {
                             maxEventsField.setText(value.toString());
                         }
+                    } else if (property.equals(ConfigurationModel.PROCESSING_STAGE_PROPERTY)) {
+                        processingStageComboBox.setSelectedItem(evt.getNewValue());
+                    } else if (property.equals(ConfigurationModel.AIDA_SERVER_NAME_PROPERTY)) {
+                        aidaServerNameField.setText((String) evt.getNewValue());
                     }
                 } finally {
                     configurationModel.addPropertyChangeListener(this);

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogLevelFilterComboBox.java	Wed Mar 25 14:43:27 2015
@@ -13,10 +13,10 @@
 import org.hps.monitoring.application.model.ConfigurationModel;
 
 /**
- * 
+ * This is a combo box used to filter the log table messages by level.
  * @author Jeremy McCormick <[log in to unmask]>
  */
-public class LogLevelFilterComboBox extends JComboBox<Level> implements ActionListener, PropertyChangeListener {
+class LogLevelFilterComboBox extends JComboBox<Level> implements ActionListener, PropertyChangeListener {
    
     ConfigurationModel configurationModel;
     

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogPanel.java	Wed Mar 25 14:43:27 2015
@@ -17,11 +17,16 @@
  * This is a simple GUI component for the log table and its controls.
  * @author Jeremy McCormick <[log in to unmask]>
  */
-public class LogPanel extends JPanel {
+class LogPanel extends JPanel{ 
 
     LogTable logTable;
+    LogLevelFilterComboBox logFilterComboBox;
+    
+    ConfigurationModel configurationModel;
         
     LogPanel(ConfigurationModel configurationModel, ActionListener listener) {
+        
+        this.configurationModel = configurationModel;
         
         setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
         
@@ -31,8 +36,8 @@
         controlsPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 5));
         
         JLabel label = new JLabel("Log Level Filter");
-        LogLevelFilterComboBox logFilterComboBox = new LogLevelFilterComboBox(configurationModel);
-        logFilterComboBox.setToolTipText("Messages below this level will be filtered out.");              
+        logFilterComboBox = new LogLevelFilterComboBox(configurationModel);
+        logFilterComboBox.setToolTipText("Messages below this level will be filtered out.");
         controlsPanel.add(label);        
         controlsPanel.add(logFilterComboBox);
         
@@ -51,5 +56,5 @@
                 
         add(controlsPanel, BorderLayout.PAGE_START);
         add(tablePane, BorderLayout.PAGE_END);
-    }    
+    }          
 }

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogTable.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogTable.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/LogTable.java	Wed Mar 25 14:43:27 2015
@@ -30,7 +30,7 @@
 
     Level filterLevel = Level.ALL;
     
-    final static SimpleDateFormat formatter = new SimpleDateFormat("MMMM-dd-yyyy HH:mm:ss.SSS");
+    final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             
     LogTable(ConfigurationModel configurationModel) {
         configurationModel.addPropertyChangeListener(this);
@@ -132,5 +132,5 @@
             filterLevel = (Level) event.getNewValue();
             model.fireTableDataChanged();
         }
-    }
+    }    
 }

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Main.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Main.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/Main.java	Wed Mar 25 14:43:27 2015
@@ -10,7 +10,6 @@
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.cli.PosixParser;
 import org.hps.monitoring.application.model.Configuration;
-
 
 /**
  * This is the front-end for running the monitoring app via a {@link #main(String[])} method.

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MenuBar.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MenuBar.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MenuBar.java	Wed Mar 25 14:43:27 2015
@@ -1,18 +1,4 @@
 package org.hps.monitoring.application;
-
-import static org.hps.monitoring.application.Commands.EXIT;
-import static org.hps.monitoring.application.Commands.CLOSE_FILE;
-import static org.hps.monitoring.application.Commands.OPEN_FILE;
-import static org.hps.monitoring.application.Commands.CLEAR_PLOTS;
-import static org.hps.monitoring.application.Commands.SAVE_PLOTS;
-import static org.hps.monitoring.application.Commands.LOAD_SETTINGS;
-import static org.hps.monitoring.application.Commands.LOAD_DEFAULT_SETTINGS;
-import static org.hps.monitoring.application.Commands.SAVE_SCREENSHOT;
-import static org.hps.monitoring.application.Commands.SAVE_SETTINGS;
-import static org.hps.monitoring.application.Commands.SHOW_SETTINGS;
-import static org.hps.monitoring.application.Commands.DEFAULT_WINDOW;
-import static org.hps.monitoring.application.Commands.MAXIMIZE_WINDOW;
-import static org.hps.monitoring.application.Commands.MINIMIZE_WINDOW;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -39,37 +25,39 @@
     JMenuItem closeFileItem;
     JMenuItem openFileItem;    
     JMenu settingsMenu;
-    ConfigurationModel configurationModel;
+    JMenuItem logItem;
+    JMenuItem serverItem;
+    ConfigurationModel configurationModel;    
     
     MenuBar(ConfigurationModel configurationModel, ConnectionStatusModel connectionModel, ActionListener listener) {
-        
-        // Do not need to listen for changes on this model.
-        this.configurationModel = configurationModel;
+         
+        this.configurationModel = configurationModel;        
+        this.configurationModel.addPropertyChangeListener(this);
         
         // Need to listen for connection status changes.
-        connectionModel.addPropertyChangeListener(this);                
-
+        connectionModel.addPropertyChangeListener(this);  
+        
         JMenu fileMenu = new JMenu("File");
         fileMenu.setMnemonic(KeyEvent.VK_F);
         add(fileMenu);
         
         openFileItem = new JMenuItem("Open File ...");
         openFileItem.setMnemonic(KeyEvent.VK_P);
-        openFileItem.setActionCommand(OPEN_FILE);
+        openFileItem.setActionCommand(Commands.OPEN_FILE);
         openFileItem.addActionListener(listener);
         openFileItem.setToolTipText("Open an EVIO or LCIO data file");
         fileMenu.add(openFileItem);
         
         closeFileItem = new JMenuItem("Close File");
         closeFileItem.setMnemonic(KeyEvent.VK_C);
-        closeFileItem.setActionCommand(CLOSE_FILE);
+        closeFileItem.setActionCommand(Commands.CLOSE_FILE);
         closeFileItem.addActionListener(listener);
         closeFileItem.setToolTipText("Close the current file data source");
         fileMenu.add(closeFileItem);
               
         JMenuItem exitItem = new JMenuItem("Exit");
         exitItem.setMnemonic(KeyEvent.VK_X);
-        exitItem.setActionCommand(EXIT);
+        exitItem.setActionCommand(Commands.EXIT);
         exitItem.addActionListener(listener);
         exitItem.setToolTipText("Exit from the application");
         fileMenu.add(exitItem);
@@ -80,7 +68,7 @@
         
         JMenuItem settingsItem = new JMenuItem("Open Settings Window ...");
         settingsItem.setMnemonic(KeyEvent.VK_O);
-        settingsItem.setActionCommand(SHOW_SETTINGS);
+        settingsItem.setActionCommand(Commands.SHOW_SETTINGS);
         settingsItem.addActionListener(listener);
         settingsItem.setToolTipText("Show settings dialog");
         settingsMenu.add(settingsItem);
@@ -88,21 +76,21 @@
         JMenuItem loadConfigItem = new JMenuItem("Load Settings ...");
         loadConfigItem.addActionListener(listener);
         loadConfigItem.setMnemonic(KeyEvent.VK_L);
-        loadConfigItem.setActionCommand(LOAD_SETTINGS);
+        loadConfigItem.setActionCommand(Commands.LOAD_SETTINGS);
         loadConfigItem.setToolTipText("Load settings from a properties file");
         settingsMenu.add(loadConfigItem);
 
         JMenuItem saveConfigItem = new JMenuItem("Save Settings ...");
         saveConfigItem.addActionListener(listener);
         saveConfigItem.setMnemonic(KeyEvent.VK_S);
-        saveConfigItem.setActionCommand(SAVE_SETTINGS);
+        saveConfigItem.setActionCommand(Commands.SAVE_SETTINGS);
         saveConfigItem.setToolTipText("Save configuration to a properties file");
         settingsMenu.add(saveConfigItem);
         
         JMenuItem defaultSettingsItem = new JMenuItem("Load Default Settings");
         defaultSettingsItem.addActionListener(listener);
         defaultSettingsItem.setMnemonic(KeyEvent.VK_D);
-        defaultSettingsItem.setActionCommand(LOAD_DEFAULT_SETTINGS);
+        defaultSettingsItem.setActionCommand(Commands.LOAD_DEFAULT_SETTINGS);
         defaultSettingsItem.setToolTipText("Load the default settings");
         settingsMenu.add(defaultSettingsItem);
         
@@ -112,7 +100,7 @@
         
         JMenuItem savePlotsItem = new JMenuItem("Save Plots ...");
         savePlotsItem.setMnemonic(KeyEvent.VK_S);
-        savePlotsItem.setActionCommand(SAVE_PLOTS);
+        savePlotsItem.setActionCommand(Commands.SAVE_PLOTS);
         savePlotsItem.addActionListener(listener);
         savePlotsItem.setEnabled(true);
         savePlotsItem.setToolTipText("Save plots to AIDA file");
@@ -120,7 +108,7 @@
 
         JMenuItem clearPlotsItem = new JMenuItem("Clear plots");
         clearPlotsItem.setMnemonic(KeyEvent.VK_C);
-        clearPlotsItem.setActionCommand(CLEAR_PLOTS);
+        clearPlotsItem.setActionCommand(Commands.CLEAR_PLOTS);
         clearPlotsItem.addActionListener(listener);
         clearPlotsItem.setEnabled(true);
         clearPlotsItem.setToolTipText("Clear the AIDA plots");
@@ -132,19 +120,35 @@
         
         JMenuItem screenshotItem = new JMenuItem("Save Screenshot ...");
         screenshotItem.setMnemonic(KeyEvent.VK_S);
-        screenshotItem.setActionCommand(SAVE_SCREENSHOT);
+        screenshotItem.setActionCommand(Commands.SAVE_SCREENSHOT);
         screenshotItem.addActionListener(listener);
         screenshotItem.setEnabled(true);
         screenshotItem.setToolTipText("Save a screenshot to a graphics file");
         toolsMenu.add(screenshotItem);
         
+        logItem = new JMenuItem("Log to File ...");
+        logItem.setMnemonic(KeyEvent.VK_R);
+        logItem.setActionCommand(Commands.LOG_TO_FILE);
+        logItem.addActionListener(listener);
+        logItem.setEnabled(true);
+        logItem.setToolTipText("Redirect System.out to a file instead of terminal");
+        toolsMenu.add(logItem);
+        
+        serverItem = new JMenuItem("Start AIDA Server ...");
+        serverItem.setMnemonic(KeyEvent.VK_A);
+        serverItem.setActionCommand(Commands.START_AIDA_SERVER);
+        serverItem.setEnabled(true);
+        serverItem.setToolTipText("Start AIDA RMI Server");
+        serverItem.addActionListener(listener);
+        toolsMenu.add(serverItem);
+        
         JMenu windowMenu = new JMenu("Window");
         windowMenu.setMnemonic(KeyEvent.VK_W);
         add(windowMenu);
         
         JMenuItem maximizeItem = new JMenuItem("Maximize");
         maximizeItem.setMnemonic(KeyEvent.VK_M);
-        maximizeItem.setActionCommand(MAXIMIZE_WINDOW);
+        maximizeItem.setActionCommand(Commands.MAXIMIZE_WINDOW);
         maximizeItem.addActionListener(listener);
         maximizeItem.setEnabled(true);
         maximizeItem.setToolTipText("Maximize the application window");
@@ -152,7 +156,7 @@
         
         JMenuItem minimizeItem = new JMenuItem("Minimize");
         minimizeItem.setMnemonic(KeyEvent.VK_I);
-        minimizeItem.setActionCommand(MINIMIZE_WINDOW);
+        minimizeItem.setActionCommand(Commands.MINIMIZE_WINDOW);
         minimizeItem.addActionListener(listener);
         minimizeItem.setEnabled(true);
         minimizeItem.setToolTipText("Minimize the application window");
@@ -160,68 +164,38 @@
         
         JMenuItem defaultsItem = new JMenuItem("Restore Defaults");
         defaultsItem.setMnemonic(KeyEvent.VK_D);
-        defaultsItem.setActionCommand(DEFAULT_WINDOW);
+        defaultsItem.setActionCommand(Commands.DEFAULT_WINDOW);
         defaultsItem.addActionListener(listener);
         defaultsItem.setEnabled(true);
         defaultsItem.setToolTipText("Restore the window defaults");
-        windowMenu.add(defaultsItem);        
-        
-        /*                       
-
-        JMenu logMenu = new JMenu("Log");
-        logMenu.setMnemonic(KeyEvent.VK_L);
-        add(logMenu);
-
-        logItem = new JMenuItem("Redirect to File ...");
-        logItem.setMnemonic(KeyEvent.VK_F);
-        logItem.setActionCommand(CHOOSE_LOG_FILE);
-        //logItem.addActionListener(this);
-        logItem.setEnabled(true);
-        logItem.setToolTipText("Redirect std out and err to a file.");
-        logMenu.add(logItem);
-
-        terminalItem = new JMenuItem("Redirect to Terminal");
-        terminalItem.setMnemonic(KeyEvent.VK_T);
-        terminalItem.setActionCommand(LOG_TO_TERMINAL);
-        //terminalItem.addActionListener(this);
-        terminalItem.setEnabled(false);
-        terminalItem.setToolTipText("Redirect std out and err back to the terminal.");
-        logMenu.add(terminalItem);
-
-        JMenuItem saveLogItem = new JMenuItem("Save Log Table to File ...");
-        saveLogItem.setMnemonic(KeyEvent.VK_S);
-        saveLogItem.setActionCommand(SAVE_LOG_TABLE);
-        //saveLogItem.addActionListener(this);
-        saveLogItem.setToolTipText("Save the log records to a tab delimited text file.");
-        logMenu.add(saveLogItem);
-
-        JMenuItem clearLogItem = new JMenuItem("Clear Log Table");
-        //clearLogItem.addActionListener(this);
-        clearLogItem.setMnemonic(KeyEvent.VK_C);
-        clearLogItem.setActionCommand(CLEAR_LOG_TABLE);
-        clearLogItem.setToolTipText("Clear the log table of all messages.");
-        logMenu.add(clearLogItem);
-
-        JMenu utilMenu = new JMenu("Util");
-        plotsMenu.setMnemonic(KeyEvent.VK_U);
-        add(utilMenu);
-
-        JMenuItem screenshotItem = new JMenuItem("Take a Screenshot ...");
-        screenshotItem.setMnemonic(KeyEvent.VK_N);
-        screenshotItem.setActionCommand(SCREENSHOT);
-        //screenshotItem.addActionListener(this);
-        screenshotItem.setToolTipText("Save a screenshot to file");
-        utilMenu.add(screenshotItem);
-        */
+        windowMenu.add(defaultsItem);
     }
 
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
-        if (evt.getPropertyName().equals(ConnectionStatusModel.CONNECTION_STATUS_PROPERTY)) {
-            ConnectionStatus status = (ConnectionStatus) evt.getNewValue();
-            boolean connected = status.equals(ConnectionStatus.CONNECTED);            
-            closeFileItem.setEnabled(!connected);
-            openFileItem.setEnabled(!connected);
+        configurationModel.removePropertyChangeListener(this);        
+        try {            
+            if (evt.getPropertyName().equals(ConnectionStatusModel.CONNECTION_STATUS_PROPERTY)) {
+                ConnectionStatus status = (ConnectionStatus) evt.getNewValue();
+                boolean connected = status.equals(ConnectionStatus.CONNECTED);
+                closeFileItem.setEnabled(!connected);
+                openFileItem.setEnabled(!connected);
+            } else if (evt.getPropertyName().equals(ConfigurationModel.LOG_TO_FILE_PROPERTY)) {
+                Boolean logToFile = (Boolean) evt.getNewValue();
+                if (logToFile == true) {
+                    // Toggle log item state to send to terminal.
+                    logItem.setText("Log to Terminal ...");
+                    logItem.setActionCommand(Commands.LOG_TO_TERMINAL);
+                    logItem.setToolTipText("Log messages to the terminal");
+                } else {
+                    // Toggle log item state to send to file.
+                    logItem.setText("Log to File ...");
+                    logItem.setActionCommand(Commands.LOG_TO_FILE);
+                    logItem.setToolTipText("Log messages to a file");
+                }
+            }
+        } finally {
+            configurationModel.addPropertyChangeListener(this);
         }
     }
 
@@ -234,6 +208,17 @@
                 closeFileItem.setEnabled(false);
             }
         }        
-    }
-    
+    }    
+    
+    void startAIDAServer() {
+        serverItem.setActionCommand(Commands.STOP_AIDA_SERVER);
+        serverItem.setText("Stop AIDA Server");
+        serverItem.setToolTipText("Stop the remote AIDA server");
+    }
+    
+    void stopAIDAServer() {
+        serverItem.setActionCommand(Commands.START_AIDA_SERVER);
+        serverItem.setText("Start AIDA Server");
+        serverItem.setToolTipText("Start the remote AIDA server");
+    }
 }

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/MonitoringApplication.java	Wed Mar 25 14:43:27 2015
@@ -3,18 +3,20 @@
 import hep.aida.jfree.AnalysisFactory;
 import hep.aida.jfree.plotter.PlotterRegion;
 import hep.aida.jfree.plotter.PlotterRegionListener;
-
-import java.awt.Dimension;
-import java.awt.Rectangle;
-import java.awt.Robot;
-import java.awt.Toolkit;
+import hep.aida.ref.remote.rmi.client.RmiStoreFactory;
+
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.List;
@@ -32,25 +34,25 @@
 import javax.swing.filechooser.FileFilter;
 import javax.swing.filechooser.FileNameExtensionFilter;
 
-import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.monitoring.application.DataSourceComboBox.DataSourceItem;
 import org.hps.monitoring.application.LogTable.LogRecordModel;
 import org.hps.monitoring.application.model.Configuration;
 import org.hps.monitoring.application.model.ConfigurationModel;
+import org.hps.monitoring.application.model.ConnectionStatus;
 import org.hps.monitoring.application.model.ConnectionStatusModel;
 import org.hps.monitoring.application.model.RunModel;
+import org.hps.monitoring.application.util.AIDAServer;
 import org.hps.monitoring.application.util.DialogUtil;
 import org.hps.monitoring.application.util.ErrorHandler;
 import org.hps.monitoring.application.util.EvioFileFilter;
 import org.hps.monitoring.application.util.TableExporter;
 import org.hps.monitoring.plotting.MonitoringAnalysisFactory;
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
-import org.hps.monitoring.subsys.StatusCode;
 import org.hps.monitoring.subsys.SystemStatus;
-import org.hps.monitoring.subsys.SystemStatusListener;
 import org.hps.monitoring.subsys.SystemStatusRegistry;
 import org.hps.record.composite.CompositeRecordProcessor;
 import org.hps.record.enums.DataSourceType;
+import org.lcsim.conditions.ConditionsListener;
 import org.lcsim.util.Driver;
 import org.lcsim.util.aida.AIDA;
 import org.lcsim.util.log.DefaultLogFormatter;
@@ -58,12 +60,11 @@
 /**
  * This is the primary class that implements the monitoring GUI application.
  * It should not be used directly.  Instead the {@link Main} class should be
- * used from the command line or via the supplied script built automatically 
- * by Maven.
+ * used from the command line.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
-final class MonitoringApplication implements ActionListener, PropertyChangeListener, SystemStatusListener {
+final class MonitoringApplication implements ActionListener, PropertyChangeListener {
 
     // Statically initialize logging, which will be fully setup later.
     static final Logger logger;
@@ -73,13 +74,16 @@
     static final Level DEFAULT_LEVEL = Level.ALL;
 
     // Default log stream.
-    PrintStream logStream = System.out;
+    MonitoringApplicationStreamHandler streamHandler;
+    LogHandler logHandler;
+    PrintStream sysOut = System.out;
+    PrintStream sysErr = System.err;
     
     // Application error handling.
-    final ErrorHandler errorHandler;
+    ErrorHandler errorHandler;
    
     // The main GUI components inside a JFrame.
-    final MonitoringApplicationFrame frame;    
+    MonitoringApplicationFrame frame;    
     
     // The primary data models.
     final RunModel runModel = new RunModel();
@@ -98,6 +102,9 @@
     // Filters for opening files.
     static final FileFilter lcioFilter = new FileNameExtensionFilter("LCIO files", "slcio");
     static final EvioFileFilter evioFilter = new EvioFileFilter();
+    
+    AIDAServer server = new AIDAServer("hps-monitoring-app");
+    static final RmiStoreFactory rsf = new RmiStoreFactory();
             
     /**
      * Default log handler.
@@ -125,43 +132,120 @@
     LogTable getLogTable() {
         return frame.logPanel.logTable;
     }
-             
+    
+    class MonitoringApplicationStreamHandler extends StreamHandler {
+        
+        MonitoringApplicationStreamHandler(PrintStream ps) {
+            super(ps, new DefaultLogFormatter());
+        }
+        
+        public void publish(LogRecord record) {
+            super.publish(record);
+            flush();
+        }
+        
+        public void setOutputStream(OutputStream out) {
+            super.setOutputStream(out);
+        }        
+    }
+                 
     /**
      * Instantiate and show the monitoring application with the given configuration.
      * @param configuration The Configuration object containing application settings.
      */
     MonitoringApplication(Configuration configuration) {
-                
-        // Setup the main GUI component.
-        frame = new MonitoringApplicationFrame(this);
-        
-        // Setup the error handler.
-        errorHandler = new ErrorHandler(frame, logger);
+        
+        try {
+        
+            // Setup the main GUI component.
+            frame = new MonitoringApplicationFrame(this);
+            
+            // Add window listener to perform clean shutdown.
+            frame.addWindowListener(new WindowListener() {
+
+                @Override
+                public void windowOpened(WindowEvent e) {
+                }
+
+                @Override
+                public void windowClosing(WindowEvent e) {
+                }
+
+                @Override
+                public void windowClosed(WindowEvent e) {
+                    exit();
+                }
+
+                @Override
+                public void windowIconified(WindowEvent e) {
+                }
+
+                @Override
+                public void windowDeiconified(WindowEvent e) {
+                }
+
+                @Override
+                public void windowActivated(WindowEvent e) {
+                }
+
+                @Override
+                public void windowDeactivated(WindowEvent e) {
+                }
+            });
+        
+            // Setup the error handler.
+            errorHandler = new ErrorHandler(frame, logger);
                        
-        // Add this class as a listener on the configuration model.
-        configurationModel.addPropertyChangeListener(this);
-        
-        // Setup the logger.
-        setupLogger();
+            // Add this class as a listener on the configuration model.
+            configurationModel.addPropertyChangeListener(this);
+        
+            // Setup the logger.
+            setupLogger();
                
-        // Setup AIDA plotting and connect it to the GUI.
-        setupAida();
-        
-        // Set the configuration.
-        if (configuration != null) {
-            // There was a user specified configuration.
-            this.configuration = configuration;
-        } else {
-            // Use the default configuration.
-            this.configuration = new Configuration(DEFAULT_CONFIGURATION);
-        }
+            // Setup AIDA plotting and connect it to the GUI.
+            setupAida();
+        
+            // Set the configuration.
+            if (configuration != null) {
+                // There was a user specified configuration.
+                this.configuration = configuration;
+            } else {
+                // Use the default configuration.
+                this.configuration = new Configuration(DEFAULT_CONFIGURATION);
+            }
                                       
-        // Load the configuration.
-        loadConfiguration(this.configuration);
-                
-        logger.info("application initialized successfully");
-    }
-    
+            // Load the configuration.
+            loadConfiguration(this.configuration);
+        
+            frame.setEnabled(true);
+        
+            logger.info("application initialized successfully");
+        
+        } catch (Exception e) {
+            // Don't use the ErrorHandler here because we don't know that it initialized successfully.
+            System.err.println("MonitoringApplication failed to initialize without errors!");
+            DialogUtil.showErrorDialog(null, "Error Starting Monitoring Application", "Monitoring application failed to initialize.");
+            e.printStackTrace();
+            System.exit(1);
+        }        
+    }
+    
+    /**
+     * Setup the logger.
+     */
+    void setupLogger() {
+        logger.setUseParentHandlers(false);        
+        logHandler = new LogHandler();
+        logger.addHandler(logHandler);
+        streamHandler = new MonitoringApplicationStreamHandler(System.out);
+        logger.addHandler(streamHandler);
+        for (Handler handler : logger.getHandlers()) {
+            handler.setLevel(DEFAULT_LEVEL);
+        }
+        logger.setLevel(DEFAULT_LEVEL);
+        logger.info("logging initialized");
+    }
+        
     /**
      * Static utility method for creating new instance.
      * @param configuration The application settings.
@@ -177,7 +261,9 @@
      */
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
-        // TODO: Handle log level configuration change here.
+        if (evt.getPropertyName().equals(ConfigurationModel.LOG_LEVEL_PROPERTY)) {
+            setLogLevel();
+        }
     }
     
     /**
@@ -186,50 +272,59 @@
      */
     public void actionPerformed(ActionEvent e) {
 
-        String cmd = e.getActionCommand();
-        if (Commands.CONNECT.equals(cmd)) {
+        logger.finest("actionPerformed - " + e.getActionCommand());
+        
+        String command = e.getActionCommand();
+        if (Commands.CONNECT.equals(command)) {
             startSession();
-        } else if (Commands.DISCONNECT.equals(cmd)) {
-            processing.stop();
-        } else if (Commands.SAVE_PLOTS.equals(cmd)) {
+        } else if (Commands.DISCONNECT.equals(command)) {
+            runDisconnectThread();
+        } else if (Commands.SAVE_PLOTS.equals(command)) {
             savePlots();
-        } else if (Commands.EXIT.equals(cmd)) {
-            exit();
-        } else if (Commands.PAUSE.equals(cmd)) { 
+        } else if (Commands.EXIT.equals(command)) {
+            // This will trigger the window closing action that cleans everything up.
+            frame.dispose();
+        } else if (Commands.PAUSE.equals(command)) { 
             processing.pause();
-        } else if (Commands.NEXT.equals(cmd)) {
+        } else if (Commands.NEXT.equals(command)) {
             processing.next();
-        } else if (Commands.RESUME.equals(cmd)) {
+        } else if (Commands.RESUME.equals(command)) {
             processing.resume();
-        } else if (Commands.SHOW_SETTINGS.equals(cmd)) {
+        } else if (Commands.SHOW_SETTINGS.equals(command)) {
             showSettingsDialog();
-        } else if (Commands.LOAD_SETTINGS.equals(cmd)) {
+        } else if (Commands.LOAD_SETTINGS.equals(command)) {
             loadSettings();
-        } else if (Commands.SAVE_SETTINGS.equals(cmd)) {
+        } else if (Commands.SAVE_SETTINGS.equals(command)) {
             saveSettings();
-        }  else if (Commands.CLEAR_PLOTS.equals(cmd)) {
+        }  else if (Commands.CLEAR_PLOTS.equals(command)) {
             clearPlots();
-        } else if (Commands.LOAD_DEFAULT_SETTINGS.equals(cmd)) {
+        } else if (Commands.LOAD_DEFAULT_SETTINGS.equals(command)) {
             loadDefaultSettings();
-        } else if (Commands.OPEN_FILE.equals(cmd)) {
+        } else if (Commands.OPEN_FILE.equals(command)) {
             openFile();
-        } else if (Commands.DEFAULT_WINDOW.equals(cmd)) {
+        } else if (Commands.DEFAULT_WINDOW.equals(command)) {
             restoreDefaultWindow();
-        } else if (Commands.MAXIMIZE_WINDOW.equals(cmd)) {
+        } else if (Commands.MAXIMIZE_WINDOW.equals(command)) {
             maximizeWindow();
-        } else if (Commands.MINIMIZE_WINDOW.equals(cmd)) {
+        } else if (Commands.MINIMIZE_WINDOW.equals(command)) {
             minimizeWindow();
-        } else if (Commands.CLOSE_FILE.equals(cmd)) {
+        } else if (Commands.CLOSE_FILE.equals(command)) {
             closeFile();
-        } else if (Commands.SAVE_SCREENSHOT.equals(cmd)) {
+        } else if (Commands.SAVE_SCREENSHOT.equals(command)) {
             saveScreenshot();
-        } else if (Commands.LOG_LEVEL_CHANGED.equals(cmd)) {
-            setLogLevel();
-        } else if (Commands.SAVE_LOG_TABLE.equals(cmd)) {
+        } else if (Commands.SAVE_LOG_TABLE.equals(command)) {
             saveLogTable();
-        } else if (Commands.CLEAR_LOG_TABLE.equals(cmd)) {
+        } else if (Commands.CLEAR_LOG_TABLE.equals(command)) {
             getLogRecordModel().clear();
-        }        
+        } else if (Commands.LOG_TO_FILE.equals(command)) {
+            chooseLogFile();
+        } else if (Commands.LOG_TO_TERMINAL.equals(command)) {
+            logToTerminal();
+        } else if (Commands.START_AIDA_SERVER.equals(command)) {
+            startAIDAServer();
+        } else if (Commands.STOP_AIDA_SERVER.equals(command)) {
+            stopAIDAServer();
+        }
     }    
     
     /**
@@ -255,26 +350,7 @@
         // Perform global configuration of the JFreeChart back end.
         AnalysisFactory.configure();
     }
-    
-    /**
-     * Setup the logger.
-     */
-    void setupLogger() {
-        logger.setUseParentHandlers(false);
-        logger.addHandler(new LogHandler());
-        logger.addHandler(new StreamHandler(logStream, new DefaultLogFormatter()) {
-            public void publish(LogRecord record) {
-                super.publish(record);
-                flush();
-            }
-        });
-        for (Handler handler : logger.getHandlers()) {
-            handler.setLevel(DEFAULT_LEVEL);
-        }
-        logger.setLevel(DEFAULT_LEVEL);
-        logger.info("logging initialized");
-    }
-            
+                
     /**
      * This method sets the configuration on the model, which fires a change for every property.
      * @param configuration The new configuration.
@@ -296,11 +372,14 @@
      * Reset the plots and clear the tabs in the plot window.
      */
     void resetPlots() {
-
+        
+        // Clear global list of registered plotters.
+        MonitoringPlotFactory.getPlotterRegistry().clear();  
+        
         // Clear the static AIDA tree in case plots are hanging around from previous sessions.
         AIDA.defaultInstance().clearAll();
 
-        // Reset plot panel which removes all tabs.
+        // Reset plot panel which removes all its tabs.
         frame.plotPanel.reset();
         
         logger.info("plots were cleared");
@@ -310,43 +389,20 @@
      * Configure the system status monitor panel for a new job.
      */
     void setupSystemStatusMonitor() {
+        
         // Clear the system status monitor table.
-        frame.systemStatusTable.getTableModel().clear();
+        frame.systemStatusPanel.clear();
 
         // Get the global registry of SystemStatus objects.
         SystemStatusRegistry registry = SystemStatusRegistry.getSystemStatusRegistery();
 
         // Process the SystemStatus objects.
         for (SystemStatus systemStatus : registry.getSystemStatuses()) {
-            // Add a row to the table for every SystemStatus.
-            frame.systemStatusTable.getTableModel().addSystemStatus(systemStatus);
-
-            // Add this class as a listener so all status changes can be logged.
-            systemStatus.addListener(this);
+            // This will add the status to the two tables.
+            frame.systemStatusPanel.addSystemStatus(systemStatus);
         }
         
         logger.info("system status monitor initialized successfully");
-    }
-    
-    /**
-     * Hook for logging all status changes from the system status monitor.
-     */
-    @Override
-    public void statusChanged(SystemStatus status) {
-
-        // Choose the appropriate log level.
-        Level level = Level.FINE;
-        if (status.getStatusCode().equals(Level.WARNING)) {
-            level = Level.WARNING;
-        } else if (status.getStatusCode().ordinal() >= StatusCode.ERROR.ordinal()) {
-            level = Level.SEVERE;
-        }
-        
-        // Log all status changes.
-        logger.log(level, "STATUS, " + "subsys: " + status.getSubsystem() + ", " 
-                + "code: " + status.getStatusCode().name() 
-                + ", " + "descr: " + status.getDescription() 
-                + ", " + "mesg: " + status.getMessage());
     }
     
     /**
@@ -368,13 +424,18 @@
 
             // List of extra composite record processors including the updater for the RunPanel.
             List<CompositeRecordProcessor> processors = new ArrayList<CompositeRecordProcessor>();
-            processors.add(frame.runPanel.new RunPanelUpdater());
-            
+            processors.add(frame.dashboardPanel.new EventDashboardUpdater());
+            
+            // Add Driver to update the trigger diagnostics tables.
             List<Driver> drivers = new ArrayList<Driver>();
             drivers.add(frame.triggerPanel.new TriggerDiagnosticGUIDriver());
-            
-            // Initialize event processing with the list of processors and reference to the application.
-            processing = new EventProcessing(this, processors, drivers);
+
+            // Add listener to push conditions changes to conditions panel.
+            List<ConditionsListener> conditionsListeners = new ArrayList<ConditionsListener>();
+            conditionsListeners.add(frame.conditionsPanel.new ConditionsPanelListener());
+            
+            // Instantiate the event processing wrapper.
+            processing = new EventProcessing(this, processors, drivers, conditionsListeners);
             
             // Connect to the ET system, if applicable.
             processing.connect();
@@ -387,7 +448,7 @@
             // Setup the system status monitor table.
             setupSystemStatusMonitor();
                                             
-            // Start the event processing thread.
+            // Start the event processing thread.            
             processing.start();            
             
             logger.info("new session successfully initialized");
@@ -406,17 +467,17 @@
     }
            
     /**
-     * Exit from the application.
+     * Exit from the application from exit menu item or hitting close window button.
      */
     void exit() {        
-        if (processing != null && processing.isActive()) {
+        if (connectionModel.isConnected()) {
             processing.stop();
         }
-        frame.setVisible(false);
+        logHandler.setLevel(Level.OFF);
         logger.info("exiting the application");
-        logger.getHandlers()[0].flush();
+        streamHandler.flush();
         System.exit(0);
-    }              
+    }
             
     /**
      * Save AIDA plots to a file using a file chooser.
@@ -570,6 +631,7 @@
     /**
      * Save a screenshot to a file using a file chooser.
      */
+    // FIXME: This might need to be on a new thread to allow the GUI to redraw w/o chooser visible.
     void saveScreenshot() {
         JFileChooser fc = new JFileChooser();
         fc.setAcceptAllFileFilterUsed(false);
@@ -584,8 +646,17 @@
             if (!fileName.endsWith("." + format)) {
                 fileName += "." + format;
             }
+            frame.repaint();
+            Object lock = new Object();
+            synchronized (lock) {
+                try {
+                    lock.wait(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
             writeScreenshot(fileName, format);
-            DialogUtil.showInfoDialog(frame, "Screenshot Saved", "Screenshot was saved to file.");
+            DialogUtil.showInfoDialog(frame, "Screenshot Saved", "Screenshot was saved to file" + '\n' + fileName);
             logger.info("saved screenshot to " + fileName);
         }
     }
@@ -595,15 +666,13 @@
      * @param fileName The name of the output file.
      */
     void writeScreenshot(String fileName, String format) {
-        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-        Rectangle screenRectangle = new Rectangle(screenSize);
+        BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_RGB);
+        frame.paint(image.getGraphics()); 
         try {
-            Robot robot = new Robot();
-            BufferedImage image = robot.createScreenCapture(screenRectangle);
             ImageIO.write(image, format, new File(fileName));
-        } catch (Exception e) {
-            errorHandler.setError(e).setMessage("Failed to take screenshot.").printStackTrace().log().showErrorDialog();
-        }
+        } catch (IOException e) {
+            errorHandler.setError(e).setMessage("Failed to save screenshot.").printStackTrace().log().showErrorDialog();
+        }        
     }            
     
     /**
@@ -644,4 +713,129 @@
     void saveLogTable() {
         saveTable(frame.logPanel.logTable);
     }
+        
+    /**
+     * Redirect <code>System.out</code> and <code>System.err</code> to file chosen
+     * by a file chooser.
+     */
+    void chooseLogFile() {
+        JFileChooser fc = new JFileChooser();
+        fc.setAcceptAllFileFilterUsed(false);
+        fc.setDialogTitle("Save Log Messages to File");       
+        fc.setCurrentDirectory(new File("."));
+        int r = fc.showSaveDialog(frame);
+        if (r == JFileChooser.APPROVE_OPTION) {            
+            String fileName = fc.getSelectedFile().getPath();
+            if (new File(fileName).exists()) {
+                DialogUtil.showErrorDialog(frame, "File Exists", "File already exists.");
+            } else {
+                logToFile(new File(fileName));
+            }
+        }        
+    }
+    
+    /**
+     * Redirect <code>System.out</code> and <code>System.err</code> to a file.
+     * @param file The output log file.
+     * @throws FileNotFoundException if the file does not exist.
+     */
+    void logToFile(File file) {
+        try {
+            
+            // Create the output file stream.
+            PrintStream fileStream = new PrintStream(new FileOutputStream(file.getPath()));
+            System.setOut(fileStream);
+            System.setErr(fileStream);
+            
+            // Flush the current handler, but do NOT close here or System.out gets clobbered!
+            streamHandler.flush();
+            
+            // Replace the current handler with one using the file stream.
+            logger.removeHandler(streamHandler);
+            streamHandler = new MonitoringApplicationStreamHandler(fileStream);
+            streamHandler.setLevel(logger.getLevel());
+            logger.addHandler(streamHandler);
+            
+            // Set the properties on the model.
+            configurationModel.setLogFileName(file.getPath());
+            configurationModel.setLogToFile(true);
+            
+            logger.info("Saving log messages to " + configurationModel.getLogFileName());
+            DialogUtil.showInfoDialog(frame, "Logging to File", 
+                    "Log messages redirected to file" + '\n' + configurationModel.getLogFileName());
+            
+        } catch (FileNotFoundException e) {
+            errorHandler.setError(e).log().showErrorDialog();
+        }
+    }      
+    
+    /**
+     * Send <code>System.out</code> and <code>System.err</code> back to the terminal, 
+     * e.g. if they were previously sent to a file.
+     */
+    void logToTerminal() {
+        
+        // Reset System.out and err back to original streams.
+        System.setOut(sysOut);
+        System.setErr(sysErr);
+        
+        // Flush and close the current handler, which is using a file stream.
+        streamHandler.flush();
+        streamHandler.close();
+        
+        // Replace the handler with the one printing to the terminal.
+        logger.removeHandler(streamHandler);               
+        streamHandler = new MonitoringApplicationStreamHandler(System.out);
+        streamHandler.setLevel(logger.getLevel());
+        logger.addHandler(streamHandler);
+        
+        logger.log(Level.INFO, "log messages redirected to terminal");
+        
+        // Update the model to indicate logging to file has been disabled.
+        configurationModel.setLogToFile(false);
+        
+        DialogUtil.showInfoDialog(frame, "Log to Terminal", "Log messages will be sent to the terminal.");
+    }    
+    
+    /**
+     * Start the AIDA server instance.
+     */
+    void startAIDAServer() {
+        if (configurationModel.hasValidProperty(ConfigurationModel.AIDA_SERVER_NAME_PROPERTY)) {
+            server.setName(configurationModel.getAIDAServerName());
+        }
+        boolean started = server.start();
+        if (started) {
+            frame.menu.startAIDAServer();
+            logger.info("AIDA server started at " + server.getName());
+            DialogUtil.showInfoDialog(frame, "AIDA Server Started", "The remote AIDA server started successfully.");
+        } else {
+            logger.warning("AIDA server failed to start");
+            DialogUtil.showErrorDialog(frame, "Failed to Start AIDA Server", "The remote AIDA server failed to start.");
+        }
+    }
+    
+    /**
+     * Stop the AIDA server instance.
+     */
+    void stopAIDAServer() {
+        server.disconnect();
+        frame.menu.stopAIDAServer();
+        logger.info("AIDA server was stopped");
+        DialogUtil.showInfoDialog(frame, "AIDA Server Stopped", "The AIDA server was stopped.");
+    }    
+    
+    /**
+     * 
+     */
+    void runDisconnectThread() {
+        new Thread() {
+            public void run() {
+                logger.fine("disconnect thread is running ...");
+                connectionModel.setConnectionStatus(ConnectionStatus.DISCONNECTING);
+                MonitoringApplication.this.processing.stop();
+                logger.fine("disconnect thread finished!");
+            }
+        }.run();
+    }
 }

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

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/PlotInfoPanel.java	Wed Mar 25 14:43:27 2015
@@ -15,10 +15,10 @@
 import hep.aida.ref.function.FunctionDispatcher;
 import hep.aida.ref.function.FunctionListener;
 
+import java.awt.Color;
 import java.awt.Component;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.EventObject;
@@ -27,6 +27,8 @@
 import java.util.TimerTask;
 
 import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JList;
 import javax.swing.JPanel;
@@ -38,15 +40,17 @@
 /**
  * <p>
  * This is a GUI component for showing the statistics and other information about an AIDA plot
- * when it is clicked on in the monitoring app.
+ * when it is clicked on in the monitoring application.
  * <p>
- * The information is updated dynamically via the <code>AIDAObserver</code> API on the AIDA object.
+ * The information in the table is updated dynamically via the <code>AIDAObserver</code> API on the AIDA object.
  */
 class PlotInfoPanel extends JPanel implements AIDAListener, ActionListener, FunctionListener {
 
     JComboBox<Object> plotComboBox;
     JTable infoTable = new JTable();
     DefaultTableModel model;
+    JButton saveButton;
+    
     PlotterRegion currentRegion;
     Object currentObject;
     static final int INSET_SIZE = 5;
@@ -63,11 +67,18 @@
      */
     @SuppressWarnings("unchecked")
     PlotInfoPanel() {
-
-        setLayout(new GridBagLayout());
-        setBorder(BorderFactory.createEmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE));
-
-        GridBagConstraints c;
+                
+        setLayout(new FlowLayout(FlowLayout.LEFT));
+
+        JPanel leftPanel = new JPanel();
+        leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
+           
+        JPanel buttonPanel = new JPanel();
+        saveButton = new JButton("Save Plots ...");
+        saveButton.setActionCommand(Commands.SAVE_SELECTED_PLOTS);
+        buttonPanel.add(saveButton);       
+        //c.anchor = GridBagConstraints.NORTHWEST;
+        leftPanel.add(buttonPanel);
 
         plotComboBox = new JComboBox<Object>();
         plotComboBox.setActionCommand(PLOT_SELECTED);
@@ -85,26 +96,17 @@
             }
         });
         plotComboBox.addActionListener(this);
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 0;
-        c.fill = GridBagConstraints.HORIZONTAL;
-        c.insets = new Insets(0, 0, INSET_SIZE, 0);
-        add(plotComboBox, c);
-
+        leftPanel.add(plotComboBox);
+        
         String data[][] = new String[0][0];
         model = new DefaultTableModel(data, COLUMN_NAMES);
         infoTable.setModel(model);
-
-        // FIXME: Are these adequate column size settings? Could prob be bigger...
         infoTable.getColumn("Field").setMinWidth(25);
         infoTable.getColumn("Value").setMinWidth(20);
-
-        c = new GridBagConstraints();
-        c.gridx = 0;
-        c.gridy = 1;
-        c.fill = GridBagConstraints.BOTH;
-        add(infoTable, c);
+        infoTable.setMinimumSize(new Dimension(100, 200));
+        leftPanel.add(infoTable);
+        
+        add(leftPanel);
     }
 
     /**

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

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SettingsPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SettingsPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SettingsPanel.java	Wed Mar 25 14:43:27 2015
@@ -31,7 +31,7 @@
         this.parent = parent;
         
         connectionPanel = new ConnectionSettingsPanel();        
-        jobPanel = new JobSettingsPanel();
+        jobPanel = new JobSettingsPanel(configurationModel);
         
         // Push configuration to sub-components.
         connectionPanel.setConfigurationModel(configurationModel);
@@ -55,7 +55,6 @@
         add(Box.createRigidArea(new Dimension(1, 5)));
         JPanel buttonsPanel = new JPanel();
         buttonsPanel.add(okayButton);
-        //buttonsPanel.add(defaultsButton);
         buttonsPanel.setLayout(new FlowLayout());
         add(buttonsPanel);
         add(Box.createRigidArea(new Dimension(1, 5)));

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/SystemStatusTable.java	Wed Mar 25 14:43:27 2015
@@ -1,8 +1,5 @@
 package org.hps.monitoring.application;
 
-import static org.hps.monitoring.application.model.SystemStatusTableModel.*;
-
-import java.awt.Color;
 import java.awt.Component;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -19,8 +16,7 @@
 import org.hps.monitoring.subsys.StatusCode;
 
 /**
- * A GUI window for showing changes to {@link org.hps.monitoring.subsys.SystemStatus} objects using
- * a <code>JTable</code>.
+ * This table shows the current state of {@link org.hps.monitoring.subsys.SystemStatus} objects.
  */
 class SystemStatusTable extends JTable {
 
@@ -39,34 +35,13 @@
 
                 // Color code the cell by its status.
                 StatusCode statusCode = StatusCode.valueOf((String) value);
-                if (statusCode.ordinal() >= StatusCode.ERROR.ordinal()) {
-                    // Any type of error is red.
-                    label.setBackground(Color.RED);
-                } else if (statusCode.equals(StatusCode.WARNING)) {
-                    // Warnings are yellow.
-                    label.setBackground(Color.YELLOW);
-                } else if (statusCode.equals(StatusCode.OKAY)) {
-                    // Okay is green.
-                    label.setBackground(Color.GREEN);
-                } else if (statusCode.equals(StatusCode.OFFLINE)) {
-                    // Offline is orange.
-                    label.setBackground(Color.ORANGE);
-                } else if (statusCode.equals(StatusCode.UNKNOWN)) {
-                    // Unknown is gray.
-                    label.setBackground(Color.GRAY);
-                } else if (statusCode.equals(StatusCode.CLEARED)) {
-                    // Cleared is light gray.
-                    label.setBackground(Color.LIGHT_GRAY);
-                } else {
-                    // Default is white, though this shouldn't happen!
-                    label.setBackground(Color.WHITE);
-                }
+                label.setBackground(statusCode.getColor());
                 return label;
             }
         });
 
         // Date formatting for last changed.
-        getColumnModel().getColumn(LAST_CHANGED_COL).setCellRenderer(new DefaultTableCellRenderer() {
+        getColumnModel().getColumn(SystemStatusTableModel.LAST_CHANGED_COL).setCellRenderer(new DefaultTableCellRenderer() {
 
             final SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM-dd-yyyy HH:mm:ss.SSS");
 
@@ -80,16 +55,16 @@
         });
 
         // Button for clearing system statuses.
-        getColumnModel().getColumn(RESET_COL).setCellRenderer(new ButtonRenderer("Clear"));
+        getColumnModel().getColumn(SystemStatusTableModel.RESET_COL).setCellRenderer(new ButtonRenderer("Clear"));
         addMouseListener(new JTableButtonMouseListener(this));
         getColumn("Clearable").setWidth(0);
         getColumn("Clearable").setMinWidth(0);
         getColumn("Clearable").setMaxWidth(0);
 
         // Column widths.
-        getColumnModel().getColumn(ACTIVE_COL).setPreferredWidth(8);
-        getColumnModel().getColumn(STATUS_COL).setPreferredWidth(10);
-        getColumnModel().getColumn(SYSTEM_COL).setPreferredWidth(10);
+        getColumnModel().getColumn(SystemStatusTableModel.ACTIVE_COL).setPreferredWidth(8);
+        getColumnModel().getColumn(SystemStatusTableModel.STATUS_COL).setPreferredWidth(10);
+        getColumnModel().getColumn(SystemStatusTableModel.SYSTEM_COL).setPreferredWidth(10);
         // TODO: Add default width setting for every column.
 
         setAutoCreateRowSorter(true);
@@ -109,7 +84,7 @@
         }
 
         public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
-            boolean clearable = (Boolean) table.getModel().getValueAt(row, CLEARABLE_COL);
+            boolean clearable = (Boolean) table.getModel().getValueAt(row, SystemStatusTableModel.CLEARABLE_COL);
             if (clearable)
                 return this;
             else

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/TriggerDiagnosticsPanel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/TriggerDiagnosticsPanel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/TriggerDiagnosticsPanel.java	Wed Mar 25 14:43:27 2015
@@ -21,7 +21,7 @@
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
-public class TriggerDiagnosticsPanel extends JPanel {
+class TriggerDiagnosticsPanel extends JPanel {
 
     JTabbedPane tabs = new JTabbedPane();
     ClusterTablePanel clusterPanel = new ClusterTablePanel();
@@ -70,9 +70,7 @@
                 for (DiagnosticUpdatable update : updateList) {
                     update.updatePanel(snapshot);
                 }
-            } else {
-                System.out.println("no diag snapshot in event");
-            }
+            } 
         }
         
         void setDiagnosticCollectionName(String name) {

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConfigurationModel.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConfigurationModel.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConfigurationModel.java	Wed Mar 25 14:43:27 2015
@@ -1,6 +1,5 @@
 package org.hps.monitoring.application.model;
 
-import java.io.File;
 import java.util.logging.Level;
 
 import org.hps.record.enums.DataSourceType;
@@ -16,6 +15,8 @@
     Configuration configuration;    
     
     // Job setting properties.
+    public static final String AIDA_SERVER_NAME_PROPERTY = "AIDAServerName";
+    public static final String CONDITIONS_TAG_PROPERTY = "ConditionsTag";
     public static final String DETECTOR_NAME_PROPERTY = "DetectorName";
     public static final String DETECTOR_ALIAS_PROPERTY = "DetectorAlias";
     public static final String DISCONNECT_ON_ERROR_PROPERTY = "DisconnectOnError";
@@ -104,18 +105,14 @@
         firePropertyChange(STEERING_TYPE_PROPERTY, oldValue, getSteeringType());
     }
 
-    public File getSteeringFile() {
-        if (configuration.hasKey(STEERING_FILE_PROPERTY)) {
-            return new File(configuration.get(STEERING_FILE_PROPERTY));
-        } else {
-            return null;
-        }
+    public String getSteeringFile() {        
+        return configuration.get(STEERING_FILE_PROPERTY);
     }
 
     public void setSteeringFile(String steeringFile) {
-        File oldValue = getSteeringFile();
+        String oldValue = getSteeringFile();
         configuration.set(STEERING_FILE_PROPERTY, steeringFile);
-        firePropertyChange(STEERING_FILE_PROPERTY, oldValue, getSteeringFile().getPath());
+        firePropertyChange(STEERING_FILE_PROPERTY, oldValue, getSteeringFile());
     }
 
     public String getSteeringResource() {
@@ -391,6 +388,26 @@
        
     public String getEtPath() {
         return getEtName() + "@" + getHost() + ":" + getPort();
+    }
+    
+    public void setConditionsTag(String conditionsTag) {
+        String oldValue = getConditionsTag();
+        configuration.set(CONDITIONS_TAG_PROPERTY, conditionsTag);
+        firePropertyChange(CONDITIONS_TAG_PROPERTY, oldValue, getConditionsTag());
+    }     
+    
+    public String getConditionsTag() {
+        return configuration.get(CONDITIONS_TAG_PROPERTY);
+    }
+    
+    public void setAIDAServerName(String AIDAServerName) {
+        String oldValue = getAIDAServerName();
+        configuration.set(AIDA_SERVER_NAME_PROPERTY, AIDAServerName);
+        firePropertyChange(AIDA_SERVER_NAME_PROPERTY, oldValue, getAIDAServerName());
+    } 
+    
+    public String getAIDAServerName() {
+        return configuration.get(AIDA_SERVER_NAME_PROPERTY);
     }
 
     public void remove(String property) {

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/model/ConnectionStatus.java	Wed Mar 25 14:43:27 2015
@@ -10,6 +10,7 @@
 public enum ConnectionStatus {
 
     DISCONNECTED(Color.RED),
+    DISCONNECTING(Color.YELLOW),
     CONNECTED(Color.GREEN);
     
     Color color;    

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

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

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ErrorHandler.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ErrorHandler.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ErrorHandler.java	Wed Mar 25 14:43:27 2015
@@ -85,7 +85,7 @@
      * @return This object.
      */
     public ErrorHandler log() {
-        logger.log(Level.SEVERE, message);
+        logger.log(Level.SEVERE, message, error);
         return this;
     }
 

Modified: java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ResourceUtil.java
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ResourceUtil.java	(original)
+++ java/branches/prod/monitoring-app/src/main/java/org/hps/monitoring/application/util/ResourceUtil.java	Wed Mar 25 14:43:27 2015
@@ -14,6 +14,7 @@
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
+import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.record.LCSimEventBuilder;
 import org.reflections.Reflections;
 
@@ -116,4 +117,8 @@
         Collections.sort(detectorNames);
         return detectorNames.toArray(new String[detectorNames.size()]);
     }        
+    
+    public static String[] getConditionsTags() {
+        return DatabaseConditionsManager.getInstance().getTags().toArray(new String[] {});
+    }
 }

Modified: java/branches/prod/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop	(original)
+++ java/branches/prod/monitoring-app/src/main/resources/org/hps/monitoring/config/default_config.prop	Wed Mar 25 14:43:27 2015
@@ -2,8 +2,9 @@
 # Monitoring Application configuration
 
 # job settings
+AIDAServerName=hps-monitoring-app
 DetectorName=HPS-Proposal2014-v8-6pt6
-DisconnectOnError=false
+DisconnectOnError=true
 DisconnectOnEndRun=true
 EventBuilderClassName=org.hps.evio.LCSimEngRunEventBuilder
 #LogFileName=
@@ -19,20 +20,18 @@
 DataSourceType=ET_SERVER
 #DataSourcePath=
 ProcessingStage=LCIO
-
+ 
 # ET connection settings
+# FIXME: These should really all be prepended by 'Et' so their purpose is clear.
 Blocking=false
 EtName=ETBuffer
 ChunkSize=1
 Host=localhost
 Port=11111
 StationPosition=1
-# Prescale was 1
-Prescale=0
+Prescale=1
 QueueSize=0
 StationName=MY_STATION
 Verbose=false
 WaitMode=TIMED
-WaitTime=1000000000
-
-SaveLayout=false;
+WaitTime=1000000000

Modified: java/branches/prod/monitoring-app/src/main/scripts/evio_file_producer.sh
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/scripts/evio_file_producer.sh	(original)
+++ java/branches/prod/monitoring-app/src/main/scripts/evio_file_producer.sh	Wed Mar 25 14:43:27 2015
@@ -14,6 +14,6 @@
 
 # Run the file producer, sending any additional arguments to the command.
 #prod="java -classpath $classpath org.hps.evio.EvioFileProducer -e ${eviofile} -f ETBuffer -host localhost -s 10000 -d 100 $@"
-prod="java -classpath $classpath org.hps.record.evio.EvioFileProducer -e ${eviofile} -f ETBuffer -host localhost -s 100000 $@"
+prod="java -classpath $classpath org.hps.record.evio.EvioFileProducer -e ${eviofile} -f ETBuffer -host localhost -s 200000 $@"
 echo $prod
 exec $prod

Modified: java/branches/prod/monitoring-app/src/main/scripts/start_et_ring.sh
 =============================================================================
--- java/branches/prod/monitoring-app/src/main/scripts/start_et_ring.sh	(original)
+++ java/branches/prod/monitoring-app/src/main/scripts/start_et_ring.sh	Wed Mar 25 14:43:27 2015
@@ -15,6 +15,6 @@
 fi
 
 # Start the ET ring, sending any script arguments to the end of the command.
-server="java -Xmx1024m -classpath $classpath org.jlab.coda.et.apps.StartEt -f $buffer_file -s 100000 -v $@" 
+server="java -Xmx1024m -classpath $classpath org.jlab.coda.et.apps.StartEt -f $buffer_file -s 200000 -v $@" 
 echo "$server"
 exec $server

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java	Wed Mar 25 14:43:27 2015
@@ -5,13 +5,14 @@
 import hep.aida.IHistogramFactory;
 import hep.aida.IPlotter;
 import hep.aida.IPlotterFactory;
+import hep.aida.IPlotterStyle;
+import hep.aida.ref.plotter.style.registry.StyleRegistry;
 
+import java.awt.Color;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.lcsim.detector.identifier.IIdentifier;
-import org.lcsim.detector.identifier.IIdentifierHelper;
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.RawTrackerHit;
@@ -19,32 +20,34 @@
 import org.lcsim.util.Driver;
 
 /**
- *	This Driver makes plots of sensor occupancies across a run. It is intended to
- * 	be used with the monitoring system.
+ * This Driver makes plots of sensor occupancies across a run. It is intended to be used with the
+ * monitoring system.
  *
- *  @author Jeremy McCormick <[log in to unmask]>
- *	@author Omar Moreno <[log in to unmask]>
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Omar Moreno <[log in to unmask]>
  */
 public class SensorOccupancyPlotsDriver extends Driver {
 
     // TODO: Add documentation
     // TODO: Set plot styles
-	
-	static IHistogramFactory histogramFactory = IAnalysisFactory.create().createHistogramFactory(null);
-	IPlotterFactory plotterFactory = IAnalysisFactory.create().createPlotterFactory();
-	
-	protected Map<String, IPlotter> plotters = new HashMap<String, IPlotter>(); 
-	protected Map<HpsSiSensor, IHistogram1D> occupancyPlots = new HashMap<HpsSiSensor, IHistogram1D>(); 
-	protected Map<HpsSiSensor, int[]> occupancyMap = new HashMap<HpsSiSensor, int[]>(); 
+
+    static IHistogramFactory histogramFactory = IAnalysisFactory.create().createHistogramFactory(null);
+    IPlotterFactory plotterFactory = IAnalysisFactory.create().createPlotterFactory();
+    static StyleRegistry styleRegistry = StyleRegistry.getStyleRegistry();
+
+    protected Map<String, IPlotter> plotters = new HashMap<String, IPlotter>();
+    protected Map<HpsSiSensor, IHistogram1D> occupancyPlots = new HashMap<HpsSiSensor, IHistogram1D>();
+    protected Map<HpsSiSensor, int[]> occupancyMap = new HashMap<HpsSiSensor, int[]>();
     private List<HpsSiSensor> sensors;
-	
+
     private static final String SUBDETECTOR_NAME = "Tracker";
     private String rawTrackerHitCollectionName = "SVTRawTrackerHits";
 
     private int eventCount = 0;
     private int eventRefreshRate = 1;
 
-    public SensorOccupancyPlotsDriver() {}
+    public SensorOccupancyPlotsDriver() {
+    }
 
     public void setRawTrackerHitCollectionName(String rawTrackerHitCollectionName) {
         this.rawTrackerHitCollectionName = rawTrackerHitCollectionName;
@@ -56,83 +59,92 @@
 
     private int computePlotterRegion(HpsSiSensor sensor) {
 
-		if (sensor.getLayerNumber() < 7) {
-			if (sensor.isTopLayer()) {
-				return 6*(sensor.getLayerNumber() - 1); 
-			} else { 
-				return 6*(sensor.getLayerNumber() - 1) + 1;
-			} 
-		} else { 
-		
-			if (sensor.isTopLayer()) {
-				if (sensor.getSide() == HpsSiSensor.POSITRON_SIDE) {
-					return 6*(sensor.getLayerNumber() - 7) + 2;
-				} else { 
-					return 6*(sensor.getLayerNumber() - 7) + 3;
-				}
-			} else if (sensor.isBottomLayer()) {
-				if (sensor.getSide() == HpsSiSensor.POSITRON_SIDE) {
-					return 6*(sensor.getLayerNumber() - 7) + 4;
-				} else {
-					return 6*(sensor.getLayerNumber() - 7) + 5;
-				}
-			}
-		}
-		
-		return -1; 
+        if (sensor.getLayerNumber() < 7) {
+            if (sensor.isTopLayer()) {
+                return 6 * (sensor.getLayerNumber() - 1);
+            } else {
+                return 6 * (sensor.getLayerNumber() - 1) + 1;
+            }
+        } else {
+
+            if (sensor.isTopLayer()) {
+                if (sensor.getSide() == HpsSiSensor.POSITRON_SIDE) {
+                    return 6 * (sensor.getLayerNumber() - 7) + 2;
+                } else {
+                    return 6 * (sensor.getLayerNumber() - 7) + 3;
+                }
+            } else if (sensor.isBottomLayer()) {
+                if (sensor.getSide() == HpsSiSensor.POSITRON_SIDE) {
+                    return 6 * (sensor.getLayerNumber() - 7) + 4;
+                } else {
+                    return 6 * (sensor.getLayerNumber() - 7) + 5;
+                }
+            }
+        }
+
+        return -1;
     }
 
     protected void detectorChanged(Detector detector) {
-    	
-		sensors 
-			= detector.getSubdetector(SUBDETECTOR_NAME).getDetectorElement().findDescendants(HpsSiSensor.class);
-   
+
+        sensors = detector.getSubdetector(SUBDETECTOR_NAME).getDetectorElement().findDescendants(HpsSiSensor.class);
+
         if (sensors.size() == 0) {
             throw new RuntimeException("No sensors were found in this detector.");
         }
 
         plotters.put("Occupancy", plotterFactory.create("Occupancy"));
-		plotters.get("Occupancy").createRegions(6,6);
-		
-		for (HpsSiSensor sensor : sensors) {
-			occupancyPlots.put(sensor, histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy", 640, 0, 640)); 
-			plotters.get("Occupancy").region(this.computePlotterRegion(sensor))
-									 .plot(occupancyPlots.get(sensor));
+        plotters.get("Occupancy").createRegions(6, 6);
+
+        for (HpsSiSensor sensor : sensors) {
+            occupancyPlots.put(sensor, histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy", 640, 0, 640));
+            plotters.get("Occupancy").region(this.computePlotterRegion(sensor)).plot(occupancyPlots.get(sensor), createPlotterStyle());
             occupancyMap.put(sensor, new int[640]);
-		}
-		
-		for (IPlotter plotter : plotters.values()) { 
-			plotter.show();
-		}
+        }
+
+        for (IPlotter plotter : plotters.values()) {
+            plotter.show();
+        }
     }
 
     public void process(EventHeader event) {
-        
-    	if (!event.hasCollection(RawTrackerHit.class, rawTrackerHitCollectionName))
-    		return;
 
-    	eventCount++;
-    	
+        if (!event.hasCollection(RawTrackerHit.class, rawTrackerHitCollectionName))
+            return;
+
+        eventCount++;
+
         // Get RawTrackerHit collection from event.
         List<RawTrackerHit> rawHits = event.get(RawTrackerHit.class, rawTrackerHitCollectionName);
 
-         // Increment strip hit count.
-         for (RawTrackerHit rawHit : rawHits) {
-        	 int[] strips = occupancyMap.get((HpsSiSensor) rawHit.getDetectorElement());
-             strips[rawHit.getIdentifierFieldValue("strip")] += 1;
-         }
+        // Increment strip hit count.
+        for (RawTrackerHit rawHit : rawHits) {
+            int[] strips = occupancyMap.get((HpsSiSensor) rawHit.getDetectorElement());
+            strips[rawHit.getIdentifierFieldValue("strip")] += 1;
+        }
 
-         // Plot strip occupancies.
-         if (eventCount % eventRefreshRate == 0) {
-        	 for (HpsSiSensor sensor : sensors) {
-                 int[] strips = occupancyMap.get(sensor);
-                 for (int i = 0; i < strips.length; i++) {
-                     double stripOccupancy = (double) strips[i] / (double) eventCount;
-                     if (stripOccupancy != 0) {
-                     	occupancyPlots.get(sensor).fill(i, stripOccupancy);
-                     }
-                 }
-             }
-         }
+        // Plot strip occupancies.
+        if (eventCount % eventRefreshRate == 0) {
+            for (HpsSiSensor sensor : sensors) {
+                int[] strips = occupancyMap.get(sensor);
+                for (int i = 0; i < strips.length; i++) {
+                    double stripOccupancy = (double) strips[i] / (double) eventCount;
+                    if (stripOccupancy != 0) {
+                        occupancyPlots.get(sensor).fill(i, stripOccupancy);
+                    }
+                }
+            }
+        }
+    }
+    
+    static IPlotterStyle createPlotterStyle() {
+        IPlotterStyle style = styleRegistry.getStore("DefaultStyleStore").getStyle("DefaultHistogram1DStyle");
+        style.dataStyle().lineStyle().setVisible(false);
+        style.dataStyle().outlineStyle().setVisible(false);
+        style.dataStyle().fillStyle().setVisible(true);
+        style.dataStyle().fillStyle().setColor("blue");
+        style.legendBoxStyle().setVisible(false);
+        style.dataStyle().errorBarStyle().setVisible(false);
+        return style;
     }
 }

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtTimingInPlots.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtTimingInPlots.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtTimingInPlots.java	Wed Mar 25 14:43:27 2015
@@ -9,6 +9,7 @@
 import hep.aida.IHistogram1D;
 import hep.aida.IPlotter;
 import hep.aida.IPlotterFactory;
+import hep.aida.IPlotterStyle;
 
 import org.lcsim.util.Driver; 
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
@@ -17,8 +18,8 @@
 import org.lcsim.event.LCRelation;
 import org.lcsim.event.RawTrackerHit;
 import org.lcsim.geometry.Detector;
-
 import org.hps.recon.tracking.FittedRawTrackerHit;
+import org.hps.recon.tracking.ShapeFitParameters;
 
 /**
  *  Monitoring driver that will be used when 'timing in' the SVT.
@@ -27,14 +28,24 @@
  *  @author Omar Moreno <[log in to unmask]>
  */
 public class SvtTimingInPlots extends Driver {
-	
+
     // TODO: Add documentation
     // TODO: Set plot styles
-	
+
+    static {
+        hep.aida.jfree.AnalysisFactory.register();
+    } 
+    
 	static IHistogramFactory histogramFactory = IAnalysisFactory.create().createHistogramFactory(null);
 	IPlotterFactory plotterFactory = IAnalysisFactory.create().createPlotterFactory();
 	protected Map<String, IPlotter> plotters = new HashMap<String, IPlotter>(); 
 	protected Map<SiSensor, IHistogram1D> t0Plots = new HashMap<SiSensor, IHistogram1D>(); 
+	protected Map<SiSensor, IHistogram1D> amplitudePlots = new HashMap<SiSensor, IHistogram1D>(); 
+	protected Map<SiSensor, IHistogram1D> chi2Plots = new HashMap<SiSensor, IHistogram1D>(); 
+	protected Map<Integer, List<RawTrackerHit>> topRawHitsPerLayer = new HashMap<Integer, List<RawTrackerHit>>();
+	protected Map<Integer, List<RawTrackerHit>> botRawHitsPerLayer = new HashMap<Integer, List<RawTrackerHit>>();
+	IPlotterStyle style = null; 
+	
 	
     private int computePlotterRegion(HpsSiSensor sensor) {
 
@@ -64,33 +75,53 @@
 		return -1; 
     }
 	
-	
-	
 	protected void detectorChanged(Detector detector) {
-		
+	  
 		List<HpsSiSensor> sensors 
 			= detector.getSubdetector("Tracker").getDetectorElement().findDescendants(HpsSiSensor.class);
 	
-		//--- t0 Plots ---//
-		//----------------//
 		plotters.put("L1-L3 t0", plotterFactory.create("L1-L3 t0"));
 		plotters.get("L1-L3 t0").createRegions(6,2);
 
 		plotters.put("L4-L6 t0", plotterFactory.create("L4-L6 t0"));
 		plotters.get("L4-L6 t0").createRegions(6,4);
-		int index = 0;
+		
+		plotters.put("L1-L3 Amplitude", plotterFactory.create("L1-L3 Amplitude"));
+		plotters.get("L1-L3 Amplitude").createRegions(6,2);
+
+		plotters.put("L4-L6 Amplitude", plotterFactory.create("L4-L6 Amplitude"));
+		plotters.get("L4-L6 Amplitude").createRegions(6,4);
+		
+		plotters.put("L1-L3 Chi^2 Probability", plotterFactory.create("L1-L3 Chi^2 Probability"));
+		plotters.get("L1-L3 Chi^2 Probability").createRegions(6,2);
+
+		plotters.put("L4-L6 Chi^2 Probability", plotterFactory.create("L1-L3 Chi^2 Probability"));
+		plotters.get("L4-L6 Chi^2 Probability").createRegions(6,4);
+		
 		for (HpsSiSensor sensor : sensors) {
 
 			t0Plots.put(sensor,histogramFactory.createHistogram1D(sensor.getName() + " - t0",75, -50, 100.0));
+			amplitudePlots.put(sensor, histogramFactory.createHistogram1D(sensor.getName() + " - Amplitude", 200, 0, 2000));
+			chi2Plots.put(sensor, histogramFactory.createHistogram1D(sensor.getName() + " - Chi^2 Probability", 20, 0, 1));
+			
 			if (sensor.getLayerNumber() < 7) {
 			    plotters.get("L1-L3 t0").region(this.computePlotterRegion(sensor))
 			                            .plot(t0Plots.get(sensor));
+			    plotters.get("L1-L3 Amplitude").region(this.computePlotterRegion(sensor))
+			                                   .plot(amplitudePlots.get(sensor));
+			    plotters.get("L1-L3 Chi^2 Probability").region(this.computePlotterRegion(sensor))
+			                                   .plot(chi2Plots.get(sensor));
+			    
 			} else {
 				plotters.get("L4-L6 t0").region(this.computePlotterRegion(sensor))
 				                        .plot(t0Plots.get(sensor));
+			    plotters.get("L4-L6 Amplitude").region(this.computePlotterRegion(sensor))
+			                                   .plot(amplitudePlots.get(sensor));
+			    plotters.get("L4-L6 Chi^2 Probability").region(this.computePlotterRegion(sensor))
+			                                   .plot(chi2Plots.get(sensor));
 			}
 		}
-	
+		
 		for (IPlotter plotter : plotters.values()) { 
 			plotter.show();
 		}
@@ -103,14 +134,23 @@
 		
 		List<LCRelation> fittedHits = event.get(LCRelation.class, "SVTFittedRawTrackerHits");
 		
-		
 		for (LCRelation fittedHit : fittedHits) { 
 			
+		    RawTrackerHit rawHit = (RawTrackerHit) fittedHit.getFrom();
+		    
 			HpsSiSensor sensor 
-				= (HpsSiSensor) ((RawTrackerHit) fittedHit.getFrom()).getDetectorElement();
+				= (HpsSiSensor) rawHit.getDetectorElement();
 			
 			double t0 = FittedRawTrackerHit.getT0(fittedHit);
 			t0Plots.get(sensor).fill(t0);
+			
+			double amplitude = FittedRawTrackerHit.getAmp(fittedHit);
+			amplitudePlots.get(sensor).fill(amplitude);
+			
+			double chi2Prob = ShapeFitParameters.getChiProb(FittedRawTrackerHit.getShapeFitParameters(fittedHit));
+			chi2Plots.get(sensor).fill(chi2Prob);
+		
+			
 		}	
 	}
 }

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/trackrecon/TrackTimePlots.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/trackrecon/TrackTimePlots.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/trackrecon/TrackTimePlots.java	Wed Mar 25 14:43:27 2015
@@ -6,9 +6,11 @@
 import hep.aida.IPlotterFactory;
 import hep.aida.IPlotterStyle;
 import hep.aida.ref.plotter.PlotterRegion;
+
 import java.util.List;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
+
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.lcsim.detector.tracker.silicon.DopedSilicon;
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.event.EventHeader;

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalClusterPlots.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalClusterPlots.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalClusterPlots.java	Wed Mar 25 14:43:27 2015
@@ -9,8 +9,8 @@
 import java.util.List;
 
 import org.apache.commons.math.stat.StatUtils;
-import org.hps.readout.ecal.TriggerModule;
 import org.hps.recon.ecal.ECalUtils;
+import org.hps.recon.ecal.triggerbank.TriggerModule;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.Cluster;
 import org.lcsim.event.EventHeader;
@@ -35,9 +35,7 @@
  * <li>The third sub-tab shows the time distribution of the cluster
  * (Histogram1D), taken from the mean of the times forming the cluster,
  * as well as the RMS (Histogram1D).</li>
- * <li>The fourth sub-tab is a larger version of the the cluster centers
- * distribution.</li>
- * <li>The fifth tab displays the cluster pair distribution for the
+ * <li>The fourth tab displays the cluster pair distribution for the
  * energy sum, energy difference, energy slope, and coplanarity cuts
  * for all top/bottom pairs received. It also displays the average x-
  * and y-coordinates for the pairs.</li>
@@ -56,7 +54,7 @@
     private boolean logScale = false;
 	private AIDA aida = AIDA.defaultInstance();
     private double maxE = 5000 * ECalUtils.MeV;
-	private IPlotter[] plotter = new IPlotter[5];
+	private IPlotter[] plotter = new IPlotter[4];
 	private String clusterCollectionName = "EcalClusters";
 	
 	// Monitoring plot variables.
@@ -79,10 +77,9 @@
     private static final int TAB_CLUSTER_COUNT = 0;
     private static final int TAB_CLUSTER_ENERGY = 1;
     private static final int TAB_CLUSTER_TIME = 2;
-    private static final int TAB_CLUSTER_CENTER = 3;
-    private static final int TAB_CLUSTER_PAIR = 4;
+    private static final int TAB_CLUSTER_PAIR = 3;
     private static final String[] TAB_NAMES = { "Cluster Count Plots", "Cluster Energy Plots",
-    	"Cluster Time Plots", "Cluster Center Plot", "Cluster Pair Plots" };
+    	"Cluster Time Plots", "Cluster Pair Plots" };
     
     /**
      * Resets all of the plots for the new detector.
@@ -136,13 +133,7 @@
         plotter[TAB_CLUSTER_TIME].createRegions(1, 2);
         plotter[TAB_CLUSTER_TIME].region(0).plot(clusterTimes);
         plotter[TAB_CLUSTER_TIME].region(1).plot(clusterTimeSigma);
-        
-        // Define the Cluster Center tab.
-        plotter[TAB_CLUSTER_CENTER].createRegion();
-        plotter[TAB_CLUSTER_CENTER].region(0).plot(edgePlot);
-        plotter[TAB_CLUSTER_CENTER].style().setParameter("hist2DStyle", "colorMap");
-        plotter[TAB_CLUSTER_CENTER].style().dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow");
-        
+               
          // Create the Cluster Pair tab.
         plotter[TAB_CLUSTER_PAIR].createRegions(2, 3);
         plotter[TAB_CLUSTER_PAIR].region(0).plot(pairEnergySum);

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplay.java	Wed Mar 25 14:43:27 2015
@@ -39,200 +39,200 @@
  * <li>If the user clicks on a crystal, the corresponding energy and time
  * distributions (both of type <code>IHistogram1D</code>) are shown in
  * the last panel of the monitoring application, as well as a 2D histogram
- * (hit time vs. hit energy). Finally, if available, the raw waveshape (in
- * mV) is displayed.</li>
+ * (hit time vs. hit energy).The fourth panel reports energy for the crystal.</li>
  * 
  * @author Andrea Celentano
  */
 public class EcalEventDisplay extends Driver implements CrystalListener, ActionListener {
-	// Class variables.
-	private static final int NUM_CHANNELS = 11 * 47;
-	
-	// Plotter objects and variables.
-	private IPlotter plotter;
-	private IPlotterFactory plotterFactory;
-	private AIDA aida = AIDA.defaultInstance();	
-	
-	// LCIO Collection names.
-	private String inputCollection = "EcalCalHits";
-	private String clusterCollection = "EcalClusters";
-	private String inputCollectionRaw = "EcalReadoutHits";
-	
-	// Channel plot lists.
-	private ArrayList<IHistogram1D> channelEnergyPlot;
-	private ArrayList<IHistogram1D> clusterEnergyPlot;
-	private ArrayList<IHistogram1D> channelTimePlot;
-	//private ArrayList<IHistogram1D> channelRawWaveform;
-	private ArrayList<IHistogram2D> channelTimeVsEnergyPlot;
-	
-	// Internal variables.
-	private PEventViewer viewer;								 // Single event display.
-	private int pedSamples = 10;								 // 
-	private IPlotterStyle pstyle;								 // The plotter style for all plots.
-	private long lastEventTime = 0;								 // Tracks the time at which the last event occurred.
-	private int eventRefreshRate = 1;							 // The number of seconds before an update occurs.
-	private boolean resetOnUpdate = true;						 // Clears the event display on each update.
-	private double minEch = 10 * ECalUtils.MeV;					 // The energy scale minimum.
-	private double maxEch = 3500 * ECalUtils.MeV;				 // The energy scale maximum.
-	private int[] windowRaw = new int[NUM_CHANNELS];			 // The number of samples in a waveform for each channel.
-	private boolean[] isFirstRaw = new boolean[NUM_CHANNELS];	 // Whether a waveform plot was initiated for each channel.
-	
-	// Plot style and title variables.
-	private static final String NO_TITLE = "";
-	//private static final String SIGNAL_TIME_TITLE = "Time (ns)";
-	private static final String HIT_TIME_TITLE = "Hit Time (ns)";
-	//private static final String SIGNAL_DATA_STYLE_COLOR = "orange";
-	//private static final String RAW_WAVEFORM_TITLE = "Raw Waveform";
-	private static final String HIT_ENERGY_TITLE = "Hit Energy (GeV)";
-	private static final String CLUSTER_ENERGY_TITLE = "Cluster Energy (GeV)";
-	//private static final String SIGNAL_AMPLITUDE_TITLE = "Signal Amplitude (mV)";
-	
-	/**
-	 * Sets the upper bound of the energy scales used by the driver.
-	 * Energy units are in GeV.
-	 * @param maxEch - The energy scale upper bound.
-	 */
-	public void setMaxEch(double maxEch) {
-		this.maxEch = maxEch;
-	}
-	
-	/**
-	 * Sets the lower bound of the energy scales used by the driver.
-	 * Energy units are in GeV.
-	 * @param minEch - The lower energy scale bound.
-	 */
-	public void setMinEch(double minEch) {
-		this.minEch = minEch;
-	}
-	
-	public void setPedSamples(int pedSamples) {
-		this.pedSamples = pedSamples;
-	}
-	/**
-	 * Sets the LCIO collection name for the processed calorimeter hits.
-	 * @param inputCollection - The LCIO collection name.
-	 */
-	public void setInputCollection(String inputCollection) {
-		this.inputCollection = inputCollection;
-	}
-	
-	/**
-	 * Sets the LCIO collection name for the raw waveform hits.
-	 * @param inputCollectionRaw - The LCIO collection name.
-	 */
-	public void setInputCollectionRaw(String inputCollectionRaw) {
-		this.inputCollectionRaw = inputCollectionRaw;
-	}
-	
-	/**
-	 * Sets the LCIO collection name for calorimeter clusters.
-	 * @param inputClusterCollection - The LCIO collection name.
-	 */
-	public void setInputClusterCollection(String inputClusterCollection) {
-		this.clusterCollection = inputClusterCollection;
-	}
-	
-	/**
-	 * Sets the rate at which the GUI updates its elements,
-	 * @param eventRefreshRate - The rate at which the GUI should be
-	 * updated, in seconds.
-	 */
-	public void setEventRefreshRate(int eventRefreshRate) {
-		this.eventRefreshRate = eventRefreshRate;
-	}
-	
-	/**
-	 * Sets whether the event display should be cleared after event
-	 * or whether it should retain the previously displayed results.
-	 * @param resetOnUpdate - <code>true</code> means that the event
-	 * display should be cleared on each update and <code>false</code>
-	 * that it should not.
-	 */
-	public void setResetOnUpdate(boolean resetOnUpdate) {
-		this.resetOnUpdate = resetOnUpdate;
-	}
-	
-	/**
-	 * Initializes the single channel monitoring plots for all crystal
-	 * channels and defines the plotter region that contains them.
-	 */
-	@Override
-	public void detectorChanged(Detector detector) {
-		// Reset the AIDA tree directory.
-		aida.tree().cd("/");
-		
-		// Store histograms for the crystals.
-		channelEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS);
-		channelTimePlot = new ArrayList<IHistogram1D>(NUM_CHANNELS);
-		//channelRawWaveform = new ArrayList<IHistogram1D>(NUM_CHANNELS);
-		clusterEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS);
-		channelTimeVsEnergyPlot = new ArrayList<IHistogram2D>(NUM_CHANNELS);
-		
-		// Create the histograms for single channel energy and time
-		// distribution.
-		for(int ii = 0; ii < NUM_CHANNELS; ii++) {
-			// The above instruction is a terrible hack, just to fill
-			// the arrayList with all the elements. They'll be initialized
-			// properly during the event readout, Since we want to account
-			// for possibly different raw waveform dimensions!
-			
-			//Get the x and y indices for the current channel.
-			int row = EcalMonitoringUtilities.getRowFromHistoID(ii);
-			int column = EcalMonitoringUtilities.getColumnFromHistoID(ii);
-			
-			// Initialize the histograms for the current crystal channel.
-			channelEnergyPlot.add(aida.histogram1D(detector.getDetectorName() + " : "
-						+ inputCollection + " : Hit Energy : " + column + " " + row
-						+ ": " + ii, 100, -0.2, maxEch));
-			channelTimePlot.add(aida.histogram1D(detector.getDetectorName() + " : "
-						+ inputCollection + " : Hit Time : " + column + " " + row + ": "
-						+ ii, 100, 0, 400));     
-			channelTimeVsEnergyPlot.add(aida.histogram2D(detector.getDetectorName()
-					+ " : " + inputCollection + " : Hit Time Vs Energy : " + column
-					+ " " + row + ": " + ii, 100, 0, 400, 100, -0.2, maxEch));              
-			//channelRawWaveform.add(aida.histogram1D(detector.getDetectorName() + " : "
-			//		+ inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii));
-			clusterEnergyPlot.add(aida.histogram1D(detector.getDetectorName() + " : "
-					+ inputCollection + " : Cluster Energy : " + column + " " + row
-					+ ": " + ii, 100, -0.2, maxEch));
-			
-			// Note that no raw waveform has yet been read for this
-			// crystal/channel.
-			windowRaw[ii] = 1;
-			isFirstRaw[ii] = true;
-		}
-		
-		// Define the plot region that will display the single channel
-		// plots in the monitoring application.
-		plotterFactory = aida.analysisFactory().createPlotterFactory("Single Channel");
-		plotter = plotterFactory.create("Single Channel");
-		pstyle = this.createDefaultStyle();
-		plotter.setTitle("");
-		plotter.createRegions(2,2);
-		
-		// Define the first plot region.
-		pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE);
-		pstyle.yAxisStyle().setLabel(NO_TITLE);
-		plotter.region(0).plot(channelEnergyPlot.get(0), pstyle);
-		
-		// Define the second plot region.
-		pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
-		pstyle.yAxisStyle().setLabel(NO_TITLE);
-		plotter.region(1).plot(channelTimePlot.get(0), pstyle);
-		
-		// Define the third plot region; this encompasses the time vs.
-		// energy plots.
-		pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
-		pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE);
-		plotter.region(2).plot(channelTimeVsEnergyPlot.get(0), pstyle);
-		
-		// Define the fourth plot region; this encompasses the cluster
-		// energy for each channel.
-		pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE);
-		pstyle.yAxisStyle().setLabel(NO_TITLE);
-		plotter.region(3).plot(clusterEnergyPlot.get(0), pstyle);
-		
-		/**
+    // Class variables.
+    private static final int NUM_CHANNELS = 11 * 47;
+
+    // Plotter objects and variables.
+    private IPlotter plotter;
+    private IPlotterFactory plotterFactory;
+    private AIDA aida = AIDA.defaultInstance();	
+
+    // LCIO Collection names.
+    private String inputCollection = "EcalCalHits";
+    private String clusterCollection = "EcalClusters";
+    private String inputCollectionRaw = "EcalReadoutHits";
+
+    // Channel plot lists.
+    private ArrayList<IHistogram1D> channelEnergyPlot;
+    private ArrayList<IHistogram1D> clusterEnergyPlot;
+    private ArrayList<IHistogram1D> channelTimePlot;
+    //private ArrayList<IHistogram1D> channelRawWaveform;
+    private ArrayList<IHistogram2D> channelTimeVsEnergyPlot;
+
+    // Internal variables.
+    private PEventViewer viewer;								 // Single event display.
+    private int pedSamples = 10;								 // 
+    private IPlotterStyle pstyle;								 // The plotter style for all plots.
+    private long lastEventTime = 0;								 // Tracks the time at which the last event occurred.
+    private int eventRefreshRate = 1;							 // The number of seconds before an update occurs.
+    private boolean resetOnUpdate = true;						 // Clears the event display on each update.
+    private double minEch = 10 * ECalUtils.MeV;					 // The energy scale minimum.
+    private double maxEch = 3500 * ECalUtils.MeV;				 // The energy scale maximum.
+    private int[] windowRaw = new int[NUM_CHANNELS];			 // The number of samples in a waveform for each channel.
+    private boolean[] isFirstRaw = new boolean[NUM_CHANNELS];	 // Whether a waveform plot was initiated for each channel.
+
+    // Plot style and title variables.
+    private static final String NO_TITLE = "";
+    //private static final String SIGNAL_TIME_TITLE = "Time (ns)";
+    private static final String HIT_TIME_TITLE = "Hit Time (ns)";
+    //private static final String SIGNAL_DATA_STYLE_COLOR = "orange";
+    //private static final String RAW_WAVEFORM_TITLE = "Raw Waveform";
+    private static final String HIT_ENERGY_TITLE = "Hit Energy (GeV)";
+    private static final String CLUSTER_ENERGY_TITLE = "Cluster Energy (GeV)";
+    private String detectorName;
+    //private static final String SIGNAL_AMPLITUDE_TITLE = "Signal Amplitude (mV)";
+
+    /**
+     * Sets the upper bound of the energy scales used by the driver.
+     * Energy units are in GeV.
+     * @param maxEch - The energy scale upper bound.
+     */
+    public void setMaxEch(double maxEch) {
+        this.maxEch = maxEch;
+    }
+
+    /**
+     * Sets the lower bound of the energy scales used by the driver.
+     * Energy units are in GeV.
+     * @param minEch - The lower energy scale bound.
+     */
+    public void setMinEch(double minEch) {
+        this.minEch = minEch;
+    }
+
+    public void setPedSamples(int pedSamples) {
+        this.pedSamples = pedSamples;
+    }
+    /**
+     * Sets the LCIO collection name for the processed calorimeter hits.
+     * @param inputCollection - The LCIO collection name.
+     */
+    public void setInputCollection(String inputCollection) {
+        this.inputCollection = inputCollection;
+    }
+
+    /**
+     * Sets the LCIO collection name for the raw waveform hits.
+     * @param inputCollectionRaw - The LCIO collection name.
+     */
+    public void setInputCollectionRaw(String inputCollectionRaw) {
+        this.inputCollectionRaw = inputCollectionRaw;
+    }
+
+    /**
+     * Sets the LCIO collection name for calorimeter clusters.
+     * @param inputClusterCollection - The LCIO collection name.
+     */
+    public void setInputClusterCollection(String inputClusterCollection) {
+        this.clusterCollection = inputClusterCollection;
+    }
+
+    /**
+     * Sets the rate at which the GUI updates its elements,
+     * @param eventRefreshRate - The rate at which the GUI should be
+     * updated, in seconds.
+     */
+    public void setEventRefreshRate(int eventRefreshRate) {
+        this.eventRefreshRate = eventRefreshRate;
+    }
+
+    /**
+     * Sets whether the event display should be cleared after event
+     * or whether it should retain the previously displayed results.
+     * @param resetOnUpdate - <code>true</code> means that the event
+     * display should be cleared on each update and <code>false</code>
+     * that it should not.
+     */
+    public void setResetOnUpdate(boolean resetOnUpdate) {
+        this.resetOnUpdate = resetOnUpdate;
+    }
+
+    /**
+     * Initializes the single channel monitoring plots for all crystal
+     * channels and defines the plotter region that contains them.
+     */
+    @Override
+    public void detectorChanged(Detector detector) {
+        // Reset the AIDA tree directory.
+        aida.tree().cd("/");
+        detectorName=detector.getName();
+        // Store histograms for the crystals.
+        channelEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS);
+        channelTimePlot = new ArrayList<IHistogram1D>(NUM_CHANNELS);
+        //channelRawWaveform = new ArrayList<IHistogram1D>(NUM_CHANNELS);
+        clusterEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS);
+        channelTimeVsEnergyPlot = new ArrayList<IHistogram2D>(NUM_CHANNELS);
+
+        // Create the histograms for single channel energy and time
+        // distribution.
+        for(int ii = 0; ii < NUM_CHANNELS; ii++) {
+            // The above instruction is a terrible hack, just to fill
+            // the arrayList with all the elements. They'll be initialized
+            // properly during the event readout, Since we want to account
+            // for possibly different raw waveform dimensions!
+
+            //Get the x and y indices for the current channel.
+            int row = EcalMonitoringUtilities.getRowFromHistoID(ii);
+            int column = EcalMonitoringUtilities.getColumnFromHistoID(ii);
+
+            // Initialize the histograms for the current crystal channel.
+            channelEnergyPlot.add(aida.histogram1D(detectorName + " : "
+                    + inputCollection + " : Hit Energy : " + column + " " + row
+                    + ": " + ii, 100, -0.2, maxEch));
+            channelTimePlot.add(aida.histogram1D(detectorName + " : "
+                    + inputCollection + " : Hit Time : " + column + " " + row + ": "
+                    + ii, 100, 0, 400));     
+            channelTimeVsEnergyPlot.add(aida.histogram2D(detectorName
+                    + " : " + inputCollection + " : Hit Time Vs Energy : " + column
+                    + " " + row + ": " + ii, 100, 0, 400, 100, -0.2, maxEch));              
+            //channelRawWaveform.add(aida.histogram1D(detector.getDetectorName() + " : "
+            //		+ inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii));
+            clusterEnergyPlot.add(aida.histogram1D(detectorName + " : "
+                    + inputCollection + " : Cluster Energy : " + column + " " + row
+                    + ": " + ii, 100, -0.2, maxEch));
+
+            // Note that no raw waveform has yet been read for this
+            // crystal/channel.
+            windowRaw[ii] = 1;
+            isFirstRaw[ii] = true;
+        }
+
+        // Define the plot region that will display the single channel
+        // plots in the monitoring application.
+        plotterFactory = aida.analysisFactory().createPlotterFactory("Single Channel");
+        plotter = plotterFactory.create("Single Channel");
+        pstyle = this.createDefaultStyle();
+        plotter.setTitle("");
+        plotter.createRegions(2,2);
+
+        // Define the first plot region.
+        pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE);
+        pstyle.yAxisStyle().setLabel(NO_TITLE);
+        plotter.region(0).plot(channelEnergyPlot.get(0), pstyle);
+
+        // Define the second plot region.
+        pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
+        pstyle.yAxisStyle().setLabel(NO_TITLE);
+        plotter.region(1).plot(channelTimePlot.get(0), pstyle);
+
+        // Define the third plot region; this encompasses the time vs.
+        // energy plots.
+        pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
+        pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE);
+        plotter.region(2).plot(channelTimeVsEnergyPlot.get(0), pstyle);
+
+        // Define the fourth plot region; this encompasses the cluster
+        // energy for each channel.
+        pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE);
+        pstyle.yAxisStyle().setLabel(NO_TITLE);
+        plotter.region(3).plot(clusterEnergyPlot.get(0), pstyle);
+
+        /**
 		// Define the fourth plot region; this encompasses the raw
 		// wave form plots.
 		pstyle.xAxisStyle().setLabel(RAW_WAVEFORM_TITLE);
@@ -241,160 +241,205 @@
 		pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR);
 		pstyle.dataStyle().errorBarStyle().setVisible(false);
 		plotter.region(3).plot(channelRawWaveform.get(0), pstyle);
-		**/
-		
-		// Display the plot region.
-		plotter.show();
-		
-		// Set the time tracker variables.
-		lastEventTime = 0;
-	}
-	
-	/**
-	 * Initializes the <code>Viewer</code> for the single event display.
-	 * If a configuration file is available, then it is used by the
-	 * <code>Viewer</code> to display hardware configuration mappings.
-	 * Otherwise, this is excluded.
-	 */
-	@Override
-	public void startOfData() {
-		// Check if the configuration mapping file exists.
-		File config = new File("ecal-mapping-config.csv");
-		
-		// If the file exists, load the viewer that will display it.
-		if(config.exists() && config.canRead()) {
-			// Account for IO read errors. Only load this version if
-			// the data file can be read successfully.
-			try { viewer = new PDataEventViewer(config.getAbsolutePath()); }
-			
-			// Otherwise, open the regular version.
-			catch (IOException e) { viewer = new PEventViewer(); }
-		}
-		
-		// If the file is not present, then just load the normal version.
-		else { viewer = new PEventViewer(); }
-		
-		// Set the viewer properties.
-		viewer.setScaleMinimum(minEch);
-		viewer.setScaleMaximum(maxEch);
-		viewer.addCrystalListener(this);
-		
-		// Make the Viewer object visible.
-		viewer.setVisible(true);
-	}
-	
-	/**
-	 * Hides the single event display and disposes it from memory.
-	 */
-	@Override
-	public void endOfData() {
-		viewer.setVisible(false);
-		viewer.dispose();
-	}
-	
-	@Override
-	public void process(EventHeader event){
-		// Check whether enough time has passed to perform an update
-		// on the event display.
-		boolean update = false;
-		long currentTime = System.currentTimeMillis() / 1000;
-		if((currentTime - lastEventTime) > eventRefreshRate){
-			lastEventTime = currentTime;
-			update = true;
-		}
-		
-		// If an update should be made, perform the update.
-		if(update && resetOnUpdate) { viewer.resetDisplay(); }
-		
-		// If the event has calorimeter hit objects...
-		if(event.hasCollection(CalorimeterHit.class, inputCollection)) {
-			// Get the list of calorimeter hits.
-			List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputCollection);
-			
-			// For each of the calorimeter hits...
-			for (CalorimeterHit hit : hits) {
-				// Get the x and y indices for the current hit.
-				int ix = hit.getIdentifierFieldValue("ix");
-				int iy = hit.getIdentifierFieldValue("iy");
-				
-				if (iy != 0 && ix != 0) {
-					// Get the histogram index for the hit.
-					int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix);
-					
-					// If the hit has energy, populate the plots.
-					if(hit.getCorrectedEnergy() > 0) {
-						channelEnergyPlot.get(id).fill(hit.getCorrectedEnergy());
-						channelTimePlot.get(id).fill(hit.getTime());
-						channelTimeVsEnergyPlot.get(id).fill(hit.getTime(), hit.getCorrectedEnergy());
-					}
-					
-					// If an update to the event display should be
-					// performed, give it the hits.
-					if(update) { viewer.addHit(hit); }
-				}
-			}
-		}
-		
-		// If there are clusters in the event...
-		if (event.hasCollection(Cluster.class, clusterCollection)) {
-			// Get the list of clusters.
-			List<Cluster> clusters = event.get(Cluster.class, clusterCollection);
-			
-			// Iterate over the clusters and add them to the event
-			// display if appropriate.
-			for (Cluster cluster : clusters) {
-				// Get the ix and iy indices for the seed.
-				int ix = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix");
-				int iy = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy");
-				
-				// Get the histogram index for the hit.
-				int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix);
-				
-				// Add the cluster energy to the plot.
-				if(cluster.getEnergy() > 0.0) {
-					clusterEnergyPlot.get(id).fill(cluster.getEnergy());
-				}
-				
-				// If an update is needed, add the cluster to the viewer.
-				if(update) { viewer.addCluster(cluster); }
-			}
-		}
-		
-		/**
+         **/
+
+        // Display the plot region.
+        plotter.show();
+
+        // Set the time tracker variables.
+        lastEventTime = 0;
+    }
+
+    /**
+     * Initializes the <code>Viewer</code> for the single event display.
+     * If a configuration file is available, then it is used by the
+     * <code>Viewer</code> to display hardware configuration mappings.
+     * Otherwise, this is excluded.
+     */
+    @Override
+    public void startOfData() {
+        // Check if the configuration mapping file exists.
+        File config = new File("ecal-mapping-config.csv");
+
+        // If the file exists, load the viewer that will display it.
+        if(config.exists() && config.canRead()) {
+            // Account for IO read errors. Only load this version if
+            // the data file can be read successfully.
+            try { viewer = new PDataEventViewer(config.getAbsolutePath()); }
+
+            // Otherwise, open the regular version.
+            catch (IOException e) { viewer = new PEventViewer(); }
+        }
+
+        // If the file is not present, then just load the normal version.
+        else { viewer = new PEventViewer(); }
+
+        // Set the viewer properties.
+        viewer.setScaleMinimum(minEch);
+        viewer.setScaleMaximum(maxEch);
+        viewer.addCrystalListener(this);
+
+        // Make the Viewer object visible.
+        viewer.setVisible(true);
+    }
+
+    /**
+     * Hides the single event display and disposes it from memory.
+     * Also removes histograms from aida tree. We do not want them in the output aida file, if any..
+     */
+    @Override
+    public void endOfData() {
+        viewer.setVisible(false);
+        viewer.dispose();
+
+        int row,column;
+        String hName;
+        System.out.println("EcalEventDisplay endOfData clear histograms");
+        for(int ii = 0; ii < NUM_CHANNELS; ii++) {
+            // The above instruction is a terrible hack, just to fill
+            // the arrayList with all the elements. They'll be initialized
+            // properly during the event readout, Since we want to account
+            // for possibly different raw waveform dimensions!
+
+            //Get the x and y indices for the current channel.
+            row = EcalMonitoringUtilities.getRowFromHistoID(ii);
+            column = EcalMonitoringUtilities.getColumnFromHistoID(ii);
+            hName=detectorName + " : "
+                    + inputCollection + " : Hit Energy : " + column + " " + row
+                    + ": " + ii;
+            aida.tree().rm(hName);
+
+            hName=detectorName + " : "
+                    + inputCollection + " : Hit Time : " + column + " " + row + ": "
+                    + ii;
+            aida.tree().rm(hName);
+
+            hName=detectorName+ " : " + inputCollection + " : Hit Time Vs Energy : " + column
+                    + " " + row + ": " + ii;
+            aida.tree().rm(hName);
+
+
+            if (isFirstRaw[ii]==false){
+                hName=detectorName+
+                        " : " + inputCollectionRaw + " : Raw Waveform : " + column + " "
+                        + row + ": " + ii;
+                aida.tree().rm(hName);
+
+            }
+
+        }
+        System.out.println("EcalEventDisplay endOfData clear histograms done");
+
+
+
+
+
+
+    }
+
+    @Override
+    public void process(EventHeader event){
+        // Check whether enough time has passed to perform an update
+        // on the event display.
+        boolean update = false;
+        long currentTime = System.currentTimeMillis() / 1000;
+        if((currentTime - lastEventTime) > eventRefreshRate){
+            lastEventTime = currentTime;
+            update = true;
+        }
+
+        // If an update should be made, perform the update.
+        if(update && resetOnUpdate) { viewer.resetDisplay(); }
+
+        // If the event has calorimeter hit objects...
+        if(event.hasCollection(CalorimeterHit.class, inputCollection)) {
+            // Get the list of calorimeter hits.
+            List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputCollection);
+
+            // For each of the calorimeter hits...
+            for (CalorimeterHit hit : hits) {
+                // Get the x and y indices for the current hit.
+                int ix = hit.getIdentifierFieldValue("ix");
+                int iy = hit.getIdentifierFieldValue("iy");
+
+                if (iy != 0 && ix != 0) {
+                    // Get the histogram index for the hit.
+                    int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix);
+
+                    // If the hit has energy, populate the plots.
+                    if(hit.getCorrectedEnergy() > 0) {
+                        channelEnergyPlot.get(id).fill(hit.getCorrectedEnergy());
+                        channelTimePlot.get(id).fill(hit.getTime());
+                        channelTimeVsEnergyPlot.get(id).fill(hit.getTime(), hit.getCorrectedEnergy());
+                    }
+
+                    // If an update to the event display should be
+                    // performed, give it the hits.
+                    if(update) { viewer.addHit(hit); }
+                }
+            }
+        }
+
+        // If there are clusters in the event...
+        if (event.hasCollection(Cluster.class, clusterCollection)) {
+            // Get the list of clusters.
+            List<Cluster> clusters = event.get(Cluster.class, clusterCollection);
+
+            // Iterate over the clusters and add them to the event
+            // display if appropriate.
+            for (Cluster cluster : clusters) {
+                // Get the ix and iy indices for the seed.
+                int ix = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix");
+                int iy = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy");
+
+                // Get the histogram index for the hit.
+                int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix);
+
+                // Add the cluster energy to the plot.
+                if(cluster.getEnergy() > 0.0) {
+                    clusterEnergyPlot.get(id).fill(cluster.getEnergy());
+                }
+
+                // If an update is needed, add the cluster to the viewer.
+                if(update) { viewer.addCluster(cluster); }
+            }
+        }
+
+        /**
 		// Plot the raw waveform only if raw tracker hit exist in the
 		// event.
 		if (event.hasCollection(RawTrackerHit.class, inputCollectionRaw)){
 			// Get the list of raw tracker hits.
 			List<RawTrackerHit> hits = event.get(RawTrackerHit.class, inputCollectionRaw);
-			
+
 			// Process each raw tracker hit.
 			for (RawTrackerHit hit : hits) {
 				// Get the x and y indices for the hit.
 				int ix = hit.getIdentifierFieldValue("ix");
 				int iy = hit.getIdentifierFieldValue("iy");
-				
+
 				if(iy != 0 && ix != 0) {
 					if(!ECalUtils.isInHole(iy, ix)) {
 						// Get the crystal ID for the current hit.
 						int id = ECalUtils.getHistoIDFromRowColumn(iy, ix);
-						
+
 						// The window is length is not known by default.
 						// If this is the first hit, read the window
 						// length and initialize the plot.
 						if(isFirstRaw[id]) {
 							// Note that this plot is initialized.
 							isFirstRaw[id] = false;
-							
+
 							// Set the waveform array.
 							windowRaw[id] = hit.getADCValues().length;
-							
+
 							// Initialize the waveform plot.
 							channelRawWaveform.set(id, aida.histogram1D(detector.getDetectorName()
 									+ " : " + inputCollectionRaw + " : Raw Waveform : " + ix + " "
 									+ iy + ": " + id, windowRaw[id], -0.5 * ECalUtils.ecalReadoutPeriod,
 									(-0.5 + windowRaw[id]) * ECalUtils.ecalReadoutPeriod));
 						}
-						
+
 						// If the plot should be updated, do so.
 						if(update) {
 							channelRawWaveform.get(id).reset();
@@ -412,60 +457,60 @@
 				}
 			}
 		}
-		**/
-		
-		// Update the single event display.
-		if(update) { viewer.updateDisplay(); }
-	}
-	
-	@Override
-	public void actionPerformed(ActionEvent ae) { }
-	
-	@Override
-	public void crystalActivated(CrystalEvent e) { }
-	
-	@Override
-	public void crystalDeactivated(CrystalEvent e) { }
-	
-	/**
-	 * Updates the monitoring plots for the crystal that was clicked.
-	 */
-	@Override
-	public void crystalClicked(CrystalEvent e) {
-		// Get the crystal that was clicked in the LCSim coordinate system.
-		Point ecalPoint = Viewer.toEcalPoint(e.getCrystalID());
-		
-		// Make sure that the clicked crystal is valid. Necessary??
-		if((ecalPoint.x != 0) && (ecalPoint.y != 0))
-			if (!EcalMonitoringUtilities.isInHole(ecalPoint.y, ecalPoint.x)) {
-				// Get the crystal ID.
-				int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(ecalPoint.y, ecalPoint.x);
-				
-				// Clear and replot region 0 for the new crystal.
-				plotter.region(0).clear();
-				pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE);
-				pstyle.yAxisStyle().setLabel(NO_TITLE);
-				plotter.region(0).plot(channelEnergyPlot.get(id), pstyle);
-				
-				// Clear and replot region 1 for the new crystal.
-				plotter.region(1).clear();
-				pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
-				pstyle.yAxisStyle().setLabel(NO_TITLE);
-				plotter.region(1).plot(channelTimePlot.get(id), pstyle);
-				
-				// Clear and replot region 2 for the new crystal.
-				plotter.region(2).clear();
-				pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
-				pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE);
-				plotter.region(2).plot(channelTimeVsEnergyPlot.get(id), pstyle);
-				
-				// Process and plot the region 3 plot.
-				plotter.region(3).clear();
-				pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE);
-				pstyle.yAxisStyle().setLabel(NO_TITLE);
-				plotter.region(3).plot(clusterEnergyPlot.get(id), pstyle);
-				
-				/**
+         **/
+
+        // Update the single event display.
+        if(update) { viewer.updateDisplay(); }
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent ae) { }
+
+    @Override
+    public void crystalActivated(CrystalEvent e) { }
+
+    @Override
+    public void crystalDeactivated(CrystalEvent e) { }
+
+    /**
+     * Updates the monitoring plots for the crystal that was clicked.
+     */
+    @Override
+    public void crystalClicked(CrystalEvent e) {
+        // Get the crystal that was clicked in the LCSim coordinate system.
+        Point ecalPoint = Viewer.toEcalPoint(e.getCrystalID());
+
+        // Make sure that the clicked crystal is valid. Necessary??
+        if((ecalPoint.x != 0) && (ecalPoint.y != 0))
+            if (!EcalMonitoringUtilities.isInHole(ecalPoint.y, ecalPoint.x)) {
+                // Get the crystal ID.
+                int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(ecalPoint.y, ecalPoint.x);
+
+                // Clear and replot region 0 for the new crystal.
+                plotter.region(0).clear();
+                pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE);
+                pstyle.yAxisStyle().setLabel(NO_TITLE);
+                plotter.region(0).plot(channelEnergyPlot.get(id), pstyle);
+
+                // Clear and replot region 1 for the new crystal.
+                plotter.region(1).clear();
+                pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
+                pstyle.yAxisStyle().setLabel(NO_TITLE);
+                plotter.region(1).plot(channelTimePlot.get(id), pstyle);
+
+                // Clear and replot region 2 for the new crystal.
+                plotter.region(2).clear();
+                pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE);
+                pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE);
+                plotter.region(2).plot(channelTimeVsEnergyPlot.get(id), pstyle);
+
+                // Process and plot the region 3 plot.
+                plotter.region(3).clear();
+                pstyle.xAxisStyle().setLabel(CLUSTER_ENERGY_TITLE);
+                pstyle.yAxisStyle().setLabel(NO_TITLE);
+                plotter.region(3).plot(clusterEnergyPlot.get(id), pstyle);
+
+                /**
 				// Process and plot the region 3 plot.
 				if(!isFirstRaw[id]) {
 					pstyle.yAxisStyle().setLabel(SIGNAL_AMPLITUDE_TITLE);
@@ -479,47 +524,47 @@
 					pstyle.yAxisStyle().setLabel("");
 				}
 				plotter.region(3).plot(channelRawWaveform.get(id), pstyle);
-				**/
-		}
-	}
-	
-	/**
-	 * Initializes the default style for plots.
-	 * @return Returns an <code>IPlotterStyle</code> object that
-	 * represents the default style for plots.
-	 */
-	public IPlotterStyle createDefaultStyle() {
-		IPlotterStyle pstyle = plotterFactory.createPlotterStyle();
-		// Set the appearance of the axes.
-		pstyle.xAxisStyle().labelStyle().setBold(true);
-		pstyle.yAxisStyle().labelStyle().setBold(true);
-		pstyle.xAxisStyle().tickLabelStyle().setBold(true);
-		pstyle.yAxisStyle().tickLabelStyle().setBold(true);
-		pstyle.xAxisStyle().lineStyle().setColor("black");
-		pstyle.yAxisStyle().lineStyle().setColor("black");
-		pstyle.xAxisStyle().lineStyle().setThickness(2);
-		pstyle.yAxisStyle().lineStyle().setThickness(2);
-		
-		// Set color settings.
-		pstyle.dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow");
-		pstyle.dataStyle().fillStyle().setParameter("showZeroHeightBins", Boolean.FALSE.toString());
-		pstyle.dataStyle().errorBarStyle().setVisible(false);
-		pstyle.setParameter("hist2DStyle", "colorMap");
-		
-		// Force auto range to zero.
-		pstyle.yAxisStyle().setParameter("allowZeroSuppression", "false");
-		pstyle.xAxisStyle().setParameter("allowZeroSuppression", "false");
-		
-		// Set the title style.
-		pstyle.titleStyle().textStyle().setFontSize(20);
-		
-		// Draw caps on error bars.
-		pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", (new Float(1.0f)).toString());
-		
-		// Turn off grid lines until explicitly enabled.
-		pstyle.gridStyle().setVisible(false);
-		
-		// Return the style.
-		return pstyle;
-	}
+                 **/
+            }
+    }
+
+    /**
+     * Initializes the default style for plots.
+     * @return Returns an <code>IPlotterStyle</code> object that
+     * represents the default style for plots.
+     */
+    public IPlotterStyle createDefaultStyle() {
+        IPlotterStyle pstyle = plotterFactory.createPlotterStyle();
+        // Set the appearance of the axes.
+        pstyle.xAxisStyle().labelStyle().setBold(true);
+        pstyle.yAxisStyle().labelStyle().setBold(true);
+        pstyle.xAxisStyle().tickLabelStyle().setBold(true);
+        pstyle.yAxisStyle().tickLabelStyle().setBold(true);
+        pstyle.xAxisStyle().lineStyle().setColor("black");
+        pstyle.yAxisStyle().lineStyle().setColor("black");
+        pstyle.xAxisStyle().lineStyle().setThickness(2);
+        pstyle.yAxisStyle().lineStyle().setThickness(2);
+
+        // Set color settings.
+        pstyle.dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow");
+        pstyle.dataStyle().fillStyle().setParameter("showZeroHeightBins", Boolean.FALSE.toString());
+        pstyle.dataStyle().errorBarStyle().setVisible(false);
+        pstyle.setParameter("hist2DStyle", "colorMap");
+
+        // Force auto range to zero.
+        pstyle.yAxisStyle().setParameter("allowZeroSuppression", "false");
+        pstyle.xAxisStyle().setParameter("allowZeroSuppression", "false");
+
+        // Set the title style.
+        pstyle.titleStyle().textStyle().setFontSize(20);
+
+        // Draw caps on error bars.
+        pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", (new Float(1.0f)).toString());
+
+        // Turn off grid lines until explicitly enabled.
+        pstyle.gridStyle().setVisible(false);
+
+        // Return the style.
+        return pstyle;
+    }
 }

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java	Wed Mar 25 14:43:27 2015
@@ -20,7 +20,6 @@
 import org.hps.monitoring.ecal.eventdisplay.util.CrystalEvent;
 import org.hps.monitoring.ecal.eventdisplay.util.CrystalListener;
 import org.hps.monitoring.ecal.plots.EcalMonitoringUtilities;
-
 import org.hps.recon.ecal.ECalUtils;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.RawTrackerHit;
@@ -89,6 +88,7 @@
 	private static final String HIT_ENERGY_TITLE = "Hit Energy (GeV)";
 	private static final String CLUSTER_ENERGY_TITLE = "Cluster Energy (GeV)";
 	private static final String SIGNAL_AMPLITUDE_TITLE = "Signal Amplitude (mV)";
+	private String detectorName;
 	
 	/**
 	 * Sets the upper bound of the energy scales used by the driver.
@@ -161,6 +161,7 @@
 	 */
 	@Override
 	public void detectorChanged(Detector detector) {
+	    detectorName=detector.getName();
 		// Reset the AIDA tree directory.
 		aida.tree().cd("/");
 		
@@ -184,18 +185,18 @@
 			int column = EcalMonitoringUtilities.getColumnFromHistoID(ii);
 			
 			// Initialize the histograms for the current crystal channel.
-			channelEnergyPlot.add(aida.histogram1D(detector.getDetectorName() + " : "
+			channelEnergyPlot.add(aida.histogram1D(detectorName + " : "
 						+ inputCollection + " : Hit Energy : " + column + " " + row
 						+ ": " + ii, 100, -0.2, maxEch));
-			channelTimePlot.add(aida.histogram1D(detector.getDetectorName() + " : "
+			channelTimePlot.add(aida.histogram1D(detectorName + " : "
 						+ inputCollection + " : Hit Time : " + column + " " + row + ": "
 						+ ii, 100, 0, 400));     
-			channelTimeVsEnergyPlot.add(aida.histogram2D(detector.getDetectorName()
+			channelTimeVsEnergyPlot.add(aida.histogram2D(detectorName 
 					+ " : " + inputCollection + " : Hit Time Vs Energy : " + column
 					+ " " + row + ": " + ii, 100, 0, 400, 100, -0.2, maxEch));              
-			channelRawWaveform.add(aida.histogram1D(detector.getDetectorName() + " : "
+			channelRawWaveform.add(aida.histogram1D(detectorName  + " : "
 					+ inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii));
-			clusterEnergyPlot.add(aida.histogram1D(detector.getDetectorName() + " : "
+			clusterEnergyPlot.add(aida.histogram1D(detectorName  + " : "
 					+ inputCollection + " : Cluster Energy : " + column + " " + row
 					+ ": " + ii, 100, -0.2, maxEch));
 			
@@ -289,6 +290,50 @@
 	public void endOfData() {
 		viewer.setVisible(false);
 		viewer.dispose();
+		
+		
+
+        int row,column;
+        String hName;
+        System.out.println("EcalEventDisplay endOfData clear histograms");
+        for(int ii = 0; ii < NUM_CHANNELS; ii++) {
+            // The above instruction is a terrible hack, just to fill
+            // the arrayList with all the elements. They'll be initialized
+            // properly during the event readout, Since we want to account
+            // for possibly different raw waveform dimensions!
+            
+            //Get the x and y indices for the current channel.
+            row = EcalMonitoringUtilities.getRowFromHistoID(ii);
+            column = EcalMonitoringUtilities.getColumnFromHistoID(ii);
+            hName=detectorName + " : "
+                    + inputCollection + " : Hit Energy : " + column + " " + row
+                    + ": " + ii;
+            aida.tree().rm(hName);
+           
+            hName=detectorName + " : "
+                    + inputCollection + " : Hit Time : " + column + " " + row + ": "
+                    + ii;
+            aida.tree().rm(hName);
+            
+            hName=detectorName+ " : " + inputCollection + " : Hit Time Vs Energy : " + column
+                    + " " + row + ": " + ii;
+            aida.tree().rm(hName);
+            
+            hName=detectorName + " : "
+                    + inputCollection + " : Cluster Energy : " + column + " " + row
+                    + ": " + ii;
+            aida.tree().rm(hName);
+        
+            hName=detectorName + " : "
+                    + inputCollection + " : Cluster Energy : " + column + " " + row
+                    + ": " + ii;
+            aida.tree().rm(hName);
+        
+        
+        }
+      System.out.println("EcalEventDisplay endOfData clear histograms done");
+		
+		
 	}
 	
 	@Override

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalHitPlots.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalHitPlots.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalHitPlots.java	Wed Mar 25 14:43:27 2015
@@ -9,13 +9,13 @@
 import java.util.List;
 
 import org.hps.recon.ecal.ECalUtils;
+import org.hps.recon.ecal.triggerbank.SSPData;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.GenericObject;
 import org.lcsim.geometry.Detector;
 import org.lcsim.util.Driver;
 import org.lcsim.util.aida.AIDA;
-//import org.jfree.chart.ChartPanel;
 
 /**
  * The driver <code>EcalHitPlots</code> implements the histogram shown to the user 
@@ -45,20 +45,19 @@
     IHistogram1D topTimePlot, botTimePlot, orTimePlot;
     IHistogram1D topTrigTimePlot, botTrigTimePlot, orTrigTimePlot;
     IHistogram2D topTimePlot2D, botTimePlot2D, orTimePlot2D;
-   // IHistogram2D topX, botX, topY, botY;
-//    IHistogram2D hitNumberPlot;
-//    IHistogram2D occupancyPlot;
-   
-  
+    IHistogram2D hitNumberPlot;
+    IHistogram2D occupancyPlot;
+
+
     IPlotterFactory plotterFactory;
-  
+
     int eventn = 0;
 
     double maxE = 5000 * ECalUtils.MeV;
-    
+
     boolean logScale = false;
     boolean hide = false;
-    
+
     public void setInputCollection(String inputCollection) {
         this.inputCollection = inputCollection;
     }
@@ -66,7 +65,7 @@
     public void setMaxE(double maxE) {
         this.maxE = maxE;
     }
-    
+
 
     public void setLogScale(boolean logScale) {
         this.logScale = logScale;
@@ -74,35 +73,36 @@
 
     @Override
     protected void detectorChanged(Detector detector) {
-        
+
         System.out.println("Detector changed called: "+ detector.getClass().getName());
         aida.tree().cd("/");
         plotterFactory = aida.analysisFactory().createPlotterFactory("Ecal Hit Plots");
 
-       
-        
+
+
         // Setup plots.
-        hitCountPlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Hit Count In Event", 10, -0.5, 9.5);
-        hitTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time", 100, 0 * 4.0, 100 * 4.0);
-      //  hitNumberPlot = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Count");        
-      //  occupancyPlot = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Occupancy");
+        hitCountPlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Hit Count In Event", 30, -0.5, 29.5);     
+        hitNumberPlot = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Rate KHz");        
+        occupancyPlot = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Occupancy");
         topTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : First Hit Time, Top", 100, 0, 100 * 4.0);
         botTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : First Hit Time, Bottom", 100, 0, 100 * 4.0);
         orTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : First Hit Time, Or", 100, 0, 100 * 4.0);
 
-        topTrigTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Trigger Time, Top", 1024, 0, 4096);
-        botTrigTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Trigger Time, Bottom", 1024, 0, 4096);
+        
+        hitTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time", 100, 0 * 4.0, 100 * 4.0);
+        topTrigTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Trigger Time, Top", 101, -2, 402);
+        botTrigTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Trigger Time, Bottom", 101, -2, 402);
         orTrigTimePlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Trigger Time, Or", 1024, 0, 4096);
 
-        topTimePlot2D = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time vs. Trig Time, Top", 100, 0, 100 * 4.0, 512, 0, 4096);
-        botTimePlot2D = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time vs. Trig Time, Bottom", 100, 0, 100 * 4.0, 512, 0, 4096);
-        orTimePlot2D = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time vs. Trig Time, Or", 100, 0, 100 * 4.0, 512, 0, 4096);
+        topTimePlot2D = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time vs. Trig Time, Top", 100, 0, 100 * 4.0, 101, -2, 402);
+        botTimePlot2D = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time vs. Trig Time, Bottom", 100, 0, 100 * 4.0, 101, -2, 402);
+        orTimePlot2D = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Hit Time vs. Trig Time, Or", 100, 0, 100 * 4.0, 101, -2, 402);
 
         hitEnergyPlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Hit Energy", 100, -0.1, maxE);
         hitMaxEnergyPlot = aida.histogram1D(detector.getDetectorName() + " : " + inputCollection + " : Maximum Hit Energy In Event", 100, -0.1, maxE);
 
-        
-    
+
+
         // Setup the plotter.
         plotter = plotterFactory.create("Hit Counts");
         plotter.setTitle("Hit Counts");
@@ -110,65 +110,65 @@
         pstyle.setParameter("hist2DStyle", "colorMap");
         pstyle.dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow");
         pstyle.dataStyle().fillStyle().setParameter("showZeroHeightBins",Boolean.FALSE.toString());
-        
-        
+
+
         // Create the plotter regions.
         plotter.createRegions(2,2);
-//        plotter.region(0).plot(hitNumberPlot);
+        plotter.region(0).plot(hitNumberPlot);
         plotter.region(1).plot(hitTimePlot,pstyle);
-//        plotter.region(2).plot(occupancyPlot,pstyle);
+        plotter.region(2).plot(occupancyPlot,pstyle);
         plotter.region(3).plot(hitCountPlot,pstyle);
-      
-        
-     
+
+
+
         if (logScale){
-        	 pstyle.zAxisStyle().setParameter("scale", "log");
+            pstyle.zAxisStyle().setParameter("scale", "log");
         }
         else pstyle.zAxisStyle().setParameter("scale", "lin");
-//        plotter.region(0).plot(hitNumberPlot,pstyle);
-        
-        
+        //        plotter.region(0).plot(hitNumberPlot,pstyle);
+
+
         // Setup the plotter.
         plotter2 = plotterFactory.create("Hit Energies");
         plotter2.setTitle("Hit Energies");
         pstyle.zAxisStyle().setParameter("scale", "lin");
-        
+
         if (logScale) {
-        	 pstyle.yAxisStyle().setParameter("scale", "log");
+            pstyle.yAxisStyle().setParameter("scale", "log");
         }
         else  pstyle.yAxisStyle().setParameter("scale", "lin");
         // Create the plotter regions.
         plotter2.createRegions(1, 2);
         plotter2.region(0).plot(hitEnergyPlot,pstyle);
         plotter2.region(1).plot(hitMaxEnergyPlot,pstyle); 
-               
+
         plotter3 = plotterFactory.create("Hit Times");
         plotter3.setTitle("Hit Times");
         plotter3.createRegions(3, 3);      
-        
+
         if (logScale) {
-       	 pstyle.yAxisStyle().setParameter("scale", "log");
-       }
-       else  pstyle.yAxisStyle().setParameter("scale", "lin");
-        
+            pstyle.yAxisStyle().setParameter("scale", "log");
+        }
+        else  pstyle.yAxisStyle().setParameter("scale", "lin");
+
         plotter3.region(0).plot(topTimePlot,pstyle);
         plotter3.region(1).plot(botTimePlot,pstyle);
         plotter3.region(2).plot(orTimePlot,pstyle);
         plotter3.region(3).plot(topTrigTimePlot,pstyle);
         plotter3.region(4).plot(botTrigTimePlot,pstyle);
         plotter3.region(5).plot(orTrigTimePlot,pstyle);
-        
+
         pstyle.yAxisStyle().setParameter("scale", "lin");
         if (logScale){
-        	 pstyle.zAxisStyle().setParameter("scale", "log");
+            pstyle.zAxisStyle().setParameter("scale", "log");
         }
         else pstyle.zAxisStyle().setParameter("scale", "lin");
-      
+
         plotter3.region(6).plot(topTimePlot2D,pstyle);
         plotter3.region(7).plot(botTimePlot2D,pstyle);
         plotter3.region(8).plot(orTimePlot2D,pstyle);
-       
-        
+
+
         if (!hide) {
             plotter.show();
             plotter2.show();
@@ -178,28 +178,29 @@
 
     @Override
     public void process(EventHeader event) {
-        
-        
+
+
         int orTrigTime=4097;
         int topTrigTime=4097;
         int botTrigTime=4097;
-        
+
         if (event.hasCollection(GenericObject.class, "TriggerBank")) {
             List<GenericObject> triggerList = event.get(GenericObject.class, "TriggerBank");
             if (!triggerList.isEmpty()) {
                 GenericObject triggerData = triggerList.get(0);
-/*  mgraham12/14/2014 ...comment this out for now as it seems to through a null pointer
+               
                 if (triggerData instanceof SSPData){ 
                 	orTrigTime=((SSPData)triggerData).getOrTrig();
                 	topTrigTime=((SSPData)triggerData).getTopTrig();
                 	botTrigTime =((SSPData)triggerData).getBotTrig(); 
-                    
+
+                	
                 	orTrigTimePlot.fill(orTrigTime);
                     topTrigTimePlot.fill(topTrigTime);
                 	botTrigTimePlot.fill(botTrigTime);
-                	
+
                 }       
-                */
+                
             }//end if triggerList isEmpty
         }
 
@@ -215,11 +216,11 @@
             double orTime = Double.POSITIVE_INFINITY;
             for (CalorimeterHit hit : hits) {
 
-               
+
                 hitEnergyPlot.fill(hit.getRawEnergy());
                 hitTimePlot.fill(hit.getTime());
-               
-                
+
+
                 if (hit.getTime() < orTime) {
                     orTime = hit.getTime();
                 }
@@ -233,7 +234,7 @@
                     maxEnergy = hit.getRawEnergy();
                 }
             }
-            
+
             if (orTime != Double.POSITIVE_INFINITY) {
                 orTimePlot.fill(orTime);
                 orTimePlot2D.fill(orTime, orTrigTime);
@@ -247,22 +248,6 @@
                 botTimePlot2D.fill(botTime, botTrigTime);
             }
             hitMaxEnergyPlot.fill(maxEnergy);
-        
-            for (int i = 0; i < hits.size(); i++) {
-                CalorimeterHit hit1 = hits.get(i);
-                int x1 = hit1.getIdentifierFieldValue("ix");
-                int y1 = hit1.getIdentifierFieldValue("iy");
-                for (int j = i + 1; j < hits.size(); j++) {
-                    CalorimeterHit hit2 = hits.get(j);
-                    int x2 = hit2.getIdentifierFieldValue("ix");
-                    int y2 = hit2.getIdentifierFieldValue("iy");
-                    if ((Math.abs(x1 - x2) <= 1 || x1 * x2 == -1) && (Math.abs(y1 - y2) <= 1)) {
-                        if (x1 != x2 || y1 != y2) {
-                         //  edgePlot.fill((x1 + x2) / 2.0, (y1 + y2) / 2.0);
-                        }
-                    }
-                }
-            }
         } else {
             hitCountPlot.fill(0);
         }         
@@ -277,54 +262,54 @@
     {
         this.hide=hide;
     }
-        
-    
+
+
     /**
-	 * Initializes the default style for plots.
-	 * @return Returns an <code>IPlotterStyle</code> object that
-	 * represents the default style for plots.
-	 */
-	public IPlotterStyle createDefaultStyle() {
-		IPlotterStyle pstyle = plotterFactory.createPlotterStyle();
-		// Set the appearance of the axes.
-		pstyle.xAxisStyle().labelStyle().setBold(true);
-		pstyle.yAxisStyle().labelStyle().setBold(true);
-		pstyle.xAxisStyle().tickLabelStyle().setBold(true);
-		pstyle.yAxisStyle().tickLabelStyle().setBold(true);
-		pstyle.xAxisStyle().lineStyle().setColor("black");
-		pstyle.yAxisStyle().lineStyle().setColor("black");
-		pstyle.xAxisStyle().lineStyle().setThickness(2);
-		pstyle.yAxisStyle().lineStyle().setThickness(2);
-		
-		// Set color settings.
-		pstyle.dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow");
-		pstyle.dataStyle().fillStyle().setParameter("showZeroHeightBins", Boolean.FALSE.toString());
-		pstyle.dataStyle().errorBarStyle().setVisible(false);
-		pstyle.setParameter("hist2DStyle", "colorMap");
-		
-		// Force auto range to zero.
-		pstyle.yAxisStyle().setParameter("allowZeroSuppression", "false");
-		pstyle.xAxisStyle().setParameter("allowZeroSuppression", "false");
-		
-		// Set the title style.
-		pstyle.titleStyle().textStyle().setFontSize(20);
-		
-		// Draw caps on error bars.
-		pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", (new Float(1.0f)).toString());
-		
-		// Turn off grid lines until explicitly enabled.
-		pstyle.gridStyle().setVisible(false);
-		
-		// Return the style.
-		return pstyle;
-	}
-    
-    
-    
-    
-    
-    
-    
+     * Initializes the default style for plots.
+     * @return Returns an <code>IPlotterStyle</code> object that
+     * represents the default style for plots.
+     */
+    public IPlotterStyle createDefaultStyle() {
+        IPlotterStyle pstyle = plotterFactory.createPlotterStyle();
+        // Set the appearance of the axes.
+        pstyle.xAxisStyle().labelStyle().setBold(true);
+        pstyle.yAxisStyle().labelStyle().setBold(true);
+        pstyle.xAxisStyle().tickLabelStyle().setBold(true);
+        pstyle.yAxisStyle().tickLabelStyle().setBold(true);
+        pstyle.xAxisStyle().lineStyle().setColor("black");
+        pstyle.yAxisStyle().lineStyle().setColor("black");
+        pstyle.xAxisStyle().lineStyle().setThickness(2);
+        pstyle.yAxisStyle().lineStyle().setThickness(2);
+
+        // Set color settings.
+        pstyle.dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow");
+        pstyle.dataStyle().fillStyle().setParameter("showZeroHeightBins", Boolean.FALSE.toString());
+        pstyle.dataStyle().errorBarStyle().setVisible(false);
+        pstyle.setParameter("hist2DStyle", "colorMap");
+
+        // Force auto range to zero.
+        pstyle.yAxisStyle().setParameter("allowZeroSuppression", "false");
+        pstyle.xAxisStyle().setParameter("allowZeroSuppression", "false");
+
+        // Set the title style.
+        pstyle.titleStyle().textStyle().setFontSize(20);
+
+        // Draw caps on error bars.
+        pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", (new Float(1.0f)).toString());
+
+        // Turn off grid lines until explicitly enabled.
+        pstyle.gridStyle().setVisible(false);
+
+        // Return the style.
+        return pstyle;
+    }
+
+
+
+
+
+
+
 }
 
-   
+

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringPlots.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringPlots.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringPlots.java	Wed Mar 25 14:43:27 2015
@@ -42,10 +42,12 @@
     IHistogram2D clusterCountDrawPlot;
     int eventRefreshRate = 1;
     int eventn = 0;
+    int thisEventN,prevEventN;
+
     boolean hide = false;
-    boolean accumulateHits = false;
     long thisTime,prevTime;
-    
+    double thisEventTime,prevEventTime;
+
     public EcalMonitoringPlots() {
     }
 
@@ -61,9 +63,7 @@
         this.hide = hide;
     }
 
-    public void setAccumulateHits(boolean accumulateHits) {
-        this.accumulateHits = accumulateHits;
-    }
+
     /**
      * Set the refresh rate for histograms in this driver
      * @param eventRefreshRate: the refresh rate, defined as number of events to accumulate before
@@ -80,21 +80,20 @@
         // Setup plots.
         aida.tree().cd("/");
         String hitCountDrawPlotTitle;
-       if (accumulateHits)  hitCountDrawPlotTitle = detector.getDetectorName() + " : " + inputCollection + " : Hit Count (accumulated)";
-       else hitCountDrawPlotTitle = detector.getDetectorName() + " : " + inputCollection + " : Hit Count (refreshed)";
-       
-       hitCountDrawPlot = aida.histogram2D(hitCountDrawPlotTitle, 47, -23.5, 23.5, 11, -5.5, 5.5);
-       hitCountFillPlot = makeCopy(hitCountDrawPlot);
+        hitCountDrawPlotTitle = detector.getDetectorName() + " : " + inputCollection + " : Hit Rate KHz";
+
+        hitCountDrawPlot = aida.histogram2D(hitCountDrawPlotTitle, 47, -23.5, 23.5, 11, -5.5, 5.5);
+        hitCountFillPlot = makeCopy(hitCountDrawPlot);
         occupancyDrawPlot = aida.histogram2D(detector.getDetectorName() + " : " + inputCollection + " : Occupancy", 47, -23.5, 23.5, 11, -5.5, 5.5);
-        clusterCountDrawPlot = aida.histogram2D(detector.getDetectorName() + " : " + clusterCollection + " : Cluster Center Count", 47, -23.5, 23.5, 11, -5.5, 5.5);
+        clusterCountDrawPlot = aida.histogram2D(detector.getDetectorName() + " : " + clusterCollection + " : Cluster Rate KHz", 47, -23.5, 23.5, 11, -5.5, 5.5);
         clusterCountFillPlot = makeCopy(clusterCountDrawPlot);
 
-      
+
         NoccupancyFill=1; //to avoid a "NaN" at beginning
         for (int ii = 0; ii < (11 * 47); ii++) {
             int row = EcalMonitoringUtilities.getRowFromHistoID(ii);
             int column = EcalMonitoringUtilities.getColumnFromHistoID(ii);
-            occupancyFill[ii]=0;
+            occupancyFill[ii]=0.;
         }
 
         // Create the plotter regions.
@@ -122,24 +121,18 @@
         }
         prevTime=0; //init the time 
         thisTime=0; //init the time 
+
+        thisEventN=0;
+        prevEventN=0;
+
+        thisEventTime=0;
+        prevEventTime=0;
     }
 
     public void process(EventHeader event) {
         int nhits = 0;
         int chits[] = new int[11 * 47];
-        /*
-         * if (event.hasCollection(BaseRawCalorimeterHit.class, inputCollection)) {
-         * List<BassRawCalorimeterHit> hits = event.get(BaseRawCalorimeterHit.class,
-         * inputCollection); for (BaseRawCalorimeterHit hit : hits) { int
-         * column=hit.getIdentifierFieldValue("ix"); int row=hit.getIdentifierFieldValue("iy"); int
-         * id=EcalMonitoringUtils.getHistoIDFromRowColumn(row, column);
-         * hitCountFillPlot.fill(column,row); chits[id]++; nhits++; } } if
-         * (event.hasCollection(RawTrackerHit.class, inputCollection)) { List<RawTrackerHit> hits =
-         * event.get(RawTrackerHit.class, inputCollection); for (RawTrackerHit hit : hits) { int
-         * column=hit.getIdentifierFieldValue("ix"); int row=hit.getIdentifierFieldValue("iy"); int
-         * id=EcalMonitoringUtils.getHistoIDFromRowColumn(row, column);
-         * hitCountFillPlot.fill(column,row); chits[id]++; nhits++; } }
-         */
+
         if (event.hasCollection(CalorimeterHit.class, inputCollection)) {
             List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputCollection);
             for (CalorimeterHit hit : hits) {
@@ -148,15 +141,15 @@
                 int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(row, column);
                 hitCountFillPlot.fill(column, row);
                 {
-                 chits[id]++;
-                 nhits++;
+                    chits[id]++;
+                    nhits++;
                 }
             }
         }
 
         if (nhits > 0) {
             for (int ii = 0; ii < (11 * 47); ii++) {
-                occupancyFill[ii]+=1.*chits[ii]/nhits;
+                occupancyFill[ii]+=(1.*chits[ii])/nhits;
             }
         }
 
@@ -166,16 +159,32 @@
                 clusterCountFillPlot.fill(cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"), cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"));
             }
         }
-       
+
         thisTime=System.currentTimeMillis()/1000;
-        
+        thisEventN=event.getEventNumber();
+        thisEventTime=event.getTimeStamp()/1E9;
         if ((thisTime-prevTime)>eventRefreshRate){
-        	prevTime=thisTime;
-        	redraw();
-        	NoccupancyFill=0;
+            double scale=1.;
+
+            if (NoccupancyFill>0){
+                scale=(thisEventN-prevEventN)/NoccupancyFill;
+                scale=scale/(thisEventTime-prevEventTime);
+                scale/=1000. ; //do KHz
+            }
+            //System.out.println("Event: "+thisEventN+" "+prevEventN);
+            //System.out.println("Time: "+thisEventTime+" "+prevEventTime);
+            // System.out.println("Monitor: "+thisTime+" "+prevTime+" "+NoccupancyFill);
+
+            hitCountFillPlot.scale(scale);
+            clusterCountFillPlot.scale(scale);
+            redraw();
+            prevTime=thisTime;
+            prevEventN=thisEventN;
+            prevEventTime=thisEventTime;
+            NoccupancyFill=0;
         }
         else{
-        	NoccupancyFill++;
+            NoccupancyFill++;
         }
     }
 
@@ -190,22 +199,21 @@
         plotter.region(0).clear();
         plotter.region(0).plot(hitCountDrawPlot);
         plotter.region(0).refresh();
-        
-        if (!accumulateHits){
-        	hitCountFillPlot.reset();
-        }
+        hitCountFillPlot.reset();
+
         clusterCountDrawPlot.reset();
         clusterCountDrawPlot.add(clusterCountFillPlot);
         plotter.region(1).clear();
         plotter.region(1).plot(clusterCountDrawPlot);
         plotter.region(1).refresh();
-        
+        clusterCountFillPlot.reset();
+
         occupancyDrawPlot.reset();
         for (int id = 0; id < (47 * 11); id++) {
             int row = EcalMonitoringUtilities.getRowFromHistoID(id);
             int column = EcalMonitoringUtilities.getColumnFromHistoID(id);
             double mean = occupancyFill[id]/NoccupancyFill;
-            
+
             occupancyFill[id]=0;
             if ((row != 0) && (column != 0) && (!EcalMonitoringUtilities.isInHole(row, column)))
                 occupancyDrawPlot.fill(column, row, mean);

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringUtilities.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringUtilities.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalMonitoringUtilities.java	Wed Mar 25 14:43:27 2015
@@ -35,6 +35,8 @@
                 return true;
             }
         }
+        else if (row == 0) return true;
+        else if (column ==0) return true;
         return false;
     }
     

Modified: java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalPedestalViewer.java
 =============================================================================
--- java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalPedestalViewer.java	(original)
+++ java/branches/prod/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalPedestalViewer.java	Wed Mar 25 14:43:27 2015
@@ -31,7 +31,7 @@
 public class EcalPedestalViewer extends Driver implements CrystalListener, ActionListener {
 
     // this has to match the one in EcalPedstalCalculator:
-    private String histoNameFormat = "Ecal/Pedestals/Mode7/ped%3d";
+    private String histoNameFormat = "Ecal/Pedestals/Mode7/ped%03d";
     
 	private AIDA aida = AIDA.defaultInstance();	
 	private IPlotter plotter;
@@ -39,12 +39,33 @@
 	private IPlotterStyle pstyle;
 	private PEventViewer viewer;
 
+	static final String[] colors={"red","black","blue","green","yellow","pink","cyan","magenta","brown"};
+	static final int nRows=3;
+	static final int nColumns=3;
+	private int theRegion=0;
+	
 	@Override
 	public void detectorChanged(Detector detector) {
 		plotterFactory = aida.analysisFactory().createPlotterFactory("ECal Peds");
 		plotter = plotterFactory.create("ECal Peds");
-		plotter.createRegions(1,1);
+		plotter.createRegions(nColumns,nRows);
+		// Plot dummmy histos, else null plotter regions later:
+		for (int ii=0; ii<nColumns*nRows; ii++) {
+  	    	plotter.region(ii).plot(aida.histogram1D("ASDF"+ii,100,11e9,11e11));
+		}
 		plotter.show();
+		
+		pstyle=plotterFactory.createPlotterStyle();
+		pstyle.xAxisStyle().labelStyle().setBold(true);
+		pstyle.yAxisStyle().labelStyle().setBold(true);
+		pstyle.xAxisStyle().tickLabelStyle().setBold(true);
+		pstyle.yAxisStyle().tickLabelStyle().setBold(true);
+		pstyle.xAxisStyle().lineStyle().setColor("black");
+		pstyle.yAxisStyle().lineStyle().setColor("black");
+		pstyle.xAxisStyle().lineStyle().setThickness(2);
+		pstyle.yAxisStyle().lineStyle().setThickness(2);
+		pstyle.dataStyle().errorBarStyle().setThickness(0);
+		pstyle.legendBoxStyle().setVisible(false);
 	}
 	
 	@Override
@@ -78,8 +99,12 @@
 	    if (hist==null) {
 	        System.err.println("Running the Driver?");
 	    } else {
-	        plotter.region(0).clear();
-	        plotter.region(0).plot(hist,pstyle);
+     	    hist.setTitle(String.format("(%d,%d)",ecalPoint.x,ecalPoint.y));
+            pstyle.dataStyle().lineStyle().setParameter("color", colors[theRegion%colors.length]);
+	        plotter.region(theRegion).clear();
+	        plotter.region(theRegion).plot(hist,pstyle);
+	        plotter.region(theRegion).refresh();
+	        theRegion=(theRegion+1)%(nColumns*nRows);
 	    }
 	}
 	

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java	Wed Mar 25 14:43:27 2015
@@ -13,6 +13,7 @@
 
 import org.jfree.chart.ChartPanel;
 import org.jfree.chart.JFreeChart;
+import org.jfree.data.time.RegularTimePeriod;
 
 /**
  * This class implements an AIDA <code>IPlotterFactory</code> for the monitoring application. It
@@ -22,7 +23,10 @@
  * by the MonitoringApplication before any calls to AIDA are made from Drivers.
  */
 public class MonitoringPlotFactory extends PlotterFactory {
-
+    
+    // Global plotter registry.
+    static PlotterRegistry plotters = new PlotterRegistry();
+    
     // The name of the factory which will be used in naming tabs in the monitoring app.
     String name = null;
 
@@ -32,14 +36,22 @@
     // Root pane where this factory's top-level tab will be inserted.
     private static JTabbedPane rootPane = null;
 
+    // The region listener for handling mouse clicks in a region.
     private static PlotterRegionListener regionListener;
-
+    
+    // The current tab index.
+    int tabIndex;
+
+    /**
+     * Set the plot region listener.
+     * @param regionListener The plot region listener.
+     */
     public static void setPlotterRegionListener(PlotterRegionListener regionListener) {
         MonitoringPlotFactory.regionListener = regionListener;
     }
 
     /**
-     * Class constructor.
+     * Class constructor for unnamed factory.
      */
     MonitoringPlotFactory() {
         super();
@@ -50,7 +62,7 @@
     }
 
     /**
-     * Class constructor.
+     * Class constructor for named factory.
      * @param name The name of the factory.
      */
     MonitoringPlotFactory(String name) {
@@ -62,11 +74,16 @@
             addPlotterRegionListener(regionListener);
     }
 
+    /**
+     * Setup the root GUI pane of this factory for display of plots in tabs.
+     * @param name The tab's label.
+     */
     private void setupRootPane(String name) {
         // FIXME: Hack to disregard call from an AIDA related class.
         if (!(new RuntimeException()).getStackTrace()[2].getClassName().equals("hep.aida.ref.plotter.style.registry.StyleStoreXMLReader")) {
             rootPane.addTab(name, tabs);
-            rootPane.setTabComponentAt(rootPane.getTabCount() - 1, new JLabel(name));
+            tabIndex = rootPane.getTabCount() - 1;
+            rootPane.setTabComponentAt(tabIndex, new JLabel(name));
         }
     }
 
@@ -97,36 +114,90 @@
         MonitoringPlotFactory.rootPane = rootPane;
     }
 
+    /**
+     * Setup a plotter tab.
+     * @param plotterName The name of the plotter.
+     * @param plotter The IPlotter which will plot into the tab.
+     */
     private void setupPlotterTab(String plotterName, IPlotter plotter) {
+        
+        // Setup the plotter's GUI pane and tab.
         JPanel plotterPanel = new JPanel(new BorderLayout());
         plotterPanel.add(PlotterUtilities.componentForPlotter(plotter), BorderLayout.CENTER);
         tabs.addTab(plotterName, plotterPanel);
-        tabs.setTabComponentAt(tabs.getTabCount() - 1, new JLabel(plotterName));
-    }
-
+        int plotterIndex = tabs.getTabCount() - 1;
+        tabs.setTabComponentAt(plotterIndex, new JLabel(plotterName));
+        
+        // Register plotter globally with its tab indices.
+        plotters.register(plotter, tabIndex, plotterIndex);
+    }
+
+    /**
+     * Add a <code>JFreeChart</code> to one of the tabs.
+     * @param chart The JFreeChart object to add.
+     */
     private void addChart(JFreeChart chart) {
         ChartPanel panel = new ChartPanel(chart);
         tabs.addTab(chart.getTitle().getText(), panel);
         tabs.setTabComponentAt(tabs.getTabCount() - 1, new JLabel(chart.getTitle().getText()));
     }
-
-    /**
-     * Create a strip chart using a JFreeChart implementation. It will be automatically updated from
-     * a {@link StripChartUpdater}. Similar to AIDA plots, the chart will be given a sub-tab in the
-     * tab of this factory.
-     * 
-     * @param title The title of the chart.
-     * @param yAxisLabel The y axis label.
-     * @param size The buffer size of the series which determines how much data displays.
-     * @param updater The updater which will update the chart in real time.
-     * @return The modified <tt>StripChartUpdater</tt> which points to the new chart.
-     */
-    public StripChartUpdater createStripChart(String title, String yAxisLabel, int size, StripChartUpdater updater) {
-        JFreeChart stripChart = StripChartBuilder.createDynamicTimeSeriesChart(title, yAxisLabel, size);
-        stripChart.getLegend().setVisible(false); /* Legend turned off for now. */
-        addChart(stripChart);
-        updater.setChart(stripChart);
+    
+    /**
+     * This creates a strip chart with full parameter settings, which will automatically
+     * update at a certain time interval.
+     * @param name The title of the chart.
+     * @param rangeLabel The range axis label text.
+     * @param seriesCount The number of series in the data set.
+     * @param seriesNames The names of the series (if non-null the length must match seriesCount).
+     * @param itemCount The maximum number of items in the series.
+     * @param timeBase The time unit for updates.
+     * @param valueProvider The interface for providing the series values.
+     * @param rangeView The view in the domain axis around the current data point (applied to plus and minus).
+     * @return The StripChartUpdater for the chart.
+     */
+    public StripChartUpdater createStripChart(
+            String name, 
+            String rangeLabel,
+            int seriesCount, 
+            String[] seriesNames,
+            int itemCount,
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider,
+            long rangeView) {
+        StripChartUpdater updater = StripChartBuilder.createDynamicTimeSeriesChart(
+                name, 
+                rangeLabel, 
+                seriesCount, 
+                seriesNames,
+                itemCount, 
+                timeBase, 
+                valueProvider, 
+                rangeView);
+        addChart(updater.getChart());
         return updater;
+    }
+    
+    /**
+     * Create a strip chart with simple parameter settings.
+     * @param name The title of the strip chart.
+     * @param seriesCount The number of series in the data set.
+     * @param timeBase The time interval for updating.
+     * @param valueProvider The interface for providing values.
+     * @return The StripChartUpdater for the chart.
+     */
+    public StripChartUpdater createStripChart(
+            String name, 
+            int seriesCount, 
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider) {
+        return createStripChart(
+                name, "Values", 
+                seriesCount, 
+                null, 
+                9999, 
+                timeBase, 
+                valueProvider, 
+                10000L);
     }
 
     /**
@@ -143,5 +214,24 @@
         stripChart.getLegend().setVisible(false); /* Legend turned off for now. */
         addChart(stripChart);
         return stripChart;
-    }
-}
+    }       
+    
+    public JFreeChart createTimeSeriesChart(
+            String title, 
+            String yAxisLabel, 
+            int seriesCount,
+            String[] datasetNames,
+            double rangeSize) {
+        JFreeChart chart = StripChartBuilder.createTimeSeriesChart(title, yAxisLabel, seriesCount, datasetNames, rangeSize);
+        addChart(chart);
+        return chart;
+    }
+    
+    /**
+     * Get the global registry of plotters.
+     * @return The global plotter registry.
+     */
+    public static PlotterRegistry getPlotterRegistry() {
+        return plotters;
+    }
+}

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java	Wed Mar 25 14:43:27 2015
@@ -1,45 +1,90 @@
 package org.hps.monitoring.plotting;
-
-import java.util.Date;
 
 import org.jfree.chart.ChartFactory;
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.axis.NumberAxis;
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.data.time.DynamicTimeSeriesCollection;
-import org.jfree.data.time.Second;
+import org.jfree.data.time.RegularTimePeriod;
 import org.jfree.data.time.TimeSeries;
 import org.jfree.data.time.TimeSeriesCollection;
 
 /**
- * Utility methods for building strip charts using JFreeChart backend.
+ * Utility methods for building strip charts using JFreeChart back end.
  */
 public final class StripChartBuilder {
 
     private StripChartBuilder() {
     }
-
-    /**
-     * This creates a strip chart that will be updated at fixed intervals from a timer.
-     * @param title
-     * @param yAxisLabel
-     * @param size
-     * @return
-     */
-    public static JFreeChart createDynamicTimeSeriesChart(String title, String yAxisLabel, int size) {
-        final DynamicTimeSeriesCollection dataset = new DynamicTimeSeriesCollection(1, size, new Second());
-        dataset.setTimeBase(new Second(new Date()));
-        dataset.addSeries(new float[] {}, 0, "Default Dataset");
-
-        final JFreeChart result = ChartFactory.createTimeSeriesChart(title, "hh:mm:ss", yAxisLabel, dataset, true, true, false);
-        final XYPlot plot = result.getXYPlot();
-        plot.getDomainAxis().setAutoRange(true);
-        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
-        rangeAxis.setAutoRange(true);
-        rangeAxis.setAutoRangeIncludesZero(true);
-        return result;
+    
+    /**
+     * Create a strip chart with simple parameter settings.
+     * @param name The title of the strip chart.
+     * @param seriesCount The number of series in the data set.
+     * @param timeBase The time interval for updating.
+     * @param valueProvider The interface for providing values.
+     * @return The StripChartUpdater for the chart.
+     */
+    static StripChartUpdater createDynamicTimeSeriesChart(
+            String name, 
+            int seriesCount, 
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider) {
+        return createDynamicTimeSeriesChart(name, "Values", seriesCount, null, 9999, timeBase, valueProvider, 10000L);
     }
-
+    
+    /**
+     * This creates a strip chart with full parameter settings, which will automatically
+     * update at a certain time interval.
+     * @param name The title of the chart.
+     * @param rangeLabel The range axis label text.
+     * @param seriesCount The number of series in the data set.
+     * @param seriesNames The names of the series (if non-null the length must match seriesCount).
+     * @param itemCount The maximum number of items in the series.
+     * @param timeBase The time unit for updates.
+     * @param valueProvider The interface for providing the series values.
+     * @param rangeView The view in the domain axis around the current data point (milliseconds).
+     * @return The StripChartUpdater for the chart.
+     */
+    static StripChartUpdater createDynamicTimeSeriesChart(
+            String name, 
+            String rangeLabel,
+            int seriesCount, 
+            String[] seriesNames,
+            int itemCount,
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider,
+            long rangeView) {
+                
+        if (seriesNames != null && seriesCount != seriesNames.length) {
+            throw new IllegalArgumentException("seriesNames is wrong length");
+        }
+        final DynamicTimeSeriesCollection dataset = new DynamicTimeSeriesCollection(seriesCount, itemCount, timeBase);
+        dataset.setTimeBase(timeBase);
+        for (int series = 0; series < seriesCount; series++) {
+            String seriesName = name + " " + series;
+            if (seriesNames != null) {
+                seriesName = seriesNames[series];
+            }
+            dataset.addSeries(new float[] {}, series, seriesName);
+        }
+        
+        final JFreeChart chart = ChartFactory.createTimeSeriesChart(name, "hh:mm:ss", rangeLabel, dataset, false, false, false);
+        
+        chart.getXYPlot().getRangeAxis().setAutoRange(true);
+
+        StripChartUpdater updater = new StripChartUpdater(
+                chart, 
+                valueProvider,
+                rangeView,
+                timeBase
+                );
+        
+        updater.start();
+        
+        return updater;
+    }
+        
     /**
      * This should be used when the time period for updating is variable.
      * 
@@ -47,13 +92,18 @@
      * 
      * <code>sensorSeries.add(new Minute(new Date()), newData);</code>
      * 
-     * @param title
-     * @param yAxisLabel
-     * @param maxAge
-     * @param maxCount
-     * @return
-     */
-    public static JFreeChart createTimeSeriesChart(String title, String yAxisLabel, int maxAge, int maxCount, int rangeSize) {
+     * @param title The title of the chart.
+     * @param yAxisLabel The range axis label.
+     * @param maxAge The maximum age of an item.
+     * @param maxCount The maximum count of items in the single data set series.
+     * @return The chart that was created.
+     */
+    static JFreeChart createTimeSeriesChart(
+            String title, 
+            String yAxisLabel, 
+            int maxAge, 
+            int maxCount,
+            int rangeSize) {
 
         TimeSeriesCollection dataset = new TimeSeriesCollection();
         TimeSeries timeSeries = new TimeSeries("Default Dataset");
@@ -61,7 +111,14 @@
         timeSeries.setMaximumItemCount(maxCount);
         dataset.addSeries(timeSeries);
 
-        final JFreeChart result = ChartFactory.createTimeSeriesChart(title, "hh:mm:ss", yAxisLabel, dataset, true, true, false);
+        final JFreeChart result = ChartFactory.createTimeSeriesChart(
+                title, 
+                "hh:mm:ss", 
+                yAxisLabel, 
+                dataset, 
+                true, 
+                false, 
+                false);
         final XYPlot plot = result.getXYPlot();
         plot.getDomainAxis().setAutoRange(true);
         plot.getDomainAxis().setAutoRangeMinimumSize(rangeSize);
@@ -69,6 +126,75 @@
         rangeAxis.setAutoRange(true);
         rangeAxis.setAutoRangeIncludesZero(true);
         return result;
-    }
-
+    }    
+    
+    /**
+     * <p>
+     * This can be used to create a strip chart with multiple <code>TimeSeries</code> 
+     * in the data set.
+     * <p>
+     * To update a chart of this type, use the following types of method calls:
+     * <pre>
+     * dataset.getSeries(0).add(new Second(time), value1);
+     * dataset.getSeries(1).add(new Second(time), value2);
+     * </pre>
+     * <p>
+     * It is not updated manually but will refresh will values are added to the backing dataset.
+     * 
+     * @param title The title of the chart.
+     * @param yAxisLabel The range axis label.
+     * @param seriesCount The number of series in the dataset.
+     * @param datasetNames The names of the datasets (can be null to use defaults).
+     * @param rangeSize The range of values to show for auto-ranging in domain axis (in milliseconds).
+     * @return The chart that was created.
+     */
+    static JFreeChart createTimeSeriesChart(
+            String title, 
+            String yAxisLabel, 
+            int seriesCount,
+            String[] datasetNames,
+            double rangeSize) {
+
+        // If dataset names are given, the length must match the number of series requested.
+        if (datasetNames != null && seriesCount != datasetNames.length) {
+            throw new IllegalArgumentException("datasetNames has wrong length: " + datasetNames.length);
+        }
+        
+        // Create the dataset and add empty series to it.
+        TimeSeriesCollection dataset = new TimeSeriesCollection();
+        for (int i = 0; i < seriesCount; i++) {
+            
+            // Uses title for dataset names if none given explicitly.            
+            String datasetName = title;
+            
+            if (datasetNames != null) {
+                // Use the explicitly given dataset names.
+                datasetName = datasetNames[i];
+            }
+            TimeSeries timeSeries = new TimeSeries(datasetName);
+            dataset.addSeries(timeSeries);
+        }
+               
+        // Create the chart.
+        final JFreeChart result = ChartFactory.createTimeSeriesChart(
+                title, 
+                "hh:mm:ss", 
+                yAxisLabel, 
+                dataset, 
+                true, 
+                false, 
+                false);
+        final XYPlot plot = result.getXYPlot();
+
+        // Configure range axis.
+        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
+        rangeAxis.setAutoRange(true);
+        rangeAxis.setAutoRangeIncludesZero(true);
+        
+        // Configure domain axis.
+        plot.getDomainAxis().setAutoRange(true);
+        plot.getDomainAxis().setAutoRangeMinimumSize(rangeSize);
+        
+        return result;
+    }        
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java	Wed Mar 25 14:43:27 2015
@@ -1,38 +1,67 @@
 package org.hps.monitoring.plotting;
 
+import java.util.Date;
 import java.util.Timer;
 import java.util.TimerTask;
 
 import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
 import org.jfree.data.time.DynamicTimeSeriesCollection;
+import org.jfree.data.time.RegularTimePeriod;
 
 /**
  * An abstract <tt>TimerTask</tt> to update a strip chart at a regular interval.
  */
-public abstract class StripChartUpdater extends TimerTask {
+public class StripChartUpdater {
+    
+    final JFreeChart chart;
+    final DateAxis domainAxis;
+    final DynamicTimeSeriesCollection dataset;
+    final Timer timer;
+    final TimerTask task;
+    final Long rangeMillis;
+    final ValueProvider valueProvider;
+    long updateInterval;
+        
+    StripChartUpdater(JFreeChart chart, ValueProvider valueProvider, Long rangeMillis, RegularTimePeriod timeBase) {
+        this.chart = chart;                        
+        this.domainAxis = (DateAxis) chart.getXYPlot().getDomainAxis();
+        this.dataset = (DynamicTimeSeriesCollection) chart.getXYPlot().getDataset();
+        this.rangeMillis = rangeMillis;
+        this.updateInterval = timeBase.getLastMillisecond() - timeBase.getFirstMillisecond();            
+        this.valueProvider = valueProvider;
+        timer = new Timer(chart.getTitle().getText() + " Timer");
+        task = new TimerTask() {
+    
+            @Override
+            public void run() {
+                StripChartUpdater.this.chart.setNotify(false);
+        
+                dataset.advanceTime();
+                long time = dataset.getNewestTime().getEnd().getTime();
+                
+                float values[] = StripChartUpdater.this.valueProvider.getValues();
+                dataset.appendData(values);
+        
+                domainAxis.setRange(
+                        new Date(time - StripChartUpdater.this.rangeMillis), 
+                        new Date(time + updateInterval));
 
-    DynamicTimeSeriesCollection dataset;
-    long updateIntervalMillis = 1000;
-
-    public StripChartUpdater() {
+                StripChartUpdater.this.chart.setNotify(true);
+                StripChartUpdater.this.chart.fireChartChanged();                  
+            }            
+        };
     }
-
-    public void setChart(JFreeChart chart) {
-        this.dataset = (DynamicTimeSeriesCollection) chart.getXYPlot().getDataset();
+    
+    JFreeChart getChart() {
+        return chart;
     }
-
-    public void setUpdateIntervalMillis(long updateIntervalMillis) {
-        this.updateIntervalMillis = updateIntervalMillis;
+     
+    void start() {
+        timer.scheduleAtFixedRate(task, 0, updateInterval);
+    }        
+    
+    public void stop() {
+        timer.cancel();
     }
-
-    public void run() {
-        dataset.advanceTime();
-        dataset.appendData(new float[] { nextValue() });
-    }
-
-    public void schedule(Timer timer) {
-        timer.schedule(this, 0, updateIntervalMillis);
-    }
-
-    public abstract float nextValue();
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/StatusCode.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/StatusCode.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/StatusCode.java	Wed Mar 25 14:43:27 2015
@@ -1,16 +1,29 @@
 package org.hps.monitoring.subsys;
+
+import java.awt.Color;
 
 /**
  * Code that represents a sub-system status.
  */
 public enum StatusCode {
-    OKAY,
-    UNKNOWN,
-    CLEARED,
-    OFFLINE,
-    INFO,
-    WARNING,
-    ERROR,
-    ALARM,
-    HALT;
+    
+    OKAY(Color.GREEN),
+    UNKNOWN(Color.GRAY),
+    CLEARED(Color.LIGHT_GRAY),
+    OFFLINE(Color.ORANGE),
+    INFO(Color.WHITE),
+    WARNING(Color.YELLOW),
+    ERROR(Color.RED),
+    ALARM(Color.RED),
+    HALT(Color.RED);
+    
+    Color color;
+    
+    StatusCode(Color color) {
+        this.color = color;
+    }
+    
+    public Color getColor() {
+        return color;
+    }    
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java	Wed Mar 25 14:43:27 2015
@@ -1,7 +1,6 @@
 package org.hps.monitoring.subsys;
 
 import java.io.PrintStream;
-import java.util.TimerTask;
 
 /**
  * This is an interface for a set of basic statistics about an online event processing system.
@@ -12,13 +11,20 @@
      * Set the desired timer tick length in millis.
      * @param tickLengthMillis The desired tick length in millis.
      */
-    void setTickLengthMillis(long tickLengthMillis);
+    void setNominalTickLengthMillis(long tickLengthMillis);
 
     /**
-     * Get the nominal length of one tick in millis. Actual ticks lengths may vary slightly.
+     * Get the nominal length of one tick in millis.
+     *  Actual tick lengths lengths may vary slightly.
      * @return The nominal tick length in millis.
      */
-    long getTickLengthMillis();
+    long getNominalTickLengthMillis();
+    
+    /**
+     * Get the end of the tick in Unix time (milliseconds since the epoch).
+     * @return The tick end in Unix time.
+     */
+    long getTickEndTimeMillis();
 
     /**
      * Start the timer thread for accumulating statistics.
@@ -106,6 +112,13 @@
      * @return The data rate in [bytes/second].
      */
     public double getBytesPerSecond();
+    
+    /**
+     * Get the immediate data rate which is the amount of data in megabytes received in the current tick
+     * over the time elapsed in the tick.
+     * @return The data rate in [bytes/second].
+     */
+    public double getMegabytesPerSecond();
 
     /**
      * Get the number of milliseconds since the last tick.
@@ -124,10 +137,10 @@
      * @param ps The PrintStream for display.
      */
     void printTick(PrintStream ps);
-
+    
     /**
-     * Add subtask which will execute right before a new tick.
-     * @param subtask The subtask to execute.
+     * 
+     * @param listener
      */
-    void addSubTask(TimerTask subtask);
+    void addSystemStatisticsListener(SystemStatisticsListener listener);   
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java	Wed Mar 25 14:43:27 2015
@@ -7,52 +7,53 @@
 import java.util.Timer;
 import java.util.TimerTask;
 
-import org.hps.monitoring.plotting.StripChartUpdater;
+import org.hps.monitoring.plotting.ValueProvider;
 
 /**
  * Implementation of {@link SystemStatistics}.
  */
-// FIXME: Rolling averages need to happen over a greater time period like 30 seconds
-// instead of 1 second, because otherwise the statistics don't look right.
 public class SystemStatisticsImpl implements SystemStatistics {
 
-    long tickLengthMillis = 1000; // default is one second tick
-    long sessionElapsedMillis;
+    long nominalTickLengthMillis = 1000; // default is 1 second tick
+    long tickStartTimeMillis;
+    long tickEndTimeMillis;    
+
+    long eventsInTick;
+    long bytesInTick;
+    
     long startTimeMillis;
     long stopTimeMillis;
-    long eventsSinceTick;
-    long bytesSinceTick;
+    
     long totalEvents;
     long totalBytes;
-    long tickStartMillis;
-    long tickElapsedMillis;
+
     static final long Kb = 1 * 1024;
     static final long Mb = Kb * 1024;
     static final double milliToSecond = 0.001;
     static final DecimalFormat decimalFormat = new DecimalFormat("#.####");
     Timer timer;
-    List<TimerTask> subtasks = new ArrayList<TimerTask>();
-
+    
+    List<SystemStatisticsListener> listeners = new ArrayList<SystemStatisticsListener>();
+    
     @Override
     public void update(int size) {
         addEvent();
         addData(size);
-        updateElapsedTime();
-    }
-
-    @Override
-    public void setTickLengthMillis(long tickLengthMillis) {
-        this.tickLengthMillis = tickLengthMillis;
-    }
-
-    @Override
-    public long getTickLengthMillis() {
-        return tickLengthMillis;
+    }
+
+    @Override
+    public void setNominalTickLengthMillis(long tickLengthMillis) {
+        this.nominalTickLengthMillis = tickLengthMillis;
+    }
+
+    @Override
+    public long getNominalTickLengthMillis() {
+        return nominalTickLengthMillis;
     }
 
     @Override
     public long getTotalElapsedMillis() {
-        return sessionElapsedMillis;
+        return System.currentTimeMillis() - startTimeMillis;
     }
 
     @Override
@@ -67,7 +68,12 @@
     
     @Override
     public long getTickElapsedMillis() {
-        return tickElapsedMillis;
+        return System.currentTimeMillis() - tickStartTimeMillis;
+    }
+    
+    @Override
+    public long getTickEndTimeMillis() {
+        return tickEndTimeMillis;
     }
 
     /**
@@ -76,7 +82,7 @@
     
     @Override
     public long getEventsReceived() {
-        return eventsSinceTick;
+        return eventsInTick;
     }
      
     @Override
@@ -86,10 +92,11 @@
     
     @Override
     public double getEventsPerSecond() {
-        if (eventsSinceTick > 0 && tickElapsedMillis > 0)
-            return (double) eventsSinceTick / millisToSeconds(tickElapsedMillis);
-        else
+        if (eventsInTick > 0 && getTickElapsedMillis() > 0) {
+            return (double) eventsInTick / millisToSeconds(getTickElapsedMillis());
+        } else {
             return 0.;
+        }
     }
 
     @Override
@@ -107,7 +114,7 @@
     
     @Override
     public long getBytesReceived() {
-        return bytesSinceTick;
+        return bytesInTick;
     }
     
     @Override
@@ -126,34 +133,59 @@
    
     @Override
     public double getBytesPerSecond() {
-        if (bytesSinceTick > 0 && tickElapsedMillis > 0)
-            return (double) bytesSinceTick / millisToSeconds(tickElapsedMillis);
+        if (bytesInTick > 0 && getTickElapsedMillis() > 0)
+            return (double) bytesInTick / millisToSeconds(getTickElapsedMillis());
         else
             return 0.;
     }
-
+    
+    @Override
+    public double getMegabytesPerSecond() {
+        double bytes = getBytesPerSecond();
+        if (bytes > 0) {
+            return bytesToMb(bytes);
+        } else {
+            return 0;
+        }
+    }
+    
     @Override
     public void start() {
 
-        // Set time variables.
+        // Set session start time variables.
         long currentTimeMillis = System.currentTimeMillis();
         startTimeMillis = currentTimeMillis;
-        tickStartMillis = currentTimeMillis;
-
-        // Start Timer task which executes at tick length.
+        tickStartTimeMillis = currentTimeMillis;
+        
+        // Notify listeners of start.
+        for (SystemStatisticsListener listener : listeners) {
+            listener.started(this);
+        }
+        
+        // Start timer task which executes at the nominal tick length to calculate statistics periodically.
         TimerTask task = new TimerTask() {
             public void run() {
-
-                // Run sub-tasks.
-                for (TimerTask subtask : subtasks) {
-                    subtask.run();
-                }
-
+                
+                // End the current tick.
+                endTick();
+               
+                // Start the new tick.
                 nextTick();
             }
         };
         timer = new Timer();
-        timer.schedule(task, 0, tickLengthMillis);
+        timer.schedule(task, 0, nominalTickLengthMillis);
+    }
+    
+    void endTick() {
+ 
+        // Set absolute end time of current tick.
+        this.tickEndTimeMillis = System.currentTimeMillis();
+        
+        // Activate listeners.
+        for (SystemStatisticsListener listener : listeners) {
+            listener.endTick(this);
+        }
     }
 
     @Override
@@ -166,8 +198,17 @@
 
         // Set stop time.
         stopTimeMillis = System.currentTimeMillis();
-    }
-
+        
+        // Notify listeners of stop.
+        for (SystemStatisticsListener listener : listeners) {
+            listener.stopped(this);
+        }
+    }
+    
+    public void addSystemStatisticsListener(SystemStatisticsListener listener) {
+        listeners.add(listener);
+    }    
+    
     @Override
     public void printSession(PrintStream ps) {
         ps.println("session statistics ...");
@@ -185,115 +226,104 @@
         ps.println("  eventsSinceTick = " + this.getEventsReceived());
         ps.println("  bytesSinceTick = " + this.getBytesReceived());
     }
-
-    @Override
-    public void addSubTask(TimerTask subtask) {
-        this.subtasks.add(subtask);
-    }
-
+   
     void addEvent() {
-        eventsSinceTick += 1;
+        eventsInTick += 1;
         totalEvents += 1;
     }
 
     void addData(int size) {
-        bytesSinceTick += size;
+        bytesInTick += size;
         totalBytes += size;
-    }
-
-    void updateElapsedTime() {
-        tickElapsedMillis = System.currentTimeMillis() - tickStartMillis;
-        sessionElapsedMillis = System.currentTimeMillis() - startTimeMillis;
     }
 
     // Bytes to megabytes to 2 decimal places.
     static final double bytesToMb(long size) {
         return Double.parseDouble(decimalFormat.format((double) size / Mb));
     }
+    
+    // Bytes to megabytes to 2 decimal places.
+    static final double bytesToMb(double size) {
+        return Double.parseDouble(decimalFormat.format(size / Mb));
+    }
 
     static final double millisToSeconds(long millis) {
         return ((double) millis) / 1000.;
     }
 
     synchronized void nextTick() {
-        eventsSinceTick = 0;
-        bytesSinceTick = 0;
-        tickElapsedMillis = 0;
-        tickStartMillis = System.currentTimeMillis();
-    }
-    
-    public abstract class SystemStatisticsUpdater extends StripChartUpdater {
-        SystemStatisticsUpdater() {
-            addSubTask(this);
-        }
-    }
-    
-    public class AverageEventsPerSecondUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return (float) getAverageEventsPerSecond();
-        }
-    }
-    
-    public class EventsPerSecondUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return (float) getEventsPerSecond();
+        eventsInTick = 0;
+        bytesInTick = 0;
+        tickStartTimeMillis = System.currentTimeMillis();
+    }
+    
+    public abstract class SystemStatisticsProvider implements ValueProvider {
+    }
+
+    public class AverageEventsPerSecondProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getAverageEventsPerSecond()};
+        }
+    }
+    
+    public class EventsPerSecondProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getEventsPerSecond()};
         }
     }
             
-    public class EventsReceivedUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return getEventsReceived();
-        }
-    }
-
-    public class TotalEventsUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return getTotalEvents();
-        }
-    }
-
-    public class BytesReceivedUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return getBytesReceived();
-        }
-    }
-
-    public class AverageMegabytesPerSecondUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return (float) getAverageMegabytesPerSecond();
-        }
-    }
-
-    public class TotalMegabytesUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return (float) getTotalMegabytes();
-        }
-    }
-    
-    public class BytesPerSecondUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return (float) getBytesPerSecond();
-        }
-    }
-    
-    public class MegabytesPerSecondUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return (float) getBytesPerSecond() / 1000000;
-        }
-    }
-    
-    
-}
+    public class EventsReceivedProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {getEventsReceived()};
+        }
+    }
+
+    public class TotalEventsProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {getTotalEvents()};
+        }
+    }
+
+    public class BytesReceivedProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[]{getBytesReceived()};
+        }
+    }
+
+    public class AverageMegabytesPerSecondProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getAverageMegabytesPerSecond()};
+        }
+    }
+
+    public class TotalMegabytesProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getTotalMegabytes()};
+        }
+    }
+    
+    public class BytesPerSecondProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getBytesPerSecond()};
+        }
+    }
+    
+    public class MegabytesPerSecondProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getBytesPerSecond() / 1000000};
+        }
+    }
+}

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatusImpl.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatusImpl.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatusImpl.java	Wed Mar 25 14:43:27 2015
@@ -8,14 +8,16 @@
  */
 public final class SystemStatusImpl implements SystemStatus {
 
+    
     StatusCode code = StatusCode.UNKNOWN;
+    Subsystem systemName;
+    String message;
+    String description;
     long lastChangedMillis;
-    String message;
+    boolean active = true;
+    boolean clearable;
+    
     List<SystemStatusListener> listeners = new ArrayList<SystemStatusListener>();
-    final Subsystem systemName;
-    final String description;
-    boolean active = true;
-    final boolean clearable;
 
     /**
      * Fully qualified constructor.
@@ -29,6 +31,36 @@
         this.clearable = clearable;
         setLastChangedTime();
         SystemStatusRegistry.getSystemStatusRegistery().register(this);
+    }
+    
+    /**
+     * Copy constructor from implementation class.
+     * The list of listeners is NOT copied.  
+     * @param status The status to copy.
+     */
+    public SystemStatusImpl(SystemStatusImpl status) {
+        this.code = status.code;
+        this.systemName = status.systemName;
+        this.message = status.message;
+        this.description = status.description;
+        this.lastChangedMillis = status.lastChangedMillis;
+        this.active = status.active;
+        this.clearable = status.clearable;
+    }
+    
+    /**
+     * Copy constructor from interface.
+     * The list of listeners is NOT copied.  
+     * @param status The status to copy.
+     */
+    public SystemStatusImpl(SystemStatus status) {
+        this.code = status.getStatusCode();
+        this.systemName = status.getSubsystem();
+        this.message = status.getMessage();
+        this.description = status.getDescription();
+        this.lastChangedMillis = status.getLastChangedMillis();
+        this.active = status.isActive();
+        this.clearable = status.isClearable();
     }
 
     @Override
@@ -97,5 +129,5 @@
     @Override
     public boolean isClearable() {
         return clearable;
-    }
+    }    
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalPedestalMonitor.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalPedestalMonitor.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalPedestalMonitor.java	Wed Mar 25 14:43:27 2015
@@ -1,108 +1,66 @@
 package org.hps.monitoring.subsys.ecal;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.conditions.ecal.EcalChannel;
 import org.hps.conditions.ecal.EcalConditions;
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
-import org.hps.monitoring.plotting.StripChartUtil;
 import org.jfree.chart.JFreeChart;
-import org.jfree.data.time.Millisecond;
-import org.jfree.data.time.TimeSeries;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.data.time.Second;
+import org.jfree.data.time.TimeSeriesCollection;
 import org.lcsim.event.EventHeader;
 import org.lcsim.geometry.Detector;
 import org.lcsim.util.Driver;
 import org.lcsim.util.aida.AIDA;
+
 /*
  * Reads output of org.hps.recon.ecal.RunningPedestalDriver and makes strip charts.
- * 
- * Are we going to lose/reinitialize these plots when run ends?
- * Or can we keep on going off ET-ring across runs?
  * 
  * Baltzell
  */
 public class EcalPedestalMonitor extends Driver {
 
-    long previousTime;
+    static final int REFRESH_RATE = 10*1000; // units = ms
+    static final double DOMAIN_SIZE = 4*60*60*1000; // x-axis range (ms)
+    static final int crates[]={1,2};
+    static final int slots[]={3,4,5,6,7,8,9,14,15,16,17,18,19,20};
+    static final String slotNames[]={"Sl3","Sl4","Sl5","Sl6","Sl7","Sl8","Sl9","Sl14","Sl15","Sl16","Sl17","Sl18","Sl19","Sl20"};
+    static final String collectionName = "EcalRunningPedestals";
+   
     long currentTime;
-    int refreshRate=1000; // units = ms
-   
+    long previousTime=0;
     int nDetectorChanges=0;
-   
-    int maxAge = 999999999;
-    int maxCount = 100000;
-    int rangeSize = 100000;
-   
-    // None of this "works":
-    //int maxAge = 86400000; // 1 day (units ms)
-    //int maxCount = 999999999;//(int)maxAge/refreshRate;
-    //int rangeSize = 999999999;//maxAge; // what is this?
-   
-    final int crates[]={1,2};
-    final int slots[]={3,4,5,6,7,8,9,14,15,16,17,18,19,20};
+    private EcalConditions ecalConditions = null;
+    List<JFreeChart> charts = new ArrayList<JFreeChart>();
+    MonitoringPlotFactory plotFactory = 
+            (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ECal Pedestal Monitoring");
     
-    String collectionName = "EcalRunningPedestals";
-    
-    MonitoringPlotFactory plotFactory = (MonitoringPlotFactory) AIDA.defaultInstance()
-            .analysisFactory().createPlotterFactory("ECal Pedestal Monitoring");
-
-    Map<Integer, Map<Integer, JFreeChart>> stripCharts = new HashMap<Integer, Map<Integer, JFreeChart>>();
-    Map<Integer, Map<Integer, TimeSeries>> stripCharts2 = new HashMap<Integer, Map<Integer, TimeSeries>>();
-
-    private EcalConditions ecalConditions = null;
-    
-    public void startOfData() {
-        //plotFactory.createStripChart("X","Y",maxAge,maxCount,rangeSize);
-        plotFactory.create().show();
-        
-        //System.out.println("----------------------------    "+maxCount);
-    }
-
-    @Override
     public void detectorChanged(Detector detector) {
-        
-        // this would defeat the purpose.
         if (nDetectorChanges++ > 0) return;
-        
-        currentTime=0;
-        previousTime=0;
-        
         ecalConditions = DatabaseConditionsManager.getInstance().getEcalConditions();
-        
-        // put them in order:
         for (int crate : crates) {
-            stripCharts.put(crate,new HashMap<Integer, JFreeChart>());
-            stripCharts2.put(crate,new HashMap<Integer, TimeSeries>());
-            for (int slot : slots) {
-                
-                final double ped=getAveragePedestal(crate,slot);
-                
-                String name = String.format("C%dS%02d",crate,slot);
-                JFreeChart stripChart = plotFactory.createStripChart(name,"asdf",
-                        maxAge,maxCount,rangeSize);
-//                stripChart.getXYPlot().getRangeAxis().setRange(70,140);
-//                stripChart.getXYPlot().getRangeAxis().setFixedAutoRange(10);
-//                stripChart.getXYPlot().getRangeAxis().setAutoRange(true);
-                stripChart.getXYPlot().getRangeAxis().setRangeAboutValue(ped,10);
-                stripCharts.get(crate).put(slot,stripChart);
-                stripCharts2.get(crate).put(slot,StripChartUtil.getTimeSeries(stripChart));
-            }
+            charts.add(plotFactory.createTimeSeriesChart(
+                    "Crate " + crate,
+                    "Offset Pedestal (ADC)", 
+                    slots.length, slotNames,
+                    DOMAIN_SIZE));
         }
     }
-
+    
     @Override
     public void process(EventHeader event) {
 
-        if (!event.hasItem(collectionName)) {
-            return;
-        }
+        if (!event.hasItem(collectionName)) return;
 
-        currentTime=System.currentTimeMillis();
-        if (currentTime - previousTime < refreshRate) return;
-        previousTime=currentTime;
+        currentTime = System.currentTimeMillis();
+        if (currentTime - previousTime < REFRESH_RATE) return;
+        previousTime = currentTime;
 
         // get the running pedestals:
         Map<EcalChannel, Double> peds = (Map<EcalChannel, Double>) event.get(collectionName);
@@ -110,6 +68,7 @@
         // tally slot pedestals:
         Map<Integer, Map<Integer, Double>> pedsum = new HashMap<Integer, Map<Integer, Double>>();
         Map<Integer, Map<Integer, Integer>> npedsum = new HashMap<Integer, Map<Integer, Integer>>();
+
         for (EcalChannel cc : peds.keySet()) {
             final Double ped = peds.get(cc);
             final int crate = cc.getCrate();
@@ -131,17 +90,24 @@
         }
 
         // fill strip charts:
+        long now = System.currentTimeMillis();
         for (int crate : pedsum.keySet()) {
-            for (int slot : pedsum.get(crate).keySet()) {
-
-                final double ped = pedsum.get(crate).get(slot) / npedsum.get(crate).get(slot);
-                stripCharts2.get(crate).get(slot).add(new Millisecond(new Date()),ped);
+            TimeSeriesCollection cc=getTimeSeriesCollection(crate-1);
+            JFreeChart chart=charts.get(crate-1);
+            DateAxis ax=(DateAxis)chart.getXYPlot().getDomainAxis();
+            ax.setRange(now-DOMAIN_SIZE,now);
+            for (int slot=0; slot<slots.length; slot++) {
+                double ped = pedsum.get(crate).get(slots[slot]) / npedsum.get(crate).get(slots[slot]);
+                ped -= getAveragePedestal(crate,slots[slot])-slot+9;
+                cc.getSeries(slot).addOrUpdate(new Second(new Date()),ped);
             }
         }
-
     }
     
-    
+    TimeSeriesCollection getTimeSeriesCollection(int chartIndex) {
+        return (TimeSeriesCollection) charts.get(chartIndex).getXYPlot().getDataset();
+    }
+
     public EcalChannel findChannel(int crate, int slot, int chan) {
         for (EcalChannel cc : ecalConditions.getChannelCollection()) {
             if (crate == cc.getCrate() && slot == cc.getSlot() && chan == cc.getChannel()) {

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java	Wed Mar 25 14:43:27 2015
@@ -1,13 +1,9 @@
 package org.hps.monitoring.subsys.ecal;
 
-import java.util.Date;
-import java.util.TimerTask;
-
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
-import org.hps.monitoring.plotting.StripChartUtil;
-import org.jfree.chart.JFreeChart;
-import org.jfree.data.time.Millisecond;
-import org.jfree.data.time.TimeSeries;
+import org.hps.monitoring.plotting.StripChartUpdater;
+import org.hps.monitoring.plotting.ValueProvider;
+import org.jfree.data.time.Second;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.RawCalorimeterHit;
 import org.lcsim.util.Driver;
@@ -21,22 +17,27 @@
     int eventInterval = 1000;
     static String collectionName = "EcalReadoutHits";
 
-    MonitoringPlotFactory plotFactory = (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ECAL System Monitoring");
-    TimeSeries series;
-    JFreeChart stripChart;
-    TimerTask updateTask;
+    MonitoringPlotFactory plotFactory = 
+            (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ECAL System Monitoring");
+
     EventHeader currentEvent;
     int hits;
+    
     int events;
-
+    double averageHits;
+    
+    StripChartUpdater updater;
+       
     public void startOfData() {
-        stripChart = plotFactory.createStripChart("Average ECAL Hits per " + eventInterval + " Events", "Hits", 99999999, /*
-                                                                                                                           * max
-                                                                                                                           * age
-                                                                                                                           */
-                1000, /* max count */
-                100000 /* range size */);
-        series = StripChartUtil.getTimeSeries(stripChart);
+        plotFactory.createStripChart(
+                "Average ECAL Hits per " + eventInterval + " Events", 
+                "Hits", 
+                1, 
+                new String[] { "Date" }, 
+                1, 
+                new Second(), 
+                new AverageHitsProvider(), 
+                20000L);        
     }
 
     public void process(EventHeader event) {
@@ -44,10 +45,19 @@
         ++events;
         hits += size;
         if (event.getEventNumber() % eventInterval == 0) {
-            double averageHits = (double) hits / (double) events;
-            series.add(new Millisecond(new Date()), averageHits);
+            averageHits = (double) hits / (double) events;
             hits = 0;
             events = 0;
         }
     }
+    
+    public void endOfData() {
+        updater.stop();
+    }
+    
+    class AverageHitsProvider implements ValueProvider {
+        public float[] getValues() {
+            return new float[] {(float) averageHits};
+        }
+    }
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java	Wed Mar 25 14:43:27 2015
@@ -1,33 +1,95 @@
+/**
+ * 
+ */
 package org.hps.monitoring.subsys.et;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
+import org.hps.monitoring.subsys.SystemStatistics;
 import org.hps.monitoring.subsys.SystemStatisticsImpl;
+import org.hps.monitoring.subsys.SystemStatisticsListener;
 import org.hps.record.et.EtEventProcessor;
+import org.jfree.chart.JFreeChart;
+import org.jfree.data.time.Second;
+import org.jfree.data.time.TimeSeriesCollection;
 import org.jlab.coda.et.EtEvent;
 import org.lcsim.util.aida.AIDA;
 
 /**
- * A basic set of strip charts for monitoring the ET system.
+ * This will show a series of strip charts from ET system performance statistics
+ * such as event and data rates.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
  */
-public final class EtSystemStripCharts extends EtEventProcessor {
+public class EtSystemStripCharts extends EtEventProcessor implements SystemStatisticsListener {
 
+    // The system statistics.
     SystemStatisticsImpl stats = new SystemStatisticsImpl();
-    MonitoringPlotFactory plotFactory = (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ET System Monitoring");
-
-    public EtSystemStripCharts() {
-        stats.setTickLengthMillis(2000);
+    
+    // Plotting API.
+    MonitoringPlotFactory plotFactory = 
+            (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ET System Monitoring");
+    
+    // List of charts.
+    List<JFreeChart> charts = new ArrayList<JFreeChart>();
+    
+    // Range size in milliseconds.
+    static final double RANGE_SIZE = 200000;
+    
+    // Chart collection indices.
+    static final int DATA_RATE_COLLECTION_INDEX = 0;    
+    static final int TOTAL_DATA_COLLECTION_INDEX = 1;    
+    static final int EVENT_RATE_COLLECTION_INDEX = 2;
+    static final int TOTAL_EVENTS_COLLECTION_INDEX = 3;
+        
+    public EtSystemStripCharts() {          
+        // Set 2 seconds between statistics updates.
+        stats.setNominalTickLengthMillis(1000);
     }
     
     /**
      * Setup the strip charts for ET system monitoring and start accumulating statistics.
      */
     @Override
-    public void startJob() {        
-        plotFactory.createStripChart("Data Rate", "MB / second", 100, stats.new MegabytesPerSecondUpdater()); 
-        plotFactory.createStripChart("Total Data", "Megabytes", 100, stats.new TotalMegabytesUpdater());      
-        plotFactory.createStripChart("Event Rate", "Events / second",  100, stats.new EventsPerSecondUpdater());
-        plotFactory.createStripChart("Total Events", "Number of Events", 100, stats.new TotalEventsUpdater());
+    public void startJob() {
+           
+        // Register this class as a listener to activate update at end of statistics clock tick.
+        stats.addSystemStatisticsListener(this);
+
+        // Start systems statistics task.
         stats.start();
+    }
+
+    /**
+     * Create the strip charts for plotting the basic ET system statistics.
+     */
+    private void createStripCharts() {
+        
+        // Data rate and average data reate in megabytes per second.
+        charts.add(plotFactory.createTimeSeriesChart(
+                "Data Rate", 
+                "MB / second",
+                2, 
+                new String[] { "Data Rate", "Average Data Rate" },
+                RANGE_SIZE));
+                
+        // Total megabytes received.
+        charts.add(plotFactory.createTimeSeriesChart("Total Data", "Megabytes", 1, null, RANGE_SIZE));
+        
+        // Event rate and average event rate in hertz.
+        charts.add(plotFactory.createTimeSeriesChart(
+                "Event Rate", 
+                "Hz", 
+                2, 
+                new String[] { "Event Rate", "Average Event Rate" }, 
+                RANGE_SIZE));
+        
+        // Total number of events received.
+        charts.add(plotFactory.createTimeSeriesChart("Total Events", "Number of Events", 1, null, RANGE_SIZE));
+              
     }
 
     @Override
@@ -35,8 +97,47 @@
         stats.update(event.getLength());
     }
 
-    @Override
     public void endJob() {
+        // Stop system statistics task.
         stats.stop();
     }
-}
+    
+    TimeSeriesCollection getTimeSeriesCollection(int chartIndex) {
+        return (TimeSeriesCollection) charts.get(chartIndex).getXYPlot().getDataset();
+    }
+
+    /**
+     * Hook for updating the charts at end of statistics clock tick.
+     * @param stats The statistics with the system information.
+     */
+    @Override
+    public void endTick(SystemStatistics stats) {
+        
+        Date now = new Date(stats.getTickEndTimeMillis());
+                
+        getTimeSeriesCollection(DATA_RATE_COLLECTION_INDEX).getSeries(0).addOrUpdate(
+                new Second(now), stats.getMegabytesPerSecond());
+        getTimeSeriesCollection(DATA_RATE_COLLECTION_INDEX).getSeries(1).addOrUpdate(
+                new Second(now), stats.getAverageMegabytesPerSecond());
+        
+        getTimeSeriesCollection(TOTAL_DATA_COLLECTION_INDEX).getSeries(0).addOrUpdate(
+                new Second(now), stats.getTotalMegabytes());
+        
+        getTimeSeriesCollection(EVENT_RATE_COLLECTION_INDEX).getSeries(0).addOrUpdate(
+                new Second(now), stats.getEventsPerSecond());
+        getTimeSeriesCollection(EVENT_RATE_COLLECTION_INDEX).getSeries(1).addOrUpdate(
+                new Second(now), stats.getAverageEventsPerSecond());
+        
+        getTimeSeriesCollection(TOTAL_EVENTS_COLLECTION_INDEX).getSeries(0).addOrUpdate(
+                new Second(now), stats.getTotalEvents());
+    }
+     
+    @Override
+    public void started(SystemStatistics stats) {
+        createStripCharts();
+    }
+   
+    @Override
+    public void stopped(SystemStatistics stats) {
+    }
+}

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/ClusterTablePanel.java	Wed Mar 25 14:43:27 2015
@@ -82,11 +82,6 @@
 			setGlobalRowValue(ROW_RECON_COUNT, String.format(countFormat, clusterValue[2]));
 			setGlobalRowValue(ROW_SSP_COUNT,   String.format(countFormat, clusterValue[3]));
 			
-			
-			System.out.printf("(row = %d, col = %d) --> %s%n", ROW_RECON_COUNT, 1, String.format(countFormat, clusterValue[0]));
-			System.out.printf("(row = %d, col = %d) --> %s%n", ROW_SSP_COUNT,   1, String.format(countFormat, clusterValue[1]));
-			
-			
 			// Output the tracked statistical data.
 			int total;
 			String percentFormat = "%" + spaces + "d / %" + spaces + "d (%7.3f)";
@@ -112,12 +107,6 @@
 			setGlobalRowValue(ROW_FAILED_POSITION,  String.format(percentFormat, statValue[5], total, 100.0 * statValue[5] / total));
 			setGlobalRowValue(ROW_FAILED_ENERGY,    String.format(percentFormat, statValue[6], total, 100.0 * statValue[6] / total));
 			setGlobalRowValue(ROW_FAILED_HIT_COUNT, String.format(percentFormat, statValue[7], total, 100.0 * statValue[7] / total));
-			
-			
-			System.out.printf("(row = %d, col = %d) --> %s%n", ROW_MATCHED,          1, String.format(percentFormat, statValue[0], total, 100.0 * statValue[0] / total));
-			System.out.printf("(row = %d, col = %d) --> %s%n", ROW_FAILED_POSITION,  1, String.format(percentFormat, statValue[1], total, 100.0 * statValue[1] / total));
-			System.out.printf("(row = %d, col = %d) --> %s%n", ROW_FAILED_ENERGY,    1, String.format(percentFormat, statValue[2], total, 100.0 * statValue[2] / total));
-			System.out.printf("(row = %d, col = %d) --> %s%n", ROW_FAILED_HIT_COUNT, 1, String.format(percentFormat, statValue[3], total, 100.0 * statValue[3] / total));
 		}
 	}
 }

Modified: java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java
 =============================================================================
--- java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java	(original)
+++ java/branches/prod/monitoring-util/src/main/java/org/hps/monitoring/trigger/TableTextModel.java	Wed Mar 25 14:43:27 2015
@@ -81,6 +81,9 @@
 		
 		// Set the value.
 		values[rowIndex][columnIndex] = value;
+		
+		// Update the table.
+		this.fireTableCellUpdated(rowIndex, columnIndex);
 	}
 	
 	/**

Modified: java/branches/prod/record-util/src/main/java/org/hps/record/RecordProcessingException.java
 =============================================================================
--- java/branches/prod/record-util/src/main/java/org/hps/record/RecordProcessingException.java	(original)
+++ java/branches/prod/record-util/src/main/java/org/hps/record/RecordProcessingException.java	Wed Mar 25 14:43:27 2015
@@ -11,4 +11,8 @@
         super(message, x);
     }
     
+    public RecordProcessingException(String message) {
+        super(message);
+    }
+    
 }

Modified: java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoop.java
 =============================================================================
--- java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoop.java	(original)
+++ java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoop.java	Wed Mar 25 14:43:27 2015
@@ -15,9 +15,15 @@
 import org.hps.record.enums.ProcessingStage;
 import org.hps.record.et.EtEventProcessor;
 import org.hps.record.et.EtEventSource;
-import org.hps.record.et.EtEventSource.EtSourceException;
 import org.hps.record.evio.EvioEventProcessor;
 import org.hps.record.evio.EvioFileSource;
+import org.jlab.coda.et.exception.EtBusyException;
+import org.jlab.coda.et.exception.EtClosedException;
+import org.jlab.coda.et.exception.EtDeadException;
+import org.jlab.coda.et.exception.EtEmptyException;
+import org.jlab.coda.et.exception.EtException;
+import org.jlab.coda.et.exception.EtTimeoutException;
+import org.jlab.coda.et.exception.EtWakeUpException;
 import org.lcsim.util.Driver;
 import org.lcsim.util.loop.LCIOEventSource;
 
@@ -149,8 +155,8 @@
                 Throwable cause = x.getCause();
                 if (cause instanceof MaxRecordsException || 
                         cause instanceof EndRunException || 
-                        cause instanceof EtSourceException || 
-                        cause instanceof NoSuchRecordException) {
+                        cause instanceof NoSuchRecordException ||
+                        isEtReadException(cause)) {
                     // These types of exceptions are never ignored.
                     return false;
                 } else {
@@ -160,6 +166,21 @@
             } 
         }
         return false;               
+    }
+    
+    /**
+     * True if the Throwable is a type that can be thrown by the ET
+     * system when it is attempting to read events from the server.
+     * The <code>IOException</code> type is ignored but can actually 
+     * be thrown.
+     * @param e The Exception.
+     * @return True if the object can be thrown by ET event reading.
+     */
+    private static boolean isEtReadException(Throwable e) {
+        return e instanceof EtException || e instanceof EtDeadException 
+                || e instanceof EtClosedException || e instanceof EtEmptyException
+                || e instanceof EtBusyException || e instanceof EtTimeoutException
+                || e instanceof EtWakeUpException;
     }
             
     /**

Modified: java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoopConfiguration.java
 =============================================================================
--- java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoopConfiguration.java	(original)
+++ java/branches/prod/record-util/src/main/java/org/hps/record/composite/CompositeLoopConfiguration.java	Wed Mar 25 14:43:27 2015
@@ -39,7 +39,6 @@
     EtConnection connection = null;
     RecordSource recordSource = null;
     LCSimEventBuilder eventBuilder = null;
-    String detectorName = null;
     
     // Event processors.
     List<EvioEventProcessor> evioProcessors = new ArrayList<EvioEventProcessor>();
@@ -119,17 +118,7 @@
         this.eventBuilder = eventBuilder;
         return this;
     }
-    
-    /**
-     * Set the name of the detector definition to be used e.g. from detector-data/detectors dir. 
-     * @param detectorName The name of the detector.
-     * @return This object.
-     */
-    public CompositeLoopConfiguration setDetectorName(String detectorName) {
-        this.detectorName = detectorName;
-        return this;
-    }
-    
+        
     /**
      * Set whether the loop will stop when event processing errors occur.
      * Certain types of errors are considered fatal or are used to control

Modified: java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java
 =============================================================================
--- java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java	(original)
+++ java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java	Wed Mar 25 14:43:27 2015
@@ -2,19 +2,31 @@
 
 public class EvioEventConstants {
     
+    // This is the old tag for physics events.
     public static final int PHYSICS_EVENT_TAG = 1;
+    
+    // CODA control event tags.
     public static final int SYNC_EVENT_TAG = 16;
     public static final int PRESTART_EVENT_TAG = 17;
     public static final int GO_EVENT_TAG = 18;
     public static final int PAUSE_EVENT_TAG = 19;
     public static final int END_EVENT_TAG = 20;
     
+    // Special tag for events with EPICS scalars.
+    public static final int EPICS_EVENT_TAG = 31;
+    
+    // Event tags greater than or equal to this will be physics events.
+    public static final int PHYSICS_START_TAG = 32;
+    
     public static final int EVENTID_BANK_TAG = 0xC000;
-    
+  
+    // Bank tag for header bank with run and event numbers in physics events.
     public static final int HEAD_BANK_TAG = 0xe10F;
   
-    public static final int EPICS_EVENT_TAG = 31;
+    public static final int EPICS_BANK_TAG = 57620;
     public static final int EPICS_BANK_TAG_2s = -1;
     public static final int EPICS_BANK_TAG_20s = -1;
+    //public static final int EPICS_BANK_TAG_2s = -1;
+    //public static final int EPICS_BANK_TAG_20s = -1;
     
 }

Modified: java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java
 =============================================================================
--- java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java	(original)
+++ java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java	Wed Mar 25 14:43:27 2015
@@ -1,11 +1,6 @@
 package org.hps.record.evio;
 
-import static org.hps.record.evio.EvioEventConstants.END_EVENT_TAG;
-import static org.hps.record.evio.EvioEventConstants.GO_EVENT_TAG;
-import static org.hps.record.evio.EvioEventConstants.PAUSE_EVENT_TAG;
-import static org.hps.record.evio.EvioEventConstants.PHYSICS_EVENT_TAG;
-import static org.hps.record.evio.EvioEventConstants.PRESTART_EVENT_TAG;
-import static org.hps.record.evio.EvioEventConstants.SYNC_EVENT_TAG;
+import static org.hps.record.evio.EvioEventConstants.*;
 
 import org.jlab.coda.jevio.BaseStructure;
 import org.jlab.coda.jevio.EvioEvent;
@@ -19,6 +14,15 @@
 public final class EvioEventUtilities {
 
     private EvioEventUtilities() {
+    }
+    
+    /**
+     * Get the event tag from the header bank.
+     * @param event The input EvioEvent.
+     * @return The event tag from the header bank.
+     */
+    public static int getEventTag(EvioEvent event) {
+        return event.getHeader().getTag();
     }
 
     /**
@@ -69,7 +73,7 @@
      * @return True if this event is a physics event.
      */
     public static boolean isPhysicsEvent(EvioEvent event) {
-        return (event.getHeader().getTag() >= SYNC_EVENT_TAG+16 ||
+        return (event.getHeader().getTag() >= PHYSICS_START_TAG ||
                 event.getHeader().getTag() < SYNC_EVENT_TAG);
         // return event.getHeader().getTag() == PHYSICS_EVENT_TAG;
     }
@@ -82,6 +86,29 @@
      */
     public static boolean isSyncEvent(EvioEvent event) {
         return event.getHeader().getTag() == SYNC_EVENT_TAG;
+    }
+    
+    /**
+     * Check if this event is an EPICS event containing scalar data.
+     * 
+     * @param event The EvioEvent.
+     * @return True if this event is an EPICS event.
+     */
+    public static boolean isEpicsEvent(EvioEvent event) {
+        return event.getHeader().getTag() == EPICS_EVENT_TAG;
+    }
+    
+    /**
+     * True if <code>event</code> is an EVIO control event.
+     * @return True if event is a control event.
+     */
+    public static boolean isControlEvent(EvioEvent event) {
+        return isPreStartEvent(event) || 
+                isGoEvent(event) || 
+                isPauseEvent(event) || 
+                isEndEvent(event) || 
+                isSyncEvent(event) ||
+                isEpicsEvent(event);
     }
 
     /**
@@ -108,7 +135,7 @@
         if (data != null) { //found the data in the top-level bank
             return data;
         } else { //data is not in event bank; look for the data bank whose tag matches the event type
-            for (BaseStructure bank : event.getChildren()) {
+            for (BaseStructure bank : event.getChildrenList()) {
                 if (bank.getHeader().getTag() == eventTag) {
                     return bank.getIntData(); //return whatever int data this bank has
                 }
@@ -125,9 +152,9 @@
      */
     public static BaseStructure getHeadBank(EvioEvent evioEvent) {
         if (evioEvent.getChildCount() > 0) {
-            for (BaseStructure topBank : evioEvent.getChildren()) {
-                if (topBank.getChildren() != null) {
-                    for (BaseStructure nestedBank : topBank.getChildren()) {
+            for (BaseStructure topBank : evioEvent.getChildrenList()) {
+                if (topBank.getChildrenList() != null) {
+                    for (BaseStructure nestedBank : topBank.getChildrenList()) {
                         if (nestedBank.getHeader().getTag() == EvioEventConstants.HEAD_BANK_TAG) {
                             return nestedBank;
                         }
@@ -137,4 +164,24 @@
         }
         return null;        
     }
+    
+    /**
+     * Get the run number from an EVIO event.
+     * @return The run number.
+     */
+    public static int getRunNumber(EvioEvent event) {
+        if (isControlEvent(event)) {
+            return getControlEventData(event)[1];
+        } else if (isPhysicsEvent(event)) {
+            BaseStructure headBank = EvioEventUtilities.getHeadBank(event);
+            if (headBank != null) {                                        
+                return headBank.getIntData()[1];   
+            } else {
+                throw new IllegalArgumentException("Head bank is missing from physics event.");
+            }
+        } else {
+            // Not sure if this would ever happen.
+            throw new IllegalArgumentException("Wrong event type: " + event.getHeader().getTag());
+        }
+    }
 }

Modified: java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioFileProducer.java
 =============================================================================
--- java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioFileProducer.java	(original)
+++ java/branches/prod/record-util/src/main/java/org/hps/record/evio/EvioFileProducer.java	Wed Mar 25 14:43:27 2015
@@ -5,6 +5,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.jlab.coda.et.EtAttachment;
@@ -228,6 +229,8 @@
                     if (debug) {
                         System.out.println("new events - size=" + size + "; group=" + group);
                     }
+                    
+                    int eventTag = EvioEventUtilities.getEventTag(event);
 
                     // Create a new array of ET events.  This always has one event.
                     mevs = sys.newEvents(
@@ -238,7 +241,12 @@
                             1, // number of events
                             size, // size of event but overwritten later
                             group); // group number; default value is arbitrary
-
+                                        
+                    // Create control data array for event selection.
+                    int[] control = new int[EtConstants.stationSelectInts];
+                    Arrays.fill(control, eventTag);
+                    mevs[0].setControl(control);
+                    
                     // Delay for X millis if applicable.
                     if (delay > 0) {
                         Thread.sleep(delay);

Modified: java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/ECalLedCommissioning.lcsim
 =============================================================================
--- java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/ECalLedCommissioning.lcsim	(original)
+++ java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/ECalLedCommissioning.lcsim	Wed Mar 25 14:43:27 2015
@@ -19,7 +19,7 @@
             <inputCollectionRaw>EcalReadoutHits</inputCollectionRaw>
             <inputClusterCollection>EcalClusters</inputClusterCollection>
             <pedSamples>20</pedSamples>
-            <maxEch>10.0</maxEch>
+            <maxEch>5.0</maxEch>
             <minEch>0.005</minEch>
             <eventRefreshRate>5</eventRefreshRate>
         </driver>

Modified: java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/EcalMonitoringFinal.lcsim
 =============================================================================
--- java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/EcalMonitoringFinal.lcsim	(original)
+++ java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/EcalMonitoringFinal.lcsim	Wed Mar 25 14:43:27 2015
@@ -8,12 +8,17 @@
     <execute>
         <!-- <driver name="EventMarkerDriver"/> -->
         <driver name="EcalRawConverter" />
-        <driver name="EcalClusterer" />
+        <driver name="DAQConfig"/> 
+        <driver name="DAQConfigDriver" />
+<!--	<driver name="EcalClusterer" />		--><!-- This is done elsewhere! -->
+        <driver name="GTPTestDriver" />
+        <driver name="TriggerDiagnostics" /> 
         <driver name="EcalMonitoringPlots" />      <!-- General plots -->
         <driver name="EcalHitPlots" />             <!-- Single hit distributions -->
         <driver name="EcalClusterPlots" />         <!-- Clusters distributions -->
         <driver name="EcalDaqPlots" />             <!-- DAQ Plots -->
         <driver name="EcalEventDisplay" />         <!-- Ecal event display -->        
+        <driver name="ClockDriver" />
         <!-- <driver name="EcalWindowPlots"/>  -->
         <!-- <driver name="EcalEvsX"/>         -->           
         <!-- <driver name="TriggerPlots"/>     -->
@@ -24,23 +29,69 @@
         <driver name="EventMarkerDriver" type="org.lcsim.job.EventMarkerDriver">
             <eventInterval>1</eventInterval>
         </driver>
+        <driver name="DAQConfig" type="org.hps.recon.ecal.daqconfig.DAQConfigDriver"/>
+
+	<driver name="DAQConfigDriver" type="org.hps.recon.ecal.daqconfig.DAQConfigDriver">
+	</driver>
+
         <driver name="EcalRawConverter" type="org.hps.recon.ecal.EcalRawConverterDriver">
-            <applyBadCrystalMap>false</applyBadCrystalMap>
+            <ecalCollectionName>EcalCalHits</ecalCollectionName>
             <use2014Gain>false</use2014Gain>
+            <useTimestamps>false</useTimestamps>
+            <useTruthTime>false</useTruthTime>
+            <useRunningPedestal>false</useRunningPedestal>
+            <useTimeWalkCorrection>false</useTimeWalkCorrection>
+            <emulateFirmware>true</emulateFirmware>
+            <emulateMode7>false</emulateMode7>
+            <leadingEdgeThreshold>12</leadingEdgeThreshold>
+            <nsa>100</nsa>
+            <nsb>20</nsb>
+            <nPeak>3</nPeak>
         </driver>
-        <driver name="EcalClusterer" type="org.hps.recon.ecal.cluster.GTPOnlineClusterDriver">
+        <driver name="GTPTestDriver" type="org.hps.recon.ecal.cluster.GTPOnlineClusterDriver">
+            <inputHitCollectionName>EcalCalHits</inputHitCollectionName>
+            <!-- <seedEnergyThreshold>0.500</seedEnergyThreshold>
+            <windowBefore>3</windowBefore>
+            <windowAfter>3</windowAfter> -->
+            <seedEnergyThreshold>0.010</seedEnergyThreshold>
+            <windowBefore>2</windowBefore>
+            <windowAfter>2</windowAfter>
+            <useDAQConfig>true</useDAQConfig>
             <verbose>false</verbose>
+        </driver>
+
+        <driver name="TriggerDiagnostics" type="org.hps.analysis.trigger.TriggerDiagnosticDriver">
+	    <printResultsEveryNEvents>10000</printResultsEveryNEvents>
+            <hitAcceptanceWindow>0</hitAcceptanceWindow>
+            <noiseThresholdCount>100</noiseThresholdCount>
+            <energyAcceptanceWindow>0.009</energyAcceptanceWindow>
+            <printOnClusterFailure>false</printOnClusterFailure>
+            <printOnSinglesSSPFailure>false</printOnSinglesSSPFailure>
+            <printOnSinglesEfficiencyFailure>false</printOnSinglesEfficiencyFailure>
+            <printOnPairSSPFailure>false</printOnPairSSPFailure>
+            <printOnPairEfficiencyFailure>false</printOnPairEfficiencyFailure>
+            <enforceStrictTimeCompliance>true</enforceStrictTimeCompliance> <!-- Set to false for mode-7 -->
+            <readDAQConfig>true</readDAQConfig>
+            <verbose>false</verbose>
+        </driver>
+
+        <driver name="GUIDiagnostics" type="org.hps.monitoring.trigger.TriggerDiagnosticGUIDriver"/>
+
+        <driver name="EcalClusterer" type="org.hps.recon.ecal.cluster.ReconClusterDriver">
             <logLevel>WARNING</logLevel>
-            <windowAfter>2</windowAfter>
-            <windowBefore>2</windowBefore>
-            <seedEnergyThreshold>0.100</seedEnergyThreshold>
-            <inputHitCollectionName>EcalCalHits</inputHitCollectionName>
             <outputClusterCollectionName>EcalClusters</outputClusterCollectionName>
+            <hitEnergyThreshold>0.01</hitEnergyThreshold>
+            <seedEnergyThreshold>0.100</seedEnergyThreshold> 
+            <clusterEnergyThreshold>0.200</clusterEnergyThreshold>
+            <minTime>0.0</minTime>
+            <timeWindow>25.0</timeWindow>
+            <useTimeCut>true</useTimeCut>
+            <writeRejectedHitCollection>false</writeRejectedHitCollection>
         </driver>
+        
         <driver name="EcalMonitoringPlots" type="org.hps.monitoring.ecal.plots.EcalMonitoringPlots">
             <inputCollection>EcalCalHits</inputCollection>
             <eventRefreshRate>5</eventRefreshRate>
-            <accumulateHits>false</accumulateHits>
         </driver>
         <driver name="EcalHitPlots" type="org.hps.monitoring.ecal.plots.EcalHitPlots">
             <inputCollection>EcalCalHits</inputCollection>
@@ -63,28 +114,6 @@
             <minEch>0.005</minEch>
             <eventRefreshRate>1</eventRefreshRate>
         </driver>        
-        <!--<driver name="EcalClusterer" type="org.hps.recon.ecal.HPSEcalCTPClusterer"> -->
-        
-        <!--  <driver name="EcalEvsX" type="org.hps.monitoring.ecal.EcalEvsX">
-            <targetZ>674</targetZ>
-            <inputCollection>EcalClusters</inputCollection>
-        </driver> -->
-        
-        <!-- <driver name="EcalWindowPlots" type="org.hps.monitoring.drivers.ecal.plots.EcalWindowPlots">
-            <inputCollection>EcalReadoutHits</inputCollection>
-        </driver> -->
-        
-        <!-- <driver name="EcalPedestalPlots" type="org.hps.monitoring.drivers.ecal.EcalPedestalPlots">
-            <inputCollection>EcalCalHits</inputCollection>
-            <eventRefreshRate>100</eventRefreshRate>
-        </driver> -->
-        
-        <!--  <driver name="TriggerPlots" type="org.hps.monitoring.ecal.TriggerPlots">
-            <clusterEnergyCut>0.500</clusterEnergyCut>
-        </driver> -->
-        
-        <!-- <driver name="AidaSaveDriver" type="org.lcsim.job.AidaSaveDriver">
-            <outputFileName>triggerEPlots</outputFileName>
-        </driver> -->
+        <driver name="ClockDriver" type="org.hps.readout.ecal.ClockDriver" />
     </drivers>
 </lcsim>

Modified: java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/TriggerDiagnosticsMonitoring.lcsim
 =============================================================================
--- java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/TriggerDiagnosticsMonitoring.lcsim	(original)
+++ java/branches/prod/steering-files/src/main/resources/org/hps/steering/monitoring/TriggerDiagnosticsMonitoring.lcsim	Wed Mar 25 14:43:27 2015
@@ -5,24 +5,16 @@
         <printDriversDetailed>false</printDriversDetailed>
     </control>
     <execute>
-        <driver name="EventMarkerDriver" />
-        <driver name="ConditionsDriver" /> 
-        <driver name="DAQConfig" />
+        <!-- <driver name="EventMarkerDriver" /> -->
         <driver name="EcalRawConverter" />
         <driver name="GTPTestDriver" />
         <driver name="TriggerDiagnostics" />
-        <!-- <driver name="AidaSaveDriver" />  -->
         <driver name="ClockDriver" />
-        <!-- <driver name="CleanupDriver" />  -->
     </execute>
     <drivers>
         <driver name="EventMarkerDriver" type="org.lcsim.job.EventMarkerDriver">
-            <eventInterval>1000</eventInterval>
+            <eventInterval>1</eventInterval>
         </driver>
-        <driver name="ConditionsDriver" type="org.hps.conditions.ConditionsDriver">
-            <tag>pass0</tag>
-        </driver>
-        <driver name="DAQConfig" type="org.hps.readout.ecal.daqconfig.DAQConfigDriver" />
         <driver name="EcalRawConverter" type="org.hps.recon.ecal.EcalRawConverterDriver">
             <ecalCollectionName>EcalCalHits</ecalCollectionName>
             <use2014Gain>false</use2014Gain>
@@ -37,13 +29,16 @@
         </driver>
         <driver name="GTPTestDriver" type="org.hps.recon.ecal.cluster.GTPOnlineClusterDriver">
             <inputHitCollectionName>EcalCalHits</inputHitCollectionName>
+            <!--
             <seedEnergyThreshold>0.500</seedEnergyThreshold>
             <windowBefore>3</windowBefore>
             <windowAfter>3</windowAfter>
-            <!-- <seedEnergyThreshold>0.010</seedEnergyThreshold>
+            -->
+            <!-- cuts used for GTP -->
+            <seedEnergyThreshold>0.010</seedEnergyThreshold>
             <windowBefore>2</windowBefore>
             <windowAfter>2</windowAfter>
-            <verbose>true</verbose> -->
+            <verbose>false</verbose>
         </driver>
         <driver name="TriggerDiagnostics" type="org.hps.analysis.trigger.TriggerDiagnosticDriver">
             <noiseThresholdCount>100</noiseThresholdCount>
@@ -51,18 +46,12 @@
             <printOnSinglesSSPFailure>false</printOnSinglesSSPFailure>
             <printOnSinglesEfficiencyFailure>false</printOnSinglesEfficiencyFailure>
             <printOnPairSSPFailure>false</printOnPairSSPFailure>
-            <printOnPairEfficiencyFailure>false</printOnPairEfficiencyFailure>
-            <!-- <readDAQConfig>true</readDAQConfig> -->
+            <printOnPairEfficiencyFailure>false</printOnPairEfficiencyFailure>            
+            <readDAQConfig>true</readDAQConfig>
             <verbose>true</verbose>
+            <!-- determines snapshot readout time which is set to 1 second here -->
+            <localWindowThresholdMilliseconds>1000</localWindowThresholdMilliseconds>
         </driver>
-<!--         
-        <driver name="AidaSaveDriver" type="org.lcsim.job.AidaSaveDriver">
-            <outputFileName>${outputFile}_triggerPlots</outputFileName>
-        </driver>
- -->        
         <driver name="ClockDriver" type="org.hps.readout.ecal.ClockDriver" />
-        <driver name="CleanupDriver" type="org.lcsim.recon.tracking.digitization.sisim.config.ReadoutCleanupDriver">
-            <collectionNames>TrackerHits</collectionNames>
-        </driver>
     </drivers>
 </lcsim>

Modified: java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/celentan/LedAnalysisFromEvio.lcsim
 =============================================================================
--- java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/celentan/LedAnalysisFromEvio.lcsim	(original)
+++ java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/celentan/LedAnalysisFromEvio.lcsim	Wed Mar 25 14:43:27 2015
@@ -30,6 +30,7 @@
         </driver>       
         <driver name="LedAnalysisDriver" type="org.hps.users.celentan.LedAnalysis">    
            <useRawEnergy>true</useRawEnergy>
+           <energyCut>5</energyCut>
         </driver>
         <driver name="AidaSaveDriver" type="org.lcsim.job.AidaSaveDriver">
             <outputFileName>${outputFile}.aida</outputFileName>

Modified: java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/mgraham/AlignmentMonitorTest.lcsim
 =============================================================================
--- java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/mgraham/AlignmentMonitorTest.lcsim	(original)
+++ java/branches/prod/steering-files/src/main/resources/org/hps/steering/users/mgraham/AlignmentMonitorTest.lcsim	Wed Mar 25 14:43:27 2015
@@ -5,16 +5,16 @@
 <lcsim xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/lcsim/1.0/lcsim.xsd">
     <execute>
         <driver name="EventMarkerDriver" />
-      <driver name="TrackerDigiDriver"/>
+<!--      <driver name="TrackerDigiDriver"/> -->
 <!--  the 3 drivers below are for reading out 6-sample ADC data -->
-<!--        <driver name="RawTrackerHitSensorSetup" /> 
+      <driver name="RawTrackerHitSensorSetup" /> 
         <driver name="RawTrackerHitFitterDriver" /> 
-        <driver name="TrackerHitDriver" />  -->
+       <driver name="TrackerHitDriver" />  
         <driver name="HelicalTrackHitDriver" />
          <driver name="TrackerL1to3ReconDriver"/> 
   <driver name="TrackerL4to6ReconDriver"/> 
 <!--           <driver name="TrackDataDriver"/>   
-        <driver name="EcalRawConverter" />
+       <driver name="EcalRawConverter" /> 
         <driver name="EcalClusterer" />
        <driver name="ReconParticle" />
         <driver name="TrackingMonitoring" />
@@ -46,7 +46,6 @@
             <useTimestamps>false</useTimestamps>
             <correctT0Shift>false</correctT0Shift>
             <useTruthTime>false</useTruthTime>
-            <subtractTOF>false</subtractTOF>
             <debug>false</debug>
         </driver>
         <driver name="TrackerHitDriver" type="org.hps.recon.tracking.DataTrackerHitDriver">
@@ -61,11 +60,10 @@
         </driver>
         <driver name="TrackerL1to3ReconDriver" type="org.hps.recon.tracking.TrackerReconDriver">
             <debug>false</debug>
-<!--            <strategyResource>/org/hps/recon/tracking/strategies/HPS-Full.xml</strategyResource> -->
         <trackCollectionName>L1to3Tracks</trackCollectionName>
             <strategyResource>/org/hps/recon/tracking/strategies/HPS-Full-L1-3.xml</strategyResource>
         </driver>
- <driver name="TrackerL4to6ReconDriver" type="org.hps.recon.tracking.TrackerReconDriver">
+        <driver name="TrackerL4to6ReconDriver" type="org.hps.recon.tracking.TrackerReconDriver">
             <debug>false</debug>
         <trackCollectionName>L4to6Tracks</trackCollectionName>
             <strategyResource>/org/hps/recon/tracking/strategies/HPS-Full-L4-6.xml</strategyResource>
@@ -81,19 +79,7 @@
         </driver>
         <driver name="ReconParticle" type="org.hps.recon.particle.HpsReconParticleDriver">
             <debug>false</debug>
-        </driver>
-        <driver name="BSTrackReconParticle" type="org.hps.recon.particle.HpsReconParticleDriver">
-            <debug>true</debug>
-            <tracksCollectionName>BeamSpotTracks</tracksCollectionName>
-            <finalStateParticlesColName>BSFinalStateParticles</finalStateParticlesColName>
-            <unconstrainedV0CandidatesColName>BSUnconstrainedV0Candidates</unconstrainedV0CandidatesColName>
-            <beamConV0CandidatesColName>BSBeamspotConstrainedV0Candidates</beamConV0CandidatesColName>
-            <targetConV0CandidatesColName>BSTargetConstrainedV0Candidates</targetConV0CandidatesColName>
-            <unconstrainedV0VerticesColName>BSUnconstrainedV0Vertices</unconstrainedV0VerticesColName>
-            <beamConV0VerticesColName>BSBeamspotConstrainedV0Vertices</beamConV0VerticesColName>
-            <targetConV0VerticesColName>BSTargetConstrainedV0Vertices</targetConV0VerticesColName>
-        </driver>
-     
+        </driver>   
         <driver name="TrackingMonitoring" type="org.hps.monitoring.drivers.trackrecon.TrackingReconPlots">         
         <outputPlots>tracking</outputPlots>
         </driver>
@@ -103,7 +89,7 @@
          <driver name="V0Monitoring" type="org.hps.monitoring.drivers.trackrecon.V0ReconPlots">         
         <outputPlots>v0recon</outputPlots>
         </driver>      
-  <driver name="SVTAlignment" type="org.hps.monitoring.drivers.trackrecon.SVTOpeningAlignment">         
+      <driver name="SVTAlignment" type="org.hps.monitoring.drivers.trackrecon.SVTOpeningAlignment">         
         <outputPlots>alignment</outputPlots>
         </driver>
 

Modified: java/branches/prod/tracking/src/main/java/org/hps/readout/svt/SimpleSvtReadout.java
 =============================================================================
--- java/branches/prod/tracking/src/main/java/org/hps/readout/svt/SimpleSvtReadout.java	(original)
+++ java/branches/prod/tracking/src/main/java/org/hps/readout/svt/SimpleSvtReadout.java	Wed Mar 25 14:43:27 2015
@@ -198,8 +198,10 @@
                 for (int sampleN = 0; sampleN < 6; sampleN++) {
                     double time = sampleN * HPSSVTConstants.SAMPLING_INTERVAL - timeOffset;
                     double tp = sensor.getShapeFitParameters(channel)[HpsSiSensor.TP_INDEX];
-                    signal[sampleN] = amplitude * pulseAmplitude(time, tp);
+                    signal[sampleN] += amplitude * pulseAmplitude(time, tp);//add the pulse to the pedestal
                     samples[sampleN] = (short) Math.round(signal[sampleN]);
+                    if (verbosity >= 1)
+                        System.out.println("\t\tMaking samples: sample#" + sampleN + " has " + samples[sampleN] + " ADC counts");
                 }
 
                 long channel_id = sensor.makeChannelID(channel);
@@ -290,15 +292,18 @@
 
     private boolean readoutCuts(RawTrackerHit hit) {
         if (enableThresholdCut && !samplesAboveThreshold(hit)) {
-            //System.out.println("Failed threshold cut");
+            if (verbosity > 1)
+                System.out.println("Failed threshold cut");
             return false;
         }
         if (enablePileupCut && !pileupCut(hit)) {
-            //System.out.println("Failed pileup cut");
+            if (verbosity > 1)
+                System.out.println("Failed pileup cut");
             return false;
         }
         if (dropBadChannels && !badChannelCut(hit)) {
-            //System.out.println("Failed bad channel cut");
+            if (verbosity > 1)
+                System.out.println("Failed bad channel cut");
             return false;
         }
         return true;
@@ -325,19 +330,18 @@
         for (int sampleN = 0; sampleN < samples.length; sampleN++) {
             pedestal = sensor.getPedestal(channel, sampleN);
             noise = sensor.getNoise(channel, sampleN);
-            //System.out.format("%d, %d\n", samples[sampleN] - pedestal, noise * 3.0);
-            if (samples[sampleN] - pedestal > noise * noiseThreshold) {
+            if (verbosity > 1)
+                System.out.format("%f, %f\n", samples[sampleN] - pedestal, noise * noiseThreshold);
+            if (samples[sampleN] - pedestal > noise * noiseThreshold)
                 count++;
-            }
         }
         return count >= samplesAboveThreshold;
     }
 
     @Override
     protected void processTrigger(EventHeader event) {
-        if (noPileup) {
+        if (noPileup)
             return;
-        }
         //System.out.println("Got trigger");
 
         // Create a list to hold the analog data
@@ -438,20 +442,18 @@
         @Override
         public int compareTo(Object o) {
             double deltaT = time - ((StripHit) o).time;
-            if (deltaT > 0) {
+            if (deltaT > 0)
                 return 1;
-            } else if (deltaT < 0) {
+            else if (deltaT < 0)
                 return -1;
-            } else {
+            else
                 return 0;
-            }
         }
     }
 
     private double pulseAmplitude(double time, double tp) {
-        if (time <= 0.0) {
+        if (time <= 0.0)
             return 0.0;
-        }
         return (time / tp) * Math.exp(1.0 - time / tp);
     }
 

Modified: java/branches/prod/users/src/main/java/org/hps/users/celentan/DummyDriverRaw.java
 =============================================================================
--- java/branches/prod/users/src/main/java/org/hps/users/celentan/DummyDriverRaw.java	(original)
+++ java/branches/prod/users/src/main/java/org/hps/users/celentan/DummyDriverRaw.java	Wed Mar 25 14:43:27 2015
@@ -6,10 +6,9 @@
 import org.lcsim.geometry.Detector;
 import org.lcsim.util.Driver;
 import org.lcsim.util.aida.AIDA;
-
-import org.hps.readout.ecal.triggerbank.SSPData;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
 import org.hps.recon.ecal.ECalUtils;
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.SSPData;
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.GenericObject;

Modified: java/branches/prod/users/src/main/java/org/hps/users/phansson/ROOTFlatTupleDriver.java
 =============================================================================
--- java/branches/prod/users/src/main/java/org/hps/users/phansson/ROOTFlatTupleDriver.java	(original)
+++ java/branches/prod/users/src/main/java/org/hps/users/phansson/ROOTFlatTupleDriver.java	Wed Mar 25 14:43:27 2015
@@ -7,6 +7,7 @@
 import hep.physics.vec.BasicHep3Vector;
 import hep.physics.vec.Hep3Vector;
 import hep.physics.vec.VecOp;
+
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -18,9 +19,10 @@
 import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+
 import org.hps.analysis.ecal.HPSMCParticlePlotsDriver;
-import org.hps.readout.ecal.triggerbank.AbstractIntData;
-import org.hps.readout.ecal.triggerbank.TestRunTriggerData;
+import org.hps.recon.ecal.triggerbank.AbstractIntData;
+import org.hps.recon.ecal.triggerbank.TestRunTriggerData;
 import org.hps.recon.tracking.BeamlineConstants;
 import org.hps.recon.tracking.EventQuality;
 import org.hps.recon.tracking.HPSTrack;