Print

Print


Author: [log in to unmask]
Date: Tue Dec  1 15:55:47 2015
New Revision: 3998

Log:
Dev work on run db and datacat; also includes trunk merges.

Added:
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/AbstractConditionsObjectConverter.java
      - copied, changed from r3997, java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileVisitor.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioReconMetadataReader.java
      - copied, changed from r3960, java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioMetadataReader.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/PathFilter.java
    java/branches/jeremy-dev/detector-data/detectors/HPS-EngRun2015-1_5mm-v3-4-fieldmap/
      - copied from r3995, java/trunk/detector-data/detectors/HPS-EngRun2015-1_5mm-v3-4-fieldmap/
    java/branches/jeremy-dev/detector-data/detectors/HPS-EngRun2015-Nominal-v3-4-fieldmap/millepede-dump-HPS-EngRun2015-Nominal-v3-4-fieldmap.dat
      - copied unchanged from r3995, java/trunk/detector-data/detectors/HPS-EngRun2015-Nominal-v3-4-fieldmap/millepede-dump-HPS-EngRun2015-Nominal-v3-4-fieldmap.dat
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/RfFitFunction.java
      - copied unchanged from r3995, java/trunk/evio/src/main/java/org/hps/evio/RfFitFunction.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/RfFitterDriver.java
      - copied unchanged from r3995, java/trunk/evio/src/main/java/org/hps/evio/RfFitterDriver.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/RfHit.java
      - copied unchanged from r3995, java/trunk/evio/src/main/java/org/hps/evio/RfHit.java
    java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/BeamspotTransformFilter.java
      - copied unchanged from r3964, java/trunk/recon/src/main/java/org/hps/recon/filtering/BeamspotTransformFilter.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractLoopAdapter.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordLoop.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigData.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigEvioProcessor.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseBuilder.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDao.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDaoImpl.java
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/baltzell/EngineeringRun2015TrigPairs1_Pass2.lcsim
      - copied unchanged from r3964, java/trunk/steering-files/src/main/resources/org/hps/steering/users/baltzell/EngineeringRun2015TrigPairs1_Pass2.lcsim
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/HitRecon.lcsim
      - copied unchanged from r3995, java/trunk/steering-files/src/main/resources/org/hps/steering/users/phansson/HitRecon.lcsim
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/Occupancy.lcsim
      - copied, changed from r3968, java/trunk/steering-files/src/main/resources/org/hps/steering/users/phansson/Occupancy.lcsim
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLKinkData.java
      - copied unchanged from r3995, java/trunk/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLKinkData.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/svt/alignment/MillepedeCompactDump.java
      - copied unchanged from r3995, java/trunk/tracking/src/main/java/org/hps/svt/alignment/MillepedeCompactDump.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfFitFunction.java
      - copied, changed from r3964, java/trunk/users/src/main/java/org/hps/users/baltzell/RfFitFunction.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfFitterDriver.java
      - copied, changed from r3964, java/trunk/users/src/main/java/org/hps/users/baltzell/RfFitterDriver.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfHit.java
      - copied unchanged from r3964, java/trunk/users/src/main/java/org/hps/users/baltzell/RfHit.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/phansson/STUtils.java
      - copied unchanged from r3995, java/trunk/users/src/main/java/org/hps/users/phansson/STUtils.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/phansson/StraightThroughAnalysisDriver.java
      - copied unchanged from r3995, java/trunk/users/src/main/java/org/hps/users/phansson/StraightThroughAnalysisDriver.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/spaul/StyleUtil.java
      - copied unchanged from r3965, java/trunk/users/src/main/java/org/hps/users/spaul/StyleUtil.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/spaul/SumEverything.java
      - copied unchanged from r3965, java/trunk/users/src/main/java/org/hps/users/spaul/SumEverything.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/spaul/feecc/
      - copied from r3965, java/trunk/users/src/main/java/org/hps/users/spaul/feecc/
Removed:
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioMetadataReader.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunProcessor.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/TriggerConfigDao.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/TriggerConfigDaoImpl.java
    java/branches/jeremy-dev/run-database/src/test/java/org/hps/run/database/TiTriggerOffsetTest.java
Modified:
    java/branches/jeremy-dev/   (props changed)
    java/branches/jeremy-dev/analysis/src/main/java/org/hps/analysis/trigger/TriggerTurnOnDriver.java
    java/branches/jeremy-dev/conditions/   (props changed)
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ConditionsDriver.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObject.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/AddCommand.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/PrintCommand.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/RunSummaryCommand.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsTagConverter.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/Converter.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConverterRegistry.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/dummy/DummyConditionsObjectConverter.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasConditionsLoader.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasMyaDataReader.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerConfig.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileUtilities.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatCrawler.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatUtilities.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/EvioMetadataReader.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/RunFilter.java
    java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java
    java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java
    java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java
    java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java
    java/branches/jeremy-dev/detector-model/src/main/java/org/lcsim/geometry/compact/converter/HPSTrackerBuilder.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/AbstractSvtEvioReader.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/EvioToLcio.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/SvtEventFlagger.java
    java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java
    java/branches/jeremy-dev/job/pom.xml
    java/branches/jeremy-dev/job/src/main/java/org/hps/job/JobManager.java
    java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java
    java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtPlotUtils.java
    java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalLedSequenceMonitor.java
    java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/EventFlagFilter.java
    java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/PulserTriggerFilterDriver.java
    java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/V0CandidateFilter.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordProcessor.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/RecordProcessor.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigDriver.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigEvioProcessor.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioDetectorConditionsProcessor.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoop.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetEvioProcessor.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDaoImpl.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsType.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsVariable.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseCommandLine.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseDaoFactory.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunManager.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummary.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDao.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDaoImpl.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryImpl.java
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/monitoring/EcalLedSequenceStandalone.lcsim
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/readout/HPSReconNoReadout.lcsim
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015FullReconMC_Pass2.lcsim
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullReconMC_Pass2_Gbl.lcsim
    java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullRecon_Pass2_Gbl.lcsim
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/TrackUtils.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutput.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutputDriver.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLRefitterDriver.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GblUtils.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/HpsGblRefitter.java
    java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/MakeGblTracks.java
    java/branches/jeremy-dev/users/src/main/java/org/hps/users/meeg/SvtChargeIntegrator.java

Modified: java/branches/jeremy-dev/analysis/src/main/java/org/hps/analysis/trigger/TriggerTurnOnDriver.java
 =============================================================================
--- java/branches/jeremy-dev/analysis/src/main/java/org/hps/analysis/trigger/TriggerTurnOnDriver.java	(original)
+++ java/branches/jeremy-dev/analysis/src/main/java/org/hps/analysis/trigger/TriggerTurnOnDriver.java	Tue Dec  1 15:55:47 2015
@@ -50,7 +50,7 @@
     IHistogram1D clusterEOne_Random_thetaY[][] = new IHistogram1D[2][5];
     IHistogram1D clusterEOne_RandomSingles1_thetaY[][] = new IHistogram1D[2][5];
     
-    private boolean showPlots = true;
+    private boolean showPlots = false;
     private int nEventsProcessed = 0;
     private int nSimSingles1 = 0;
     private int nResultSingles1 = 0;
@@ -70,6 +70,10 @@
     public TriggerTurnOnDriver() {
     }
     
+	public void setShowPlots(boolean showPlots) {
+		this.showPlots = showPlots;
+	}
+
     @Override
     protected void detectorChanged(Detector detector) {
         

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ConditionsDriver.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ConditionsDriver.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ConditionsDriver.java	Tue Dec  1 15:55:47 2015
@@ -33,7 +33,10 @@
  * time to achieve the proper behavior.
  *
  * @author Jeremy McCormick, SLAC
+ * 
+ * @deprecated Use built-in options of job manager.
  */
+@Deprecated
 public class ConditionsDriver extends Driver {
 
     /** The name of the detector model. */

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObject.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObject.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObject.java	Tue Dec  1 15:55:47 2015
@@ -15,7 +15,7 @@
  *
  * @author Jeremy McCormick, SLAC
  */
-public class BaseConditionsObject implements ConditionsObject {
+public abstract class BaseConditionsObject implements ConditionsObject {
 
     /**
      * Field name for collection ID.
@@ -440,4 +440,22 @@
         }
         return rowsUpdated != 0;
     }
+    
+    public boolean equals(Object object) {
+        // Is it the same object?
+        if (object == this) {
+            return true;
+        }
+        // Are these objects the same class?
+        if (object.getClass().equals(this.getClass())) {
+            BaseConditionsObject otherObject = BaseConditionsObject.class.cast(object);
+            // Do the row IDs and database table name match?
+            if (otherObject.getTableMetaData().getTableName().equals(this.getTableMetaData().getTableName()) &&
+                    this.getRowId() == otherObject.getRowId()) {
+                // These are considered the same object (same database table and row ID).
+                return true;
+            }
+        }
+        return false;
+    }
 }

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java	Tue Dec  1 15:55:47 2015
@@ -102,6 +102,15 @@
         if (object == null) {
             throw new IllegalArgumentException("The object argument is null.");
         }
+        //checkCollectionId(object);
+        final boolean added = this.objects.add(object);
+        if (!added) {
+            throw new RuntimeException("Failed to add object.");
+        }
+        return added;
+    }
+
+    private void checkCollectionId(final ObjectType object) {
         // Does this collection have a valid ID yet?
         if (this.getCollectionId() != BaseConditionsObject.UNSET_COLLECTION_ID) {
             // Does the object that is being added have a collection ID?
@@ -122,11 +131,6 @@
                 }
             }
         }
-        final boolean added = this.objects.add(object);
-        if (!added) {
-            throw new RuntimeException("Failed to add object.");
-        }
-        return added;
     }
 
     /**
@@ -344,7 +348,7 @@
         } else {
             // If the collection already exists in the database with this ID then it cannot be inserted.
             if (this.exists()) {
-                throw new DatabaseObjectException("The collection " + this.collectionId
+                throw new DatabaseObjectException("The collection ID " + this.collectionId
                         + " cannot be inserted because it already exists in the " + this.tableMetaData.getTableName()
                         + " table.", this);
             }
@@ -703,7 +707,6 @@
     public void writeCsv(final File file) throws IOException {
         FileWriter fileWriter = null;
         CSVPrinter csvFilePrinter = null;
-
         try {
             fileWriter = new FileWriter(file);
             csvFilePrinter = new CSVPrinter(fileWriter, CSVFormat.DEFAULT);

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/AddCommand.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/AddCommand.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/AddCommand.java	Tue Dec  1 15:55:47 2015
@@ -32,16 +32,13 @@
      */
     private static final Options OPTIONS = new Options();
     static {
-        OPTIONS.addOption(new Option("h", false, "print help for add command"));
-        OPTIONS.addOption("r", true, "starting run number (required)");
-        OPTIONS.getOption("r").setRequired(true);
-        OPTIONS.addOption("e", true, "ending run number (default is starting run number)");
-        OPTIONS.addOption("t", true, "table name (required)");
-        OPTIONS.getOption("t").setRequired(true);
-        OPTIONS.addOption("c", true, "collection ID (required)");
-        OPTIONS.getOption("c").setRequired(true);
-        OPTIONS.addOption("u", true, "user name (optional)");
-        OPTIONS.addOption("m", true, "notes about this conditions set (optional)");
+        OPTIONS.addOption(new Option("h", "help", false, "print help for add command"));
+        OPTIONS.addOption("r", "run-start", true, "starting run number (required)");
+        OPTIONS.addOption("e", "run-end", true, "ending run number (default is starting run number)");
+        OPTIONS.addOption("t", "table", true, "table name (required)");
+        OPTIONS.addOption("c", "collection", true, "collection ID (required)");
+        OPTIONS.addOption("u", "user", true, "user name (optional)");
+        OPTIONS.addOption("m", "notes", true, "notes about this conditions set (optional)");
     }
 
     /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java	Tue Dec  1 15:55:47 2015
@@ -34,13 +34,12 @@
     private static Options OPTIONS = new Options();
 
     static {
-        OPTIONS.addOption(new Option("h", false, "print help"));
-        OPTIONS.addOption(new Option("d", true, "detector name"));
-        OPTIONS.addOption(new Option("r", true, "run number"));
-        OPTIONS.addOption(new Option("p", true, "database connection properties file"));
-        OPTIONS.addOption(new Option("x", true, "conditions XML configuration file"));
-        OPTIONS.addOption(new Option("t", true, "conditions tag to use for filtering records"));
-        OPTIONS.addOption(new Option("l", true, "log level of the conditions manager (INFO, FINE, etc.)"));
+        OPTIONS.addOption(new Option("h", "help", false, "print help"));
+        OPTIONS.addOption(new Option("d", "detector", true, "detector name"));
+        OPTIONS.addOption(new Option("r", "run", true, "run number"));
+        OPTIONS.addOption(new Option("p", "connection", true, "database connection properties file"));
+        OPTIONS.addOption(new Option("x", "xml", true, "conditions XML configuration file"));
+        OPTIONS.addOption(new Option("t", "tag", true, "conditions tag to use for filtering records"));
     }
 
     /**
@@ -177,13 +176,6 @@
         // Create new manager.
         this.conditionsManager = DatabaseConditionsManager.getInstance();
 
-        // Set the conditions manager log level (does not affect logger of this class or sub-commands).
-        if (commandLine.hasOption("l")) {
-            final Level newLevel = Level.parse(commandLine.getOptionValue("l"));
-            Logger.getLogger(DatabaseConditionsManager.class.getPackage().getName()).setLevel(newLevel);
-            LOGGER.config("conditions manager log level will be set to " + newLevel.toString());
-        }
-
         // Connection properties.
         if (commandLine.hasOption("p")) {
             final File connectionPropertiesFile = new File(commandLine.getOptionValue("p"));

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java	Tue Dec  1 15:55:47 2015
@@ -33,12 +33,10 @@
      */
     private static final Options OPTIONS = new Options();
     static {
-        OPTIONS.addOption(new Option("h", false, "print help for load command"));
-        OPTIONS.addOption(new Option("t", true, "name of the target table (required)"));
-        OPTIONS.getOption("t").setRequired(true);
-        OPTIONS.addOption(new Option("f", true, "input data file path (required)"));
-        OPTIONS.getOption("f").setRequired(true);
-        OPTIONS.addOption(new Option("d", true, "description for the collection log"));
+        OPTIONS.addOption(new Option("h", "help", false, "print help for load command"));
+        OPTIONS.addOption(new Option("t", "table", true, "name of the target table (required)"));
+        OPTIONS.addOption(new Option("f", "file", true, "input data file path (required)"));
+        OPTIONS.addOption(new Option("d", "description", true, "description for the collection log"));
     }
 
     /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/PrintCommand.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/PrintCommand.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/PrintCommand.java	Tue Dec  1 15:55:47 2015
@@ -38,12 +38,12 @@
     static Options options = new Options();
 
     static {
-        options.addOption(new Option("h", false, "print help for print command"));
-        options.addOption(new Option("t", true, "table name"));
-        options.addOption(new Option("i", false, "print the ID for the records (off by default)"));
-        options.addOption(new Option("f", true, "write print output to a file (must be used with -t option)"));
-        options.addOption(new Option("H", false, "suppress printing of conditions record and table info"));
-        options.addOption(new Option("d", false, "use tabs for field delimiter instead of spaces"));
+        options.addOption(new Option("h", "help", false, "print help for print command"));
+        options.addOption(new Option("t", "table", true, "table name"));
+        options.addOption(new Option("i", "print-id", false, "print the ID for the records (off by default)"));
+        options.addOption(new Option("f", "file", true, "write print output to a file (must be used with -t option)"));
+        options.addOption(new Option("H", "no-header", false, "suppress printing of conditions record and table info"));
+        options.addOption(new Option("d", "tabs", false, "use tabs for field delimiter instead of spaces"));
     }
 
     /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/RunSummaryCommand.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/RunSummaryCommand.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/RunSummaryCommand.java	Tue Dec  1 15:55:47 2015
@@ -35,8 +35,8 @@
      */
     static Options options = new Options();
     static {
-        options.addOption(new Option("h", false, "Show help for run-summary command"));
-        options.addOption(new Option("a", false, "Print all collections found for the run"));
+        options.addOption(new Option("h", "print", false, "Show help for run-summary command"));
+        options.addOption(new Option("a", "all", false, "Print all collections found for the run"));
     }
 
     /**

Copied: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/AbstractConditionsObjectConverter.java (from r3997, java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java)
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/AbstractConditionsObjectConverter.java	Tue Dec  1 15:55:47 2015
@@ -1,11 +1,17 @@
-package org.hps.conditions.api;
+package org.hps.conditions.database;
 
 import java.sql.SQLException;
 import java.util.logging.Logger;
 
+import org.hps.conditions.api.BaseConditionsObjectCollection;
+import org.hps.conditions.api.ConditionsObject;
+import org.hps.conditions.api.ConditionsObjectCollection;
+import org.hps.conditions.api.ConditionsObjectException;
+import org.hps.conditions.api.ConditionsRecord;
+import org.hps.conditions.api.DatabaseObjectException;
+import org.hps.conditions.api.TableMetaData;
+import org.hps.conditions.api.TableRegistry;
 import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection;
-import org.hps.conditions.database.DatabaseConditionsManager;
-import org.hps.conditions.database.MultipleCollectionsAction;
 import org.lcsim.conditions.ConditionsConverter;
 import org.lcsim.conditions.ConditionsManager;
 
@@ -17,7 +23,6 @@
  * @author Jeremy McCormick, SLAC
  * @param <T> The type of the returned data which should be a class extending {@link BaseConditionsObjectCollection}.
  */
-// TODO: Move to conditions.database package (not an API class).
 public abstract class AbstractConditionsObjectConverter<T> implements ConditionsConverter<T> {
 
     /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java	Tue Dec  1 15:55:47 2015
@@ -3,7 +3,6 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
 import org.hps.conditions.api.ConditionsObject;
 import org.hps.conditions.api.ConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsObjectException;

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsTagConverter.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsTagConverter.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConditionsTagConverter.java	Tue Dec  1 15:55:47 2015
@@ -5,7 +5,6 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
 import org.hps.conditions.api.ConditionsObjectException;
 import org.hps.conditions.api.ConditionsTag;
 import org.hps.conditions.api.ConditionsTag.ConditionsTagCollection;

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/Converter.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/Converter.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/Converter.java	Tue Dec  1 15:55:47 2015
@@ -4,8 +4,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
 
 /**
  * This is an annotation for providing converter configuration for {@link org.hps.conditions.api.ConditionsObject}

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConverterRegistry.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConverterRegistry.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/ConverterRegistry.java	Tue Dec  1 15:55:47 2015
@@ -6,7 +6,6 @@
 
 import javassist.Modifier;
 
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
 import org.hps.conditions.api.BaseConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsObject;
 import org.hps.conditions.api.TableRegistry;

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java	Tue Dec  1 15:55:47 2015
@@ -20,7 +20,6 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
 import org.hps.conditions.api.ConditionsObject;
 import org.hps.conditions.api.ConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection;
@@ -349,7 +348,7 @@
      * Close the database connection.
      */
     public synchronized void closeConnection() {
-        LOGGER.fine("closing connection");
+        //LOGGER.finer("closing connection");
         if (this.connection != null) {
             try {
                 if (!this.connection.isClosed()) {
@@ -361,7 +360,7 @@
         }
         this.connection = null;
         this.isConnected = false;
-        LOGGER.fine("connection closed");
+        //LOGGER.finer("connection closed");
     }
 
     /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/dummy/DummyConditionsObjectConverter.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/dummy/DummyConditionsObjectConverter.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/dummy/DummyConditionsObjectConverter.java	Tue Dec  1 15:55:47 2015
@@ -1,6 +1,6 @@
 package org.hps.conditions.dummy;
 
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
+import org.hps.conditions.database.AbstractConditionsObjectConverter;
 import org.hps.conditions.dummy.DummyConditionsObject.DummyConditionsObjectCollection;
 
 /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java	Tue Dec  1 15:55:47 2015
@@ -4,12 +4,12 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.hps.conditions.api.AbstractConditionsObjectConverter;
 import org.hps.conditions.api.AbstractIdentifier;
 import org.hps.conditions.api.BaseConditionsObject;
 import org.hps.conditions.api.BaseConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsObjectException;
+import org.hps.conditions.database.AbstractConditionsObjectConverter;
 import org.hps.conditions.database.Converter;
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.conditions.database.Field;
@@ -290,11 +290,16 @@
         public EcalChannelCollection getData(final ConditionsManager conditionsManager, final String name) {
             final EcalChannelCollection collection = super.getData(conditionsManager, name);
             final Subdetector ecal = DatabaseConditionsManager.getInstance().getEcalSubdetector();
-            if (ecal.getDetectorElement() != null) {
-                collection.buildGeometryMap(ecal.getDetectorElement().getIdentifierHelper(), ecal.getSystemID());
+            if (ecal != null) {
+                if (ecal.getDetectorElement() != null) {
+                    collection.buildGeometryMap(ecal.getDetectorElement().getIdentifierHelper(), ecal.getSystemID());
+                } else {
+                    // This can happen when not running with the detector-framework jar in the classpath.
+                    throw new IllegalStateException("The ECal subdetector's detector element is not setup.");
+                }
             } else {
-                // This can happen when not running with the detector-framework jar in the classpath.
-                throw new IllegalStateException("The ECal subdetector's detector element is not setup.");
+                // Bad detector or conditions system not initialized properly.
+                throw new IllegalStateException("The ECal subdetector object is null.");
             }
             return collection;
         }

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java	Tue Dec  1 15:55:47 2015
@@ -25,7 +25,7 @@
  * The rows are accessible as raw CSV data through the Apache Commons CSV library, and this data must be manually cleaned up and converted 
  * to the correct data type before being inserted into the conditions database.
  *
- * @author Jeremy McCormick
+ * @author Jeremy McCormick, SLAC
  */
 public final class RunSpreadsheet {
 
@@ -38,23 +38,23 @@
         "start_time", 
         "end_time", 
         "to_tape", 
-        "n_events", 
+        "events",
         "files",
         "trigger_rate", 
         "target", 
         "beam_current",
         "beam_x", 
-        "beam_y", 
-        "trigger_config",
-        /*
-        "ecal_fadc_mode", 
+        "beam_y",
+        "trigger_config", 
+        /* Next 7 are actually hidden in the spreadsheet! */
+        "ecal_fadc_mode",
         "ecal_fadc_thresh", 
         "ecal_fadc_window", 
         "ecal_cluster_thresh_seed", 
         "ecal_cluster_thresh_cluster",
         "ecal_cluster_window_hits", 
-        "ecal_cluster_window_pairs",
-        */ 
+        "ecal_cluster_window_pairs", 
+        /* End hidden fields. */
         "ecal_scalers_fadc", 
         "ecal_scalers_dsc", 
         "svt_y_position", 
@@ -62,8 +62,7 @@
         "svt_offset_time",
         "ecal_temp", 
         "ecal_lv_current", 
-        "notes"
-    };
+        "notes"};
 
     /**
      * Read the CSV file from the command line and print the data to the terminal (just a basic test).
@@ -161,14 +160,15 @@
         return records;
     }
     
-    public static final AnotherSimpleDateFormat DATE_FORMAT = new AnotherSimpleDateFormat("MM/dd/yyyy H:mm"); 
+    public static final RunSpreadsheetDateFormat DATE_FORMAT = new RunSpreadsheetDateFormat("MM/dd/yyyy H:mm"); 
     private static final TimeZone TIME_ZONE =  TimeZone.getTimeZone("EST");
     
     
     @SuppressWarnings("serial")
     public
-    static class AnotherSimpleDateFormat extends SimpleDateFormat {
-        public AnotherSimpleDateFormat(String formatstring) {
+    static class RunSpreadsheetDateFormat extends SimpleDateFormat {
+        
+        public RunSpreadsheetDateFormat(String formatstring) {
             super(formatstring);
             //Calendar c = Calendar.getInstance(TIME_ZONE,Locale.US);
             //setTimeZone(TIME_ZONE);

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasConditionsLoader.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasConditionsLoader.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasConditionsLoader.java	Tue Dec  1 15:55:47 2015
@@ -6,22 +6,15 @@
 import hep.aida.IPlotter;
 import hep.aida.IPlotterStyle;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
 import java.sql.SQLException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.GregorianCalendar;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.TimeZone;
 import java.util.logging.Logger;
 
 import org.apache.commons.cli.CommandLine;
@@ -180,7 +173,7 @@
         options.addOption(new Option("t", false, "use run table format (from crawler) for bias"));
         options.addOption(new Option("d", false, "discard first line of MYA data (for myaData output)"));
         options.addOption(new Option("g", false, "Actually load stuff into DB"));
-        options.addOption(new Option("b", true, "beam current file"));
+//        options.addOption(new Option("b", true, "beam current file"));
         options.addOption(new Option("s", false, "Show plots"));
 
         final CommandLineParser parser = new DefaultParser();
@@ -261,9 +254,9 @@
             }
         }
 
-        if (cl.hasOption("b") && cl.hasOption("m") && cl.hasOption("p")) {
-            readBeamData(new File(cl.getOptionValue("b")), runList, positionRunRanges, biasRunRanges);
-        }
+//        if (cl.hasOption("b") && cl.hasOption("m") && cl.hasOption("p")) {
+//            readBeamData(new File(cl.getOptionValue("b")), runList, positionRunRanges, biasRunRanges);
+//        }
 
         // load to DB
         if (cl.hasOption("g")) {
@@ -427,111 +420,111 @@
         }
     }
 
-    private static void readBeamData(File file, List<RunData> runList, List<SvtPositionRunRange> positionRanges, List<SvtBiasRunRange> biasRanges) {
-        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-        dateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
-
-        Map<Integer, SvtPositionRunRange> positionRangeMap = new HashMap<Integer, SvtPositionRunRange>();
-        for (SvtPositionRunRange range : positionRanges) {
-            positionRangeMap.put(range.getRun().getRun(), range);
-        }
-        Map<Integer, SvtBiasRunRange> biasRangeMap = new HashMap<Integer, SvtBiasRunRange>();
-        for (SvtBiasRunRange range : biasRanges) {
-            biasRangeMap.put(range.getRun().getRun(), range);
-        }
-
-        try {
-            BufferedReader br = new BufferedReader(new FileReader(file));
-            String line;
-            System.out.println("myaData header: " + br.readLine()); //discard the first line
-            System.out.println("run\ttotalQ\ttotalQBias\tfracBias\ttotalQNom\tfracNom\ttotalQ1pt5\tfrac1pt5\ttotalGatedQ\ttotalGatedQBias\tfracGatedBias\ttotalGatedQNom\tfracGatedNom\ttotalGatedQ1pt5\tfracGated1pt5");
-
-            for (RunData run : runList) {
-                double totalCharge = 0;
-                double totalChargeWithBias = 0;
-                double totalChargeWithBiasAtNominal = 0;
-                double totalChargeWithBiasAt1pt5 = 0;
-                double totalGatedCharge = 0;
-                double totalGatedChargeWithBias = 0;
-                double totalGatedChargeWithBiasAtNominal = 0;
-                double totalGatedChargeWithBiasAt1pt5 = 0;
-                Date lastDate = null;
-
-                while ((line = br.readLine()) != null) {
-                    String arr[] = line.split(" +");
-
-                    if (arr.length != 4) {
-                        throw new java.text.ParseException("this line is not correct.", 0);
-                    }
-                    Date date = dateFormat.parse(arr[0] + " " + arr[1]);
-                    if (date.after(run.getEndDate())) {
-                        break;
-                    }
-                    if (date.before(run.getStartDate())) {
-                        continue;
-                    }
-
-                    double current, livetime;
-                    if (arr[2].equals("<undefined>")) {
-                        current = 0;
-                    } else {
-                        current = Double.parseDouble(arr[2]);
-                    }
-                    if (arr[3].equals("<undefined>")) {
-                        livetime = 0;
-                    } else {
-                        livetime = Math.min(100.0, Math.max(0.0, Double.parseDouble(arr[3]))) / 100.0;
-                    }
-
-                    if (date.after(run.getStartDate())) {
-                        if (lastDate != null) {
-                            double dt = (date.getTime() - lastDate.getTime()) / 1000.0;
-                            double dq = dt * current; // nC
-                            double dqGated = dt * current * livetime; // nC
-
-                            totalCharge += dq;
-                            totalGatedCharge += dqGated;
-                            SvtBiasRunRange biasRunRange = biasRangeMap.get(run.getRun());
-                            if (biasRunRange != null) {
-                                for (SvtBiasMyaRange biasRange : biasRunRange.getRanges()) {
-                                    if (biasRange.includes(date)) {
-                                        totalChargeWithBias += dq;
-                                        totalGatedChargeWithBias += dqGated;
-
-                                        SvtPositionRunRange positionRunRange = positionRangeMap.get(run.getRun());
-                                        if (positionRunRange != null) {
-                                            for (SvtPositionMyaRange positionRange : positionRunRange.getRanges()) {
-                                                if (positionRange.includes(date)) {
-                                                    if (Math.abs(positionRange.getBottom()) < 0.0001 && Math.abs(positionRange.getTop()) < 0.0001) {
-                                                        totalChargeWithBiasAtNominal += dq;
-                                                        totalGatedChargeWithBiasAtNominal += dqGated;
-                                                    } else if (Math.abs(positionRange.getBottom() - 0.0033) < 0.0001 && Math.abs(positionRange.getTop() - 0.0031) < 0.0001) {
-                                                        totalChargeWithBiasAt1pt5 += dq;
-                                                        totalGatedChargeWithBiasAt1pt5 += dqGated;
-                                                    }
-                                                    break;
-                                                }
-                                            }
-                                        }
-
-                                        break;
-                                    }
-                                }
-                            }
-
-                        }
-                    }
-                    lastDate = date;
-                }
-//                System.out.format("run\t%d\ttotalQ\t%.0f\ttotalQBias\t%.0f\tfracBias\t%f\ttotalQNom\t%.0f\tfracNom\t%f\ttotalQ1pt5\t%.0f\tfrac1pt5\t%f\ttotalGatedQ\t%.0f\ttotalGatedQBias\t%.0f\tfracGatedBias\t%f\ttotalGatedQNom\t%.0f\tfracGatedNom\t%f\ttotalGatedQ1pt5\t%.0f\tfracGated1pt5\t%f\n", run.getRun(), totalCharge, totalChargeWithBias, totalChargeWithBias / totalCharge, totalChargeWithBiasAtNominal, totalChargeWithBiasAtNominal / totalCharge, totalChargeWithBiasAt1pt5, totalChargeWithBiasAt1pt5 / totalCharge, totalGatedCharge, totalGatedChargeWithBias, totalGatedChargeWithBias / totalGatedCharge, totalGatedChargeWithBiasAtNominal, totalGatedChargeWithBiasAtNominal / totalGatedCharge, totalGatedChargeWithBiasAt1pt5, totalGatedChargeWithBiasAt1pt5 / totalGatedCharge);
-                System.out.format("%d\t%.0f\t%.0f\t%f\t%.0f\t%f\t%.0f\t%f\t%.0f\t%.0f\t%f\t%.0f\t%f\t%.0f\t%f\n", run.getRun(), totalCharge, totalChargeWithBias, totalChargeWithBias / totalCharge, totalChargeWithBiasAtNominal, totalChargeWithBiasAtNominal / totalCharge, totalChargeWithBiasAt1pt5, totalChargeWithBiasAt1pt5 / totalCharge, totalGatedCharge, totalGatedChargeWithBias, totalGatedChargeWithBias / totalGatedCharge, totalGatedChargeWithBiasAtNominal, totalGatedChargeWithBiasAtNominal / totalGatedCharge, totalGatedChargeWithBiasAt1pt5, totalGatedChargeWithBiasAt1pt5 / totalGatedCharge);
-            }
-            br.close();
-
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } catch (java.text.ParseException e) {
-            throw new RuntimeException(e);
-        }
-    }
+//    private static void readBeamData(File file, List<RunData> runList, List<SvtPositionRunRange> positionRanges, List<SvtBiasRunRange> biasRanges) {
+//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+//        dateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+//
+//        Map<Integer, SvtPositionRunRange> positionRangeMap = new HashMap<Integer, SvtPositionRunRange>();
+//        for (SvtPositionRunRange range : positionRanges) {
+//            positionRangeMap.put(range.getRun().getRun(), range);
+//        }
+//        Map<Integer, SvtBiasRunRange> biasRangeMap = new HashMap<Integer, SvtBiasRunRange>();
+//        for (SvtBiasRunRange range : biasRanges) {
+//            biasRangeMap.put(range.getRun().getRun(), range);
+//        }
+//
+//        try {
+//            BufferedReader br = new BufferedReader(new FileReader(file));
+//            String line;
+//            System.out.println("myaData header: " + br.readLine()); //discard the first line
+//            System.out.println("run\ttotalQ\ttotalQBias\tfracBias\ttotalQNom\tfracNom\ttotalQ1pt5\tfrac1pt5\ttotalGatedQ\ttotalGatedQBias\tfracGatedBias\ttotalGatedQNom\tfracGatedNom\ttotalGatedQ1pt5\tfracGated1pt5");
+//
+//            for (RunData run : runList) {
+//                double totalCharge = 0;
+//                double totalChargeWithBias = 0;
+//                double totalChargeWithBiasAtNominal = 0;
+//                double totalChargeWithBiasAt1pt5 = 0;
+//                double totalGatedCharge = 0;
+//                double totalGatedChargeWithBias = 0;
+//                double totalGatedChargeWithBiasAtNominal = 0;
+//                double totalGatedChargeWithBiasAt1pt5 = 0;
+//                Date lastDate = null;
+//
+//                while ((line = br.readLine()) != null) {
+//                    String arr[] = line.split(" +");
+//
+//                    if (arr.length != 4) {
+//                        throw new java.text.ParseException("this line is not correct.", 0);
+//                    }
+//                    Date date = dateFormat.parse(arr[0] + " " + arr[1]);
+//                    if (date.after(run.getEndDate())) {
+//                        break;
+//                    }
+//                    if (date.before(run.getStartDate())) {
+//                        continue;
+//                    }
+//
+//                    double current, livetime;
+//                    if (arr[2].equals("<undefined>")) {
+//                        current = 0;
+//                    } else {
+//                        current = Double.parseDouble(arr[2]);
+//                    }
+//                    if (arr[3].equals("<undefined>")) {
+//                        livetime = 0;
+//                    } else {
+//                        livetime = Math.min(100.0, Math.max(0.0, Double.parseDouble(arr[3]))) / 100.0;
+//                    }
+//
+//                    if (date.after(run.getStartDate())) {
+//                        if (lastDate != null) {
+//                            double dt = (date.getTime() - lastDate.getTime()) / 1000.0;
+//                            double dq = dt * current; // nC
+//                            double dqGated = dt * current * livetime; // nC
+//
+//                            totalCharge += dq;
+//                            totalGatedCharge += dqGated;
+//                            SvtBiasRunRange biasRunRange = biasRangeMap.get(run.getRun());
+//                            if (biasRunRange != null) {
+//                                for (SvtBiasMyaRange biasRange : biasRunRange.getRanges()) {
+//                                    if (biasRange.includes(date)) {
+//                                        totalChargeWithBias += dq;
+//                                        totalGatedChargeWithBias += dqGated;
+//
+//                                        SvtPositionRunRange positionRunRange = positionRangeMap.get(run.getRun());
+//                                        if (positionRunRange != null) {
+//                                            for (SvtPositionMyaRange positionRange : positionRunRange.getRanges()) {
+//                                                if (positionRange.includes(date)) {
+//                                                    if (Math.abs(positionRange.getBottom()) < 0.0001 && Math.abs(positionRange.getTop()) < 0.0001) {
+//                                                        totalChargeWithBiasAtNominal += dq;
+//                                                        totalGatedChargeWithBiasAtNominal += dqGated;
+//                                                    } else if (Math.abs(positionRange.getBottom() - 0.0033) < 0.0001 && Math.abs(positionRange.getTop() - 0.0031) < 0.0001) {
+//                                                        totalChargeWithBiasAt1pt5 += dq;
+//                                                        totalGatedChargeWithBiasAt1pt5 += dqGated;
+//                                                    }
+//                                                    break;
+//                                                }
+//                                            }
+//                                        }
+//
+//                                        break;
+//                                    }
+//                                }
+//                            }
+//
+//                        }
+//                    }
+//                    lastDate = date;
+//                }
+////                System.out.format("run\t%d\ttotalQ\t%.0f\ttotalQBias\t%.0f\tfracBias\t%f\ttotalQNom\t%.0f\tfracNom\t%f\ttotalQ1pt5\t%.0f\tfrac1pt5\t%f\ttotalGatedQ\t%.0f\ttotalGatedQBias\t%.0f\tfracGatedBias\t%f\ttotalGatedQNom\t%.0f\tfracGatedNom\t%f\ttotalGatedQ1pt5\t%.0f\tfracGated1pt5\t%f\n", run.getRun(), totalCharge, totalChargeWithBias, totalChargeWithBias / totalCharge, totalChargeWithBiasAtNominal, totalChargeWithBiasAtNominal / totalCharge, totalChargeWithBiasAt1pt5, totalChargeWithBiasAt1pt5 / totalCharge, totalGatedCharge, totalGatedChargeWithBias, totalGatedChargeWithBias / totalGatedCharge, totalGatedChargeWithBiasAtNominal, totalGatedChargeWithBiasAtNominal / totalGatedCharge, totalGatedChargeWithBiasAt1pt5, totalGatedChargeWithBiasAt1pt5 / totalGatedCharge);
+//                System.out.format("%d\t%.0f\t%.0f\t%f\t%.0f\t%f\t%.0f\t%f\t%.0f\t%.0f\t%f\t%.0f\t%f\t%.0f\t%f\n", run.getRun(), totalCharge, totalChargeWithBias, totalChargeWithBias / totalCharge, totalChargeWithBiasAtNominal, totalChargeWithBiasAtNominal / totalCharge, totalChargeWithBiasAt1pt5, totalChargeWithBiasAt1pt5 / totalCharge, totalGatedCharge, totalGatedChargeWithBias, totalGatedChargeWithBias / totalGatedCharge, totalGatedChargeWithBiasAtNominal, totalGatedChargeWithBiasAtNominal / totalGatedCharge, totalGatedChargeWithBiasAt1pt5, totalGatedChargeWithBiasAt1pt5 / totalGatedCharge);
+//            }
+//            br.close();
+//
+//        } catch (IOException e) {
+//            throw new RuntimeException(e);
+//        } catch (java.text.ParseException e) {
+//            throw new RuntimeException(e);
+//        }
+//    }
 }

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasMyaDataReader.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasMyaDataReader.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/svt/SvtBiasMyaDataReader.java	Tue Dec  1 15:55:47 2015
@@ -141,10 +141,6 @@
 
             records = parser.getRecords();
 
-            // Remove first two rows of headers.
-            records.remove(0);
-            records.remove(0);
-
             parser.close();
         } catch (FileNotFoundException ex) {
             Logger.getLogger(SvtBiasMyaDataReader.class.getName()).log(Level.SEVERE, null, ex);

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerConfig.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerConfig.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerConfig.java	Tue Dec  1 15:55:47 2015
@@ -11,6 +11,7 @@
 import java.util.Set;
 
 import org.hps.conditions.database.ConnectionParameters;
+import org.hps.datacat.client.DatacatConstants;
 import org.hps.datacat.client.DatasetFileFormat;
 import org.hps.datacat.client.DatasetSite;
 
@@ -41,20 +42,18 @@
 
     /**
      * The name of the folder in the data catalog for inserting data (under "/HPS" root folder).
-     * <p>
-     * Default provided for Eng Run 2015 data.
      */
     private String datacatFolder = null;
 
     /**
-     * Set whether extraction of metadata from files is enabled.
+     * Set whether extraction of metadata is enabled.
      */
     private boolean enableMetadata;
 
     /**
-     * Set of file formats for filtering files.
-     */
-    Set<DatasetFileFormat> formats = new HashSet<DatasetFileFormat>();
+     * Set of accepted file formats.
+     */
+    private Set<DatasetFileFormat> formats = new HashSet<DatasetFileFormat>();
 
     /**
      * The maximum depth to crawl.
@@ -69,7 +68,7 @@
     /**
      * The dataset site for the datacat.
      */
-    private DatasetSite site;
+    private DatasetSite site = DatasetSite.JLAB;
 
     /**
      * A timestamp to use for filtering input files on their creation date.
@@ -85,6 +84,21 @@
      * Dry run for not actually executing updates.
      */
     private boolean dryRun = false;
+    
+    /**
+     * Base URL of datacat client.
+     */
+    private String baseUrl = DatacatConstants.BASE_URL;
+    
+    /**
+     * Root URL of datacat client (e.g. 'HPS').
+     */
+    private String rootFolder = DatacatConstants.ROOT_FOLDER;
+    
+    /**
+     * Set of paths used for filtering files (file's path must match one of these).
+     */
+    private Set<String> paths = new HashSet<String>();
 
     /**
      * Get the set of runs that will be accepted for the job.
@@ -329,4 +343,28 @@
     boolean dryRun() {
         return this.dryRun;
     }
+    
+    void setBaseUrl(String baseUrl) {
+        this.baseUrl = baseUrl;        
+    }
+    
+    String baseUrl() {
+        return this.baseUrl;
+    }
+    
+    void setRootFolder(String rootFolder) {
+        this.rootFolder = rootFolder;
+    }
+    
+    String rootFolder() {
+        return this.rootFolder;
+    }    
+    
+    void addPath(String path) {
+        this.paths.add(path);
+    }
+    
+    Set<String> paths() {
+        return this.paths;
+    }
 }

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileUtilities.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileUtilities.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileUtilities.java	Tue Dec  1 15:55:47 2015
@@ -3,7 +3,7 @@
 import java.io.File;
 
 /**
- * File utilities for crawler.
+ * File utilities for the datacat crawler.
  *
  * @author Jeremy McCormick, SLAC
  */
@@ -19,4 +19,45 @@
         final String name = file.getName();
         return Integer.parseInt(name.substring(4, 10));
     }
+    
+    /**
+     * Get a cached file path, assuming that the input file path is on the JLAB MSS e.g. it starts with "/mss".
+     * If the file is not on the JLAB MSS an error will be thrown.
+     * <p>
+     * If the file is already on the cache disk just return the same file.
+     *
+     * @param mssFile the MSS file path
+     * @return the cached file path (prepends "/cache" to the path)
+     * @throws IllegalArgumentException if the file is not on the MSS (e.g. path does not start with "/mss")
+     */
+    public static File getCachedFile(final File mssFile) {
+        if (!isMssFile(mssFile)) {
+            throw new IllegalArgumentException("File " + mssFile.getPath() + " is not on the JLab MSS.");
+        }
+        File cacheFile = mssFile;
+        if (!isCachedFile(mssFile)) {
+            cacheFile = new File("/cache" + mssFile.getAbsolutePath());
+        }
+        return cacheFile;
+    }
+    
+    /**
+     * Return <code>true</code> if this is a file on the cache disk e.g. the path starts with "/cache".
+     *
+     * @param file the file
+     * @return <code>true</code> if the file is a cached file
+     */
+    public static boolean isCachedFile(final File file) {
+        return file.getPath().startsWith("/cache");
+    }
+    
+    /**
+     * Return <code>true</code> if this file is on the JLAB MSS e.g. the path starts with "/mss".
+     *
+     * @param file the file
+     * @return <code>true</code> if the file is on the MSS
+     */
+    public static boolean isMssFile(final File file) {
+        return file.getPath().startsWith("/mss");
+    }
 }

Added: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileVisitor.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileVisitor.java	(added)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/CrawlerFileVisitor.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,84 @@
+package org.hps.crawler;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hps.datacat.client.DatasetFileFormat;
+
+/**
+ * Visitor which creates a {@link FileSet} from walking a directory tree.
+ * <p>
+ * Any number of {@link java.io.FileFilter} objects can be registered with this visitor to restrict which files are
+ * accepted.
+ *
+ * @author Jeremy McCormick, SLAC
+ */
+final class CrawlerFileVisitor extends SimpleFileVisitor<Path> {
+
+    /**
+     * The run log containing information about files from each run.
+     */
+    private final FileSet fileSet = new FileSet();
+
+    /**
+     * A list of file filters to apply.
+     */
+    private final List<FileFilter> filters = new ArrayList<FileFilter>();
+
+    /**
+     * Run the filters on the file to tell whether it should be accepted or not.
+     *
+     * @param file the EVIO file
+     * @return <code>true</code> if file should be accepted
+     */
+    private boolean accept(final File file) {
+        boolean accept = true;
+        for (final FileFilter filter : this.filters) {
+            accept = filter.accept(file);
+            if (!accept) {
+                break;
+            }
+        }
+        return accept;
+    }
+
+    /**
+     * Add a file filter.
+     *
+     * @param filter the file filter
+     */
+    void addFilter(final FileFilter filter) {
+        this.filters.add(filter);
+    }
+
+    /**
+     * Get the file set created by visiting the directory tree.
+     *
+     * @return the file set from visiting the directory tree
+     */
+    FileSet getFileSet() {
+        return this.fileSet;
+    }
+
+    /**
+     * Visit a single file.
+     *
+     * @param path the file to visit
+     * @param attrs the file attributes
+     */
+    @Override
+    public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs) {
+        final File file = path.toFile();
+        if (this.accept(file)) {
+            final DatasetFileFormat format = DatacatUtilities.getFileFormat(file);
+            fileSet.addFile(format, file);
+        }
+        return FileVisitResult.CONTINUE;
+    }
+}

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatCrawler.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatCrawler.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatCrawler.java	Tue Dec  1 15:55:47 2015
@@ -1,15 +1,10 @@
 package org.hps.crawler;
 
 import java.io.File;
-import java.io.FileFilter;
 import java.io.IOException;
 import java.nio.file.FileVisitOption;
-import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -21,10 +16,10 @@
 import java.util.logging.Logger;
 
 import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.DefaultParser;
 import org.hps.datacat.client.DatacatClient;
 import org.hps.datacat.client.DatacatClientFactory;
 import org.hps.datacat.client.DatasetFileFormat;
@@ -35,81 +30,10 @@
  *
  * @author Jeremy McCormick, SLAC
  */
+// TODO: add support for patching metadata if resource exists
 public final class DatacatCrawler {
 
     /**
-     * Visitor which creates a {@link FileSet} from walking a directory tree.
-     * <p>
-     * Any number of {@link java.io.FileFilter} objects can be registered with this visitor to restrict which files are
-     * accepted.
-     *
-     * @author Jeremy McCormick, SLAC
-     */
-    final class DatacatFileVisitor extends SimpleFileVisitor<Path> {
-
-        /**
-         * The run log containing information about files from each run.
-         */
-        private final FileSet fileSet = new FileSet();
-
-        /**
-         * A list of file filters to apply.
-         */
-        private final List<FileFilter> filters = new ArrayList<FileFilter>();
-
-        /**
-         * Run the filters on the file to tell whether it should be accepted or not.
-         *
-         * @param file the EVIO file
-         * @return <code>true</code> if file should be accepted
-         */
-        private boolean accept(final File file) {
-            boolean accept = true;
-            for (final FileFilter filter : this.filters) {
-                accept = filter.accept(file);
-                if (!accept) {
-                    break;
-                }
-            }
-            return accept;
-        }
-
-        /**
-         * Add a file filter.
-         *
-         * @param filter the file filter
-         */
-        void addFilter(final FileFilter filter) {
-            this.filters.add(filter);
-        }
-
-        /**
-         * Get the file set created by visiting the directory tree.
-         *
-         * @return the file set from visiting the directory tree
-         */
-        FileSet getFileSet() {
-            return this.fileSet;
-        }
-
-        /**
-         * Visit a single file.
-         *
-         * @param path the file to visit
-         * @param attrs the file attributes
-         */
-        @Override
-        public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs) {
-            final File file = path.toFile();
-            if (this.accept(file)) {
-                final DatasetFileFormat format = DatacatUtilities.getFileFormat(file);
-                fileSet.addFile(format, file);
-            }
-            return FileVisitResult.CONTINUE;
-        }
-    }
-
-    /**
      * Make a list of available file formats for printing help.
      */
     private static String AVAILABLE_FORMATS;
@@ -118,7 +42,7 @@
      * Setup the logger.
      */
     private static final Logger LOGGER = Logger.getLogger(DatacatCrawler.class.getPackage().getName());
-
+    
     /**
      * Command line options for the crawler.
      */
@@ -148,6 +72,7 @@
         OPTIONS.addOption("t", "timestamp-file", true, "existing or new timestamp file name");
         OPTIONS.addOption("x", "max-depth", true, "max depth to crawl");
         OPTIONS.addOption("D", "dry-run", false, "dry run which will not update the datacat");
+        OPTIONS.addOption("u", "base-url", true, "provide a base URL of the datacat server");
     }
 
     /**
@@ -168,6 +93,11 @@
      * The options parser.
      */
     private final DefaultParser parser = new DefaultParser();
+    
+    /**
+     * The data catalog client interface.
+     */
+    private DatacatClient datacatClient;
 
     /**
      * Throw an exception if the path doesn't exist in the data catalog or it is not a folder.
@@ -176,7 +106,6 @@
      * @throws RuntimeException if the given path does not exist or it is not a folder
      */
     private void checkFolder(final String folder) {
-        final DatacatClient datacatClient = new DatacatClientFactory().createClient();
         if (!datacatClient.exists(folder)) {
             throw new RuntimeException("The folder " + folder + " does not exist in the data catalog.");
         }
@@ -328,13 +257,21 @@
             if (cl.hasOption("D")) {
                 config.setDryRun(true);
             }
+            
+            if (cl.hasOption("u")) {
+                config.setBaseUrl(cl.getOptionValue("u"));
+            }
+            
+            if (!cl.getArgList().isEmpty()) {
+                for (String arg : cl.getArgList()) {
+                    config.addPath(arg);
+                }
+                
+            }
 
         } catch (final ParseException e) {
             throw new RuntimeException("Error parsing options.", e);
         }
-
-        // Check the datacat folder which must already exist.
-        this.checkFolder(config.datacatFolder());
 
         // Check that there is at least one file format enabled for filtering.
         if (this.config.getFileFormats().isEmpty()) {
@@ -351,7 +288,7 @@
      */
     private void printUsage() {
         final HelpFormatter help = new HelpFormatter();
-        help.printHelp(70, "DatacatCrawler [options]", "", OPTIONS, "");
+        help.printHelp(70, "DatacatCrawler [options] path ...", "", OPTIONS, "");
         System.exit(0);
     }
 
@@ -359,19 +296,33 @@
      * Run the crawler job.
      */
     private void run() {
+        
+        LOGGER.config("creating datacat client with url = " + config.baseUrl() + "; site = " + config.datasetSite() + "; rootFolder = " + config.rootFolder());
+        datacatClient = new DatacatClientFactory().createClient(config.baseUrl(), config.datasetSite(), config.rootFolder()); 
+        
+        // Check the datacat folder which must already exist.
+        this.checkFolder(config.datacatFolder());
 
         // Create the file visitor for crawling the root directory with the given date filter.
-        final DatacatFileVisitor visitor = new DatacatFileVisitor();
+        final CrawlerFileVisitor visitor = new CrawlerFileVisitor();
 
         // Add date filter if timestamp is supplied.
         if (config.timestamp() != null) {
             visitor.addFilter(new DateFileFilter(config.timestamp()));
+            LOGGER.config("added timestamp filter " + config.timestamp());
+        }
+        
+        if (!config.paths().isEmpty()) {
+            visitor.addFilter(new PathFilter(config.paths()));
+            StringBuffer sb = new StringBuffer();
+            for (String path : config.paths()) {
+                sb.append(path + ":");
+            }
+            sb.setLength(sb.length() - 1);
+            LOGGER.config("added paths " + sb.toString());
         }
 
         // Add file format filter.
-        for (final DatasetFileFormat fileFormat : config.getFileFormats()) {
-            LOGGER.info("adding file format filter for " + fileFormat.name());
-        }
         visitor.addFilter(new FileFormatFilter(config.getFileFormats()));
 
         // Run number filter.
@@ -379,11 +330,17 @@
             visitor.addFilter(new RunFilter(config.acceptRuns()));
         }
 
-        // Walk the file tree using the visitor.
+        // Walk the file tree using the visitor with the enabled filters.
         this.walk(visitor);
+        
+        LOGGER.info(visitor.getFileSet().toString());
 
         // Update the data catalog.
-        this.updateDatacat(visitor.getFileSet());
+        if (!visitor.getFileSet().isEmpty()) {
+            this.updateDatacat(visitor.getFileSet());
+        } else {
+            LOGGER.warning("no files found");
+        }
     }
 
     /**
@@ -392,7 +349,6 @@
      * @param runMap the map of run information including the EVIO file list
      */
     private void updateDatacat(final FileSet fileSet) {
-        final DatacatClient datacatClient = new DatacatClientFactory().createClient();
         for (final DatasetFileFormat fileFormat : config.getFileFormats()) {
             List<File> formatFiles = fileSet.get(fileFormat);
             LOGGER.info("adding " + formatFiles.size() + " files with format " + fileFormat.name());
@@ -400,28 +356,50 @@
 
                 LOGGER.info("adding file " + file.getAbsolutePath());
 
-                // Create metadata if this is enabled (will take awhile).
                 Map<String, Object> metadata = new HashMap<String, Object>();
+
+                // Use file on JLAB cache disk if necessary.
+                File actualFile = file;
+                if (CrawlerFileUtilities.isMssFile(file)) {
+                    actualFile = CrawlerFileUtilities.getCachedFile(file);
+                    LOGGER.info("using cached file " + actualFile.getPath());
+                }
+                
                 if (config.enableMetaData()) {
-                    LOGGER.info("creating metadata for " + file.getPath());
-                    metadata = DatacatUtilities.createMetadata(file);
+                    // Create metadata map for file.
+                    LOGGER.info("creating metadata for " + actualFile.getPath());
+                    metadata = DatacatUtilities.createMetadata(actualFile);
+                    metadata.put("scanStatus", "OK");
+                } else {
+                    // Assign run number even if metadata is not enabled.
+                    metadata = new HashMap<String, Object>();
+                    int run = CrawlerFileUtilities.getRunFromFileName(file);
+                    metadata.put("runMin", run);
+                    metadata.put("runMax", run);
+                    metadata.put("scanStatus", "UNSCANNED");
                 }
 
                 // Register file in the catalog.
                 if (!config.dryRun()) {
-                    int response = DatacatUtilities.addFile(datacatClient, config.datacatFolder(), file, config.datasetSite(), metadata);
+                    int response = DatacatUtilities.addFile(
+                            datacatClient, 
+                            config.datacatFolder(),
+                            file,  
+                            actualFile.length(),
+                            config.datasetSite(), 
+                            metadata);
                     LOGGER.info("HTTP response " + response);
                     if (response >= 400) {
                         // Throw exception if response from server indicates an error occurred.
-                        throw new RuntimeException("HTTP error code " + response + " received from server.");
+                        throw new RuntimeException("HTTP error code " + response + " was received from server.");
                     }
                 } else {
-                    LOGGER.info("skipped updated on " + file.getPath() + " from dry run");
-                }
-            }
-            LOGGER.info("successfully added " + formatFiles.size() + " " + fileFormat + " files");
-        }
-        LOGGER.info("done updating datacat");
+                    LOGGER.info("Skipped update on " + file.getPath() + " because dry run is enabled.");
+                }
+            }
+            LOGGER.info("Successfully added " + formatFiles.size() + " " + fileFormat + " files to data catalog.");
+        }
+        LOGGER.info("Done updating data catalog.");
     }
        
     /**
@@ -429,7 +407,7 @@
      *
      * @param visitor the file visitor
      */
-    private void walk(final DatacatFileVisitor visitor) {
+    private void walk(final CrawlerFileVisitor visitor) {
         try {
             // Walk the file tree from the root directory.
             final EnumSet<FileVisitOption> options = EnumSet.noneOf(FileVisitOption.class);

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatUtilities.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatUtilities.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatUtilities.java	Tue Dec  1 15:55:47 2015
@@ -31,21 +31,6 @@
     /**
      * Add a file to the data catalog.
      *
-     * @param datacatClient the data catalog client
-     * @param folder the target folder in the data catalog
-     * @param file the file with the full path
-     * @param metadata the file's meta data
-     */
-    static int addFile(final DatacatClient datacatClient, final String folder, final File file,
-            DatasetSite site, final Map<String, Object> metadata) {
-        final DatasetFileFormat fileFormat = DatacatUtilities.getFileFormat(file);
-        final DatasetDataType dataType = DatacatUtilities.getDataType(file);
-        return DatacatUtilities.addFile(datacatClient, folder, file, metadata, fileFormat, dataType, site);
-    }
-
-    /**
-     * Add a file to the data catalog.
-     *
      * @param client the data catalog client
      * @param folder the folder name e.g. "data/raw"
      * @param fileMetadata the file's meta data including the path
@@ -53,21 +38,15 @@
      * @param dataType the file's data type (RAW, RECON, etc.)
      * @return the HTTP response code
      */
-    static int addFile(final DatacatClient client, final String folder, final File file,
-            final Map<String, Object> metadata, final DatasetFileFormat fileFormat, final DatasetDataType dataType,
-            final DatasetSite site) {
+    static int addFile(final DatacatClient client, final String folder, final File file, long fileLength,
+            final DatasetSite site, final Map<String, Object> metadata) {
         
-        // Get the cache file if this file is on JLAB MSS.
-        File actualFile = file;
-        if (EvioFileUtilities.isMssFile(file)) {
-            actualFile = EvioFileUtilities.getCachedFile(file);
-        }
-
+        // Get the dataset format and type.
+        final DatasetFileFormat fileFormat = DatacatUtilities.getFileFormat(file);
+        final DatasetDataType dataType = DatacatUtilities.getDataType(file);
+        
         // Add the dataset to the data catalog using the REST API.
-        final int response = client.addDataset(folder, dataType, file.getAbsolutePath(), actualFile.length(), site, fileFormat, 
-                file.getName(), metadata);
-
-        return response;
+        return client.addDataset(folder, dataType, file.getAbsolutePath(), fileLength, site, fileFormat, file.getName(), metadata);
     }
 
     /**
@@ -149,7 +128,7 @@
     static FileMetadataReader getFileMetaDataReader(final DatasetFileFormat fileFormat, final DatasetDataType dataType) {
         FileMetadataReader reader = null;
         if (fileFormat.equals(DatasetFileFormat.LCIO)) {
-            reader = new LcioMetadataReader();
+            reader = new LcioReconMetadataReader();
         } else if (fileFormat.equals(DatasetFileFormat.EVIO)) {
             reader = new EvioMetadataReader();
         } else if (fileFormat.equals(DatasetFileFormat.ROOT) && dataType.equals(DatasetDataType.DST)) {

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/EvioMetadataReader.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/EvioMetadataReader.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/EvioMetadataReader.java	Tue Dec  1 15:55:47 2015
@@ -4,32 +4,28 @@
 import java.io.IOException;
 import java.math.RoundingMode;
 import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.hps.record.epics.EpicsData;
-import org.hps.record.epics.EpicsEvioProcessor;
-import org.hps.record.evio.EventTagConstant;
-import org.hps.record.evio.EventTagMask;
 import org.hps.record.evio.EvioEventUtilities;
 import org.hps.record.evio.EvioFileUtilities;
-import org.hps.record.scalers.ScalerData;
-import org.hps.record.scalers.ScalersEvioProcessor;
+import org.hps.record.triggerbank.AbstractIntData.IntBankDefinition;
+import org.hps.record.triggerbank.HeadBankData;
+import org.hps.record.triggerbank.TIData;
+import org.hps.record.triggerbank.TiTimeOffsetEvioProcessor;
 import org.hps.record.triggerbank.TriggerType;
+import org.jlab.coda.jevio.BaseStructure;
 import org.jlab.coda.jevio.EvioEvent;
 import org.jlab.coda.jevio.EvioException;
 import org.jlab.coda.jevio.EvioReader;
 
 /**
- * Reads metadata from EVIO files, including the event count, run min and run max expected by the datacat,
- * as well as many custom field values applicable to HPS EVIO raw data.
+ * Reads metadata from EVIO files, including the event count, run min and run max expected by the datacat, as well as
+ * many custom field values applicable to HPS EVIO raw data.
  * <p>
  * The size of the data file is set externally to this reader using the datacat client.
  * 
@@ -38,245 +34,172 @@
 final class EvioMetadataReader implements FileMetadataReader {
 
     /**
-     * Initialize the logger.
+     * Initialize the package logger.
      */
     private static Logger LOGGER = Logger.getLogger(EvioMetadataReader.class.getPackage().getName());
-    
+
+    /**
+     * Head bank definition.
+     */
+    private static IntBankDefinition HEAD_BANK = new IntBankDefinition(HeadBankData.class, new int[] {0x2e, 0xe10f});
+
+    /**
+     * TI data bank definition.
+     */
+    private static IntBankDefinition TI_BANK = new IntBankDefinition(TIData.class, new int[] {0x2e, 0xe10a});
+
     /**
      * Get the EVIO file metadata.
-     *      
+     * 
      * @param file the EVIO file
      * @return the metadata map of key and value pairs
      */
     @Override
     public Map<String, Object> getMetadata(final File file) throws IOException {
 
-        Integer firstTimestamp = null;
-        Integer lastTimestamp = null;
-        Integer firstPhysicsTimestamp = null;
-        Integer lastPhysicsTimestamp = null;
-        int totalEvents = 0;
+        int events = 0;
         int badEvents = 0;
-        int epicsEvents = 0;
-        int scalerEvents = 0;
-        int physicsEvents = 0;
-        int syncEvents = 0;
-        int pauseEvents = 0;
-        int prestartEvents = 0;
-        int endEvents = 0;
-        int goEvents = 0;        
         boolean blinded = true;
         Integer run = null;
+        Integer firstHeadTimestamp = null;
+        Integer lastHeadTimestamp = null;
         Integer lastPhysicsEvent = null;
         Integer firstPhysicsEvent = null;
         double triggerRate = 0;
-        List<ScalerData> scalerData = new ArrayList<ScalerData>();
-        List<EpicsData> epicsData = new ArrayList<EpicsData>();
-                               
-        // Create map for counting event masks.        
-        Map<TriggerType, Integer> eventCounts = new HashMap<TriggerType, Integer>();
-        for (TriggerType mask : TriggerType.values()) {
-            eventCounts.put(mask, 0);
-        }
-                
-        // Scaler processor to check for scaler bank.
-        ScalersEvioProcessor scalersProcessor = new ScalersEvioProcessor();
-        
-        // EPICS data processor.
-        EpicsEvioProcessor epicsProcessor = new EpicsEvioProcessor(); 
+        long lastTI = 0;
+        long minTIDelta = 0;
+        long maxTIDelta = 0;
+        long firstTI = 0;
+        
+        TiTimeOffsetEvioProcessor tiProcessor = new TiTimeOffsetEvioProcessor();
+
+        // Create map for counting trigger types.
+        Map<TriggerType, Integer> triggerCounts = new LinkedHashMap<TriggerType, Integer>();
+        for (TriggerType triggerType : TriggerType.values()) {
+            triggerCounts.put(triggerType, 0);
+        }
 
         // Get the file number from the name.
         final int fileNumber = EvioFileUtilities.getSequenceFromName(file);
-        
-        // Only files divisible by 10 are unblinded (Eng Run 2015 scheme).
+
+        // Files with sequence number divisible by 10 are unblinded (Eng Run 2015 scheme).
         if (fileNumber % 10 == 0) {
             blinded = false;
         }
-        
+
         EvioReader evioReader = null;
-        try {                        
+        try {
             // Open file in sequential mode.
-            evioReader = EvioFileUtilities.open(file, true);            
+            evioReader = EvioFileUtilities.open(file, true);
             EvioEvent evioEvent = null;
 
             // Event read loop.
-            while (true) {
-                
-                // Read in an EVIO event, trapping exceptions in case a parse error occurs.
-                boolean badEvent = false;
+            fileLoop: while (true) {
                 try {
                     // Parse next event.
-                    evioEvent = evioReader.parseNextEvent();                    
-                } catch (IOException | EvioException e) {
-                    // Trap event parsing errors from bad EVIO data.
-                    badEvent = true;
-                    badEvents++;
-                    LOGGER.warning("bad EVIO event " + evioEvent.getEventNumber() + " could not be parsed");
-                } finally {
+                    evioEvent = evioReader.parseNextEvent();
+
                     // End of file.
-                    if (!badEvent && evioEvent == null) {
-                        LOGGER.info("EOF after " + totalEvents + " events");
-                        break;
+                    if (evioEvent == null) {
+                        LOGGER.info("EOF after " + events + " events");
+                        break fileLoop;
                     }
                     
-                    // Increment event count.
-                    totalEvents++;
-                }
-                                
-                // Continue to next event if a parse error occurred.
-                if (badEvent) {
-                    continue;
-                }                
-                                
-                // Debug print event number and tag.
-                LOGGER.finest("parsed event " + evioEvent.getEventNumber() + " with tag 0x" + String.format("%08x", evioEvent.getHeader().getTag()));
-                                
-                // Process different event types.
-                if (EventTagConstant.PRESTART.matches(evioEvent)) {
-                                                            
-                    // File has PRESTART event.
-                    LOGGER.fine("found PRESTART event " + evioEvent.getEventNumber());
-                    ++prestartEvents;
-                    
-                    // Set the run number from the PRESTART event.
-                    final int[] controlEventData = EvioEventUtilities.getControlEventData(evioEvent);
-                    if (run == null) {
-                        run = controlEventData[1];
-                        LOGGER.fine("set run to " + run + " from PRESTART");
-                    }
-                    
-                    // Set the first timestamp from the PRESTART event.
-                    if (firstTimestamp == null) {
-                        firstTimestamp = controlEventData[0];
-                        LOGGER.fine("set first timestamp to " + firstTimestamp + " from PRESTART event " + evioEvent.getEventNumber());
-                    }
-                    
-                } else if (EventTagConstant.GO.matches(evioEvent)) {
-                    
-                    // File has GO event.
-                    goEvents++;
-                    
-                    // Set first timestamp from the GO event (will not override PRESTART time).
-                    if (firstTimestamp == null) {
-                        final int[] controlEventData = EvioEventUtilities.getControlEventData(evioEvent);
-                        firstTimestamp = controlEventData[0];
-                        LOGGER.fine("set first timestamp to " + firstTimestamp + " from GO event " + evioEvent.getEventNumber());
-                    }
-                                                           
-                } else if (EventTagConstant.END.matches(evioEvent)) {
-                    
-                    // File has END event.
-                    LOGGER.fine("got END event");
-                    endEvents++;
-                    
-                    // Set the last timestamp from the END event.
-                    final int[] controlEventData = EvioEventUtilities.getControlEventData(evioEvent);
-                    lastTimestamp = controlEventData[0];
-                    LOGGER.fine("set last timestamp " + lastTimestamp + " from END event " + evioEvent.getEventNumber());
-                    if (run == null) {
-                        run = controlEventData[1];
-                        LOGGER.fine("set run to " + run);
-                    }
-                    
-                } else if (EventTagConstant.PAUSE.matches(evioEvent)) {
-                    
-                    // Count pause events.
-                    pauseEvents++;
-                    
-                } else if (EvioEventUtilities.isPhysicsEvent(evioEvent)) {
-                    
-                    // Count physics events.
-                    physicsEvents++;
-                    
+                    ++events;
+
+                    // Debug print event number and tag.
+                    LOGGER.finest("parsed event " + evioEvent.getEventNumber() + " with tag 0x"
+                            + String.format("%08x", evioEvent.getHeader().getTag()));
+
                     // Get head bank.
-                    final int[] headBankData = EvioEventUtilities.getHeadBankData(evioEvent);
-                    
-                    // Is head bank present?
-                    if (headBankData != null) {
+                    BaseStructure headBank = HEAD_BANK.findBank(evioEvent);
+
+                    // Current timestamp.
+                    int thisTimestamp = 0;
+
+                    // Process head bank if not null.
+                    if (headBank != null) {
+                        if (headBank != null) {
+                            final int[] headBankData = headBank.getIntData();
+                            thisTimestamp = headBankData[3];
+                            if (thisTimestamp != 0) {
+                                // First header timestamp.
+                                if (firstHeadTimestamp == null) {
+                                    firstHeadTimestamp = thisTimestamp;
+                                    LOGGER.info("first head timestamp " + firstHeadTimestamp + " from event "
+                                            + evioEvent.getEventNumber());
+                                }
+
+                                // Last header timestamp.
+                                lastHeadTimestamp = thisTimestamp;
+                            }
+
+                            // Run number.
+                            if (run == null) {
+                                if (headBankData[1] != 0) {
+                                    run = headBankData[1];
+                                    LOGGER.info("run " + run + " from event " + evioEvent.getEventNumber());
+                                }
+                            }
+                        }
+                    }
+
+                    // Process trigger bank data for TI times (copied from Sho's BasicEvioFileReader class).
+                    BaseStructure tiBank = TI_BANK.findBank(evioEvent);
+                    if (tiBank != null) {
+                        TIData tiData = new TIData(tiBank.getIntData());
+                        if (lastTI == 0) {
+                            firstTI = tiData.getTime();
+                        }
+                        lastTI = tiData.getTime();
+                        if (thisTimestamp != 0) {
+                            long delta = thisTimestamp * 1000000000L - tiData.getTime();
+                            if (minTIDelta == 0 || minTIDelta > delta) {
+                                minTIDelta = delta;
+                            }
+                            if (maxTIDelta == 0 || maxTIDelta < delta) {
+                                maxTIDelta = delta;
+                            }
+                        }
+                    }
+
+                    if (EvioEventUtilities.isPhysicsEvent(evioEvent)) {
+                                                
+                        final int[] eventIdData = EvioEventUtilities.getEventIdData(evioEvent);
                         
-                        // Is timestamp set?
-                        if (headBankData[3] != 0) {
-                            
-                            // Set first timestamp.
-                            if (firstTimestamp == null) {
-                                firstTimestamp = headBankData[3];
-                                LOGGER.fine("set first timestamp to " + firstTimestamp + " from physics event " + evioEvent.getEventNumber());                                
-                            }
-                            
-                            // Set first physics timestamp.
-                            if (firstPhysicsTimestamp == null) {                     
-                                firstPhysicsTimestamp = headBankData[3];
-                                LOGGER.fine("set first physics timestamp to " + firstTimestamp + " from event " + evioEvent.getEventNumber());                                
-                            }
-                            
-                            // Set last physics timestamp.
-                            lastPhysicsTimestamp = headBankData[3];
-                            LOGGER.finest("set last physics timestamp to " + firstTimestamp + " from event " + evioEvent.getEventNumber());
-                            
-                            // Set last timestamp.
-                            lastTimestamp = headBankData[3];
-                        }
+                        if (eventIdData != null) {
                         
-                        // Set run number.
-                        if (run == null) {
-                            run = headBankData[1];
-                            LOGGER.info("set run to " + run + " from physics event " + evioEvent.getEventNumber());
-                        }
-                    }                                                                
-                                                                                
-                    // Get the event ID data.
-                    final int[] eventIdData = EvioEventUtilities.getEventIdData(evioEvent);
-                    if (eventIdData != null) {
-                        
-                        // Set the last physics event.
-                        lastPhysicsEvent = eventIdData[0];
-
-                        // Set the first physics event.
-                        if (firstPhysicsEvent == null) {
-                            firstPhysicsEvent = eventIdData[0];
-                            LOGGER.fine("set start event " + firstPhysicsEvent + " from physics event " + evioEvent.getEventNumber());
-                        }                        
-                    }
-                                                                         
-                    // Count scaler events.
-                    scalersProcessor.process(evioEvent);
-                    if (scalersProcessor.getCurrentScalerData() != null) {
-                        scalerData.add(scalersProcessor.getCurrentScalerData());
-                        scalerEvents++;
-                    }
-                    
+                            // Set the last physics event.
+                            lastPhysicsEvent = eventIdData[0];
+
+                            // Set the first physics event.
+                            if (firstPhysicsEvent == null) {
+                                firstPhysicsEvent = eventIdData[0];
+                                LOGGER.info("set first physics event " + firstPhysicsEvent);
+                            }
+                        }
+                    }
+
                     // Count trigger types for this event.
                     Set<TriggerType> triggerTypes = TriggerType.getTriggerTypes(evioEvent);
                     for (TriggerType mask : triggerTypes) {
-                        int count = eventCounts.get(mask) + 1;
-                        eventCounts.put(mask, count);
-                        LOGGER.finer("incremented " + mask.name() + " to " + count);
+                        int count = triggerCounts.get(mask) + 1;
+                        triggerCounts.put(mask, count);
+                        LOGGER.finest("incremented " + mask.name() + " to " + count);
                     }
                     
-                    // Count sync events.
-                    if (EventTagMask.SYNC.matches(evioEvent.getHeader().getTag())) {
-                        // Count sync events.
-                        ++syncEvents;
-                        LOGGER.finer("got sync event from tag " + String.format("%08x", evioEvent.getHeader().getTag()));
-                    }
-                                          
-                } else if (EventTagConstant.EPICS.matches(evioEvent)) {
-                                        
-                    // Count EPICS events.
-                    ++epicsEvents;
+                    // Activate TI time offset processor.
+                    tiProcessor.process(evioEvent);
                     
-                    // Get EPICS data for charge calculation.
-                    epicsProcessor.process(evioEvent);
-                    EpicsData epicsEvent = epicsProcessor.getEpicsData();
-                    if (epicsEvent.hasKey("scaler_calc1")) {
-                        epicsData.add(epicsEvent);    
-                    }                    
-                } 
+                } catch (IOException | EvioException e) {
+                    // Trap event processing errors (not counted in event total).
+                    badEvents++;
+                    LOGGER.warning("error processing EVIO event " + evioEvent.getEventNumber());
+                }
             }
-            
         } catch (final EvioException e) {
             // Error reading the EVIO file.
-            throw new IOException(e);
+            throw new IOException("Error reading EVIO file.", e);
         } finally {
             // Close the reader.
             if (evioReader != null) {
@@ -287,68 +210,55 @@
                 }
             }
         }
-        
-        LOGGER.info("done reading "  + totalEvents + " events");
-        
+
+        LOGGER.info("done reading " + events + " events");
+
         // Rough trigger rate calculation.
-        triggerRate = calculateTriggerRate(firstPhysicsTimestamp, lastPhysicsTimestamp, physicsEvents);
-
-        // Calculate ungated charge.
-        //double ungatedCharge = calculateCharge(epicsData, firstPhysicsTimestamp, lastPhysicsTimestamp);
-        
-        // Calculated gated charge.
-        //double gatedCharge = calculateGatedCharge(ungatedCharge, scalerData, ScalerDataIndex.FCUP_TRG_GATED, ScalerDataIndex.FCUP_TRG_UNGATED);
-        
+        triggerRate = calculateTriggerRate(firstHeadTimestamp, lastHeadTimestamp, events);
+
         // Create and fill the metadata map.
         final Map<String, Object> metadataMap = new LinkedHashMap<String, Object>();
-        
-        // Built-in fields of datacat.
+
+        // Set built-in system metadata.
         metadataMap.put("runMin", run);
         metadataMap.put("runMax", run);
-        metadataMap.put("eventCount", totalEvents);
-        
-        // Run number.
-        metadataMap.put("RUN", run);
+        metadataMap.put("eventCount", events);
         
         // File sequence number.
-        metadataMap.put("FILE_NUMBER", fileNumber);
-        
+        metadataMap.put("FILE", fileNumber);
+
         // Blinded flag.
         metadataMap.put("BLINDED", blinded);
-        
+
         // First and last timestamps which may come from control or physics events.
-        metadataMap.put("FIRST_TIMESTAMP", firstTimestamp);
-        metadataMap.put("LAST_TIMESTAMP", lastTimestamp);
-        
-        // First and last physics events.
+        metadataMap.put("FIRST_HEAD_TIMESTAMP", firstHeadTimestamp);
+        metadataMap.put("LAST_HEAD_TIMESTAMP", lastHeadTimestamp);
+
+        // First and last physics event numbers.
         metadataMap.put("FIRST_PHYSICS_EVENT", firstPhysicsEvent);
         metadataMap.put("LAST_PHYSICS_EVENT", lastPhysicsEvent);
-        
-        // First and last physics event timestamps.
-        metadataMap.put("FIRST_PHYSICS_TIMESTAMP", firstPhysicsTimestamp);
-        metadataMap.put("LAST_PHYSICS_TIMESTAMP", lastPhysicsTimestamp);
-        
+
+        // TI times and offset.
+        metadataMap.put("FIRST_TI_TIME", firstTI);
+        metadataMap.put("LAST_TI_TIME", lastTI);
+        metadataMap.put("TI_TIME_DELTA", maxTIDelta - minTIDelta);
+        
+        // TI time offset (stored as string because of bug in MySQL datacat backend).
+        metadataMap.put("TI_TIME_OFFSET", ((Long) tiProcessor.getTiTimeOffset()).toString());
+
         // Event counts.
-        metadataMap.put("PHYSICS_EVENTS", physicsEvents);
         metadataMap.put("BAD_EVENTS", badEvents);
-        metadataMap.put("EPICS_EVENTS", epicsEvents);        
-        metadataMap.put("SCALER_EVENTS", scalerEvents);
-        metadataMap.put("END_EVENTS", endEvents);
-        metadataMap.put("PRESTART_EVENTS", prestartEvents);
-        metadataMap.put("GO_EVENTS", goEvents);
-        metadataMap.put("PAUSE_EVENTS", pauseEvents);
-        metadataMap.put("SYNC_EVENTS", syncEvents);
-        
-        // Trigger rate.
+        
+        // Trigger rate in KHz.
         DecimalFormat df = new DecimalFormat("#.####");
         df.setRoundingMode(RoundingMode.CEILING);
-        metadataMap.put("TRIGGER_RATE_KHZ", Double.parseDouble(df.format(triggerRate)));
-                                            
-        // Add the trigger counts.
-        for (Entry<TriggerType, Integer> entry : eventCounts.entrySet()) {
+        metadataMap.put("TRIGGER_RATE", Double.parseDouble(df.format(triggerRate)));
+
+        // Trigger type counts.
+        for (Entry<TriggerType, Integer> entry : triggerCounts.entrySet()) {
             metadataMap.put(entry.getKey().name(), entry.getValue());
         }
-        
+
         // Print the file metadata to log.
         StringBuffer sb = new StringBuffer();
         sb.append('\n');
@@ -356,20 +266,20 @@
             sb.append("  " + entry.getKey() + " = " + entry.getValue() + '\n');
         }
         LOGGER.info("file metadata ..." + '\n' + sb.toString());
-        
+
         // Return the completed metadata map.
         return metadataMap;
     }
-
+         
     /**
      * Calculate the trigger rate in KHz.
      * 
      * @param firstTimestamp the first physics timestamp
      * @param lastTimestamp the last physics timestamp
-     * @param physicsEvents the number of physics events
+     * @param events the number of physics events
      * @return the trigger rate calculation in KHz
      */
-    private double calculateTriggerRate(Integer firstTimestamp, Integer lastTimestamp, int physicsEvents) {
-        return ((double) physicsEvents / ((double) lastTimestamp - (double) firstTimestamp)) / 1000.;
-    }    
+    private double calculateTriggerRate(Integer firstTimestamp, Integer lastTimestamp, int events) {
+        return ((double) events / ((double) lastTimestamp - (double) firstTimestamp)) / 1000.;
+    }
 }

Copied: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioReconMetadataReader.java (from r3960, java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioMetadataReader.java)
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioMetadataReader.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/LcioReconMetadataReader.java	Tue Dec  1 15:55:47 2015
@@ -4,12 +4,16 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.lcsim.conditions.ConditionsManager;
 import org.lcsim.conditions.ConditionsManagerImplementation;
 import org.lcsim.conditions.ConditionsReader;
 import org.lcsim.event.EventHeader;
+import org.lcsim.event.EventHeader.LCMetaData;
 import org.lcsim.lcio.LCIOReader;
 import org.lcsim.util.loop.DummyConditionsConverter;
 import org.lcsim.util.loop.DummyDetector;
@@ -19,7 +23,7 @@
  * 
  * @author Jeremy McCormick, SLAC
  */
-public class LcioMetadataReader implements FileMetadataReader {
+public class LcioReconMetadataReader implements FileMetadataReader {
 
     /**
      * Setup the conditions system in dummy mode.
@@ -40,31 +44,53 @@
      */
     @Override
     public Map<String, Object> getMetadata(File file) throws IOException {
-        Map<String, Object> metaData = new HashMap<String, Object>();
-        LCIOReader reader = null;
+        
+        Set<String> collectionNames = new HashSet<String>();
+        String detectorName = null;
+        int eventCount = 0;
+        Integer run = null;
+        LCIOReader reader = null;                
         try {        
             reader = new LCIOReader(file);               
             EventHeader eventHeader = null;
-            int eventCount = 0;
-            Integer run = null;
             try {
                 while((eventHeader = reader.read()) != null) {
                     if (run == null) {
                         run = eventHeader.getRunNumber();
-                    }            
+                    }
+                    if (detectorName == null) {
+                        detectorName = eventHeader.getDetectorName();
+                    }
+                    for (List<?> list : eventHeader.getLists()) {
+                        LCMetaData metadata = eventHeader.getMetaData(list);
+                        collectionNames.add(metadata.getName());
+                    }
                     eventCount++;
                 }
             } catch (EOFException e) {
                 e.printStackTrace();
             }
-            metaData.put("eventCount", eventCount);
-            metaData.put("runMin", run);
-            metaData.put("runMax", run);
         } finally {
             if (reader != null) {
                 reader.close();
             }
-        }        
-        return metaData;
+        }    
+        
+        // Build collection names string.
+        StringBuffer sb = new StringBuffer();
+        for (String collectionName : collectionNames) {
+            sb.append(collectionName + ",");
+        }
+        sb.setLength(sb.length() - 1);
+        
+        Map<String, Object> metadata = new HashMap<String, Object>();
+        metadata.put("eventCount", eventCount);
+        metadata.put("runMin", run);
+        metadata.put("runMax", run);
+        metadata.put("DETECTOR", detectorName);
+        metadata.put("COLLECTIONS", sb.toString());
+        
+        
+        return metadata;
     }
 }

Added: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/PathFilter.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/PathFilter.java	(added)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/PathFilter.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,43 @@
+package org.hps.crawler;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Implementation of {@link java.io.FileFilter} which accepts a file if its path is 
+ * equal to any of the paths in a set of strings.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+final class PathFilter implements FileFilter {
+
+    private static Logger LOGGER = Logger.getLogger(PathFilter.class.getPackage().getName());
+    
+    /**
+     * Set of paths for filtering.
+     */
+    private Set<String> paths = null;
+    
+    PathFilter(Set<String> paths) {
+        this.paths = paths;
+    }
+
+    /**
+     * Return <code>true</code> if the <code>pathname</code> has a path which is in the set of <code>paths</code>.
+     * 
+     * @return <code>true</code> if <code>pathname</code> passes the filter
+     */
+    @Override
+    public boolean accept(File pathname) {
+        for (String acceptPath : paths) {
+            if (pathname.getPath().equals(acceptPath)) {
+                LOGGER.info("accepted path " + pathname);                
+                return true;
+            }
+        }
+        LOGGER.info("rejected path " + pathname);
+        return false;
+    }
+}

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/RunFilter.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/RunFilter.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/RunFilter.java	Tue Dec  1 15:55:47 2015
@@ -38,6 +38,11 @@
      */
     @Override
     public boolean accept(final File file) {
-        return this.acceptRuns.contains(EvioFileUtilities.getRunFromName(file));
+        try {
+            int run = EvioFileUtilities.getRunFromName(file);
+            return this.acceptRuns.contains(run);
+        } catch (NumberFormatException e) {
+            return false;
+        }
     }
 }

Modified: java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java
 =============================================================================
--- java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java	(original)
+++ java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java	Tue Dec  1 15:55:47 2015
@@ -9,6 +9,7 @@
  *
  * @author Jeremy McCormick, SLAC
  */
+// TODO: add method for adding a location to an existing dataset
 public interface DatacatClient {
 
     /**

Modified: java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java
 =============================================================================
--- java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java	(original)
+++ java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java	Tue Dec  1 15:55:47 2015
@@ -46,7 +46,7 @@
      * Create client with default parameters.
      */
     DatacatClientImpl() {
-        this(DatacatConstants.BASE_URL, DatasetSite.JLAB, DatacatConstants.ROOT_DIR);
+        this(DatacatConstants.BASE_URL, DatasetSite.JLAB, DatacatConstants.ROOT_FOLDER);
     }
 
     /**
@@ -60,7 +60,7 @@
         try {
             this.url = new URL(url);
         } catch (final MalformedURLException e) {
-            throw new IllegalArgumentException("The URL is bad.", e);
+            throw new IllegalArgumentException("The URL is not valid.", e);
         }
         if (site == null) {
             throw new IllegalArgumentException("The site argument is null.");
@@ -204,7 +204,7 @@
             }
         }
         
-        LOGGER.info("findDatasets: " + urlLocation);
+        LOGGER.info(urlLocation);
         final StringBuffer outputBuffer = new StringBuffer();
         final int response = HttpUtilities.doGet(urlLocation, outputBuffer);
         if (response >= 400) {
@@ -213,7 +213,7 @@
 
         // Build and return dataset list
         final JSONObject searchResults = new JSONObject(outputBuffer.toString());
-        LOGGER.info("returning search results: " + searchResults.toString());
+        LOGGER.info(searchResults.toString());
         return createDatasetsFromSearch(searchResults);
     }
 
@@ -276,7 +276,7 @@
     @Override
     public int makeFolder(final String path) {
         final Map<String, Object> parameters = new HashMap<String, Object>();
-        parameters.put("path", "/" + DatacatConstants.ROOT_DIR + "/" + path);
+        parameters.put("path", "/" + DatacatConstants.ROOT_FOLDER + "/" + path);
         final String name = new File(path).getName();
         parameters.put("name", name);
         parameters.put("_type", "folder");

Modified: java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java
 =============================================================================
--- java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java	(original)
+++ java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java	Tue Dec  1 15:55:47 2015
@@ -5,12 +5,12 @@
  * 
  * @author Jeremy McCormick, SLAC
  */
-final class DatacatConstants {
+public final class DatacatConstants {
 
     /**
      * The root directory in the catalog for HPS folders.
      */
-    public static final String ROOT_DIR = "HPS";
+    public static final String ROOT_FOLDER = "HPS";
         
     /**
      * The base URL of the datacat server.

Modified: java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java
 =============================================================================
--- java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java	(original)
+++ java/branches/jeremy-dev/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java	Tue Dec  1 15:55:47 2015
@@ -48,6 +48,10 @@
         if (metadataCopy.containsKey("eventCount")) {
             dataset.put("eventCount", metadataCopy.get("eventCount"));
             metadataCopy.remove("eventCount");
+        }
+        if (metadataCopy.containsKey("scanStatus")) {
+            dataset.put("scanStatus", metadataCopy.get("scanStatus"));
+            metadataCopy.remove("scanStatus");
         }
         
         if (metadata != null && metadata.size() != 0) {

Modified: java/branches/jeremy-dev/detector-model/src/main/java/org/lcsim/geometry/compact/converter/HPSTrackerBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/detector-model/src/main/java/org/lcsim/geometry/compact/converter/HPSTrackerBuilder.java	(original)
+++ java/branches/jeremy-dev/detector-model/src/main/java/org/lcsim/geometry/compact/converter/HPSTrackerBuilder.java	Tue Dec  1 15:55:47 2015
@@ -74,6 +74,10 @@
             LOGGER.info("mille parameters will be read from compact.xml file");
             initAlignmentParameters();
         }
+        if(debug) {
+            for (MilleParameter p : milleparameters)
+                System.out.printf("%d,%f \n", p.getId(),p.getValue());
+        }
     }
 
     /**
@@ -125,9 +129,8 @@
         if (debug) {
             System.out.printf("%s: Initialized %d alignment parameters:\n", this.getClass().getSimpleName(),
                     milleparameters.size());
-            for (MilleParameter p : milleparameters) {
+            for (MilleParameter p : milleparameters)
                 System.out.printf("%s: %s \n", this.getClass().getSimpleName(), p.toString());
-            }
         }
 
     }

Modified: java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/AbstractSvtEvioReader.java
 =============================================================================
--- java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/AbstractSvtEvioReader.java	(original)
+++ java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/AbstractSvtEvioReader.java	Tue Dec  1 15:55:47 2015
@@ -259,7 +259,7 @@
                         LOGGER.finest("this is a data multisample for apv " + SvtEvioUtils.getApvFromMultiSample(samples) + " ch " + SvtEvioUtils.getChannelNumber(samples));
                     
                     
-                    // Extract data words from multisample header 
+                    // Extract data words from multisample header and update index
                     multisampleHeaderIndex += this.extractMultisampleHeaderData(samples, multisampleHeaderIndex, multisampleHeaderData);
                     
                     // If a set of samples is associated with an APV header or tail, skip it
@@ -294,6 +294,12 @@
 
     
     
+    /**
+     * Process the headers that were extracted from the SVT data. 
+     * @param headers - list of all headers
+     * @param lcsimEvent - the current LCSIM event being processed
+     * @throws SvtEvioHeaderException
+     */
     protected abstract void processSvtHeaders(List<SvtHeaderDataInfo> headers, EventHeader lcsimEvent) throws SvtEvioHeaderException;
     
     /**
@@ -311,6 +317,13 @@
 
     }
     
+    /**
+     * Copy the multisample header data for the samples into a long array.
+     * @param samples
+     * @param index
+     * @param multisampleHeaderData
+     * @return
+     */
     protected int extractMultisampleHeaderData(int[] samples, int index, int[] multisampleHeaderData) {
         LOGGER.finest("extractMultisampleHeaderData: index " + index);
         if( SvtEvioUtils.isMultisampleHeader(samples) && !SvtEvioUtils.isMultisampleTail(samples) ) {
@@ -323,11 +336,23 @@
         }
     }
     
+    /**
+     * Checks that the SVT header data count is consistent with the bank size.
+     * @param sampleCount - sample count from the size.
+     * @param headerData - header extracted from the bank.
+     * @throws SvtEvioHeaderException
+     */
     protected void checkSvtSampleCount(int sampleCount, SvtHeaderDataInfo headerData) throws SvtEvioHeaderException {
         if( sampleCount != SvtEvioUtils.getSvtTailMultisampleCount(headerData.getTail())*4)
             throw new SvtEvioHeaderException("multisample count is not consistent with bank size.");
     }
     
+    /**
+     * Add the multisample headers to the {@link SvtHeaderDataInfo} object.
+     * @param headerData - object to add multisample headers to.
+     * @param n - number of multisample headers
+     * @param multisampleHeaders - multisample headers to copy
+     */
     protected void setMultiSampleHeaders(SvtHeaderDataInfo headerData, int n, int[] multisampleHeaders) {
         //copy out the headers that are non-zero
         int[] vals = new int[n];

Modified: java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/EvioToLcio.java
 =============================================================================
--- java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/EvioToLcio.java	(original)
+++ java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/EvioToLcio.java	Tue Dec  1 15:55:47 2015
@@ -367,6 +367,10 @@
         // Process the LCSim job variable definitions, if any.
         jobManager = new JobManager();
         
+        // Initialize run manager and add as listener on conditions system.
+        RunManager runManager = RunManager.getRunManager();
+        DatabaseConditionsManager.getInstance().addConditionsListener(runManager);
+        
         // Enable dry run because events will be processed individually.
         jobManager.setDryRun(true);
         

Modified: java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java	(original)
+++ java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/LCSimEngRunEventBuilder.java	Tue Dec  1 15:55:47 2015
@@ -67,6 +67,8 @@
      * Modulus of TI timestamp offset (units of nanoseconds).
      */
     private final long timestampCycle = 24 * 6 * 35;
+    
+    private Long currentTiTimeOffset = null;
 
     /**
      * Class constructor.
@@ -92,22 +94,27 @@
     public void conditionsChanged(final ConditionsEvent conditionsEvent) {
         super.conditionsChanged(conditionsEvent);
         svtEventFlagger.initialize();
-    }
-
-    /**
-     * Get the time from the TI data.
+        
+        // Get TI time offset from run db.
+        if (RunManager.getRunManager().runExists() && RunManager.getRunManager().getRunSummary().getTiTimeOffset() != null) {
+            currentTiTimeOffset = RunManager.getRunManager().getRunSummary().getTiTimeOffset();
+            LOGGER.info("TI time offset set to " + currentTiTimeOffset + " for run " + conditionsEvent.getConditionsManager().getRun());
+        } else {
+            currentTiTimeOffset = null;
+            LOGGER.info("no TI time offset in database for run " + conditionsEvent.getConditionsManager().getRun());
+        }
+    }
+
+    /**
+     * Get the time from the TI data with time offset applied from run database.
      *
      * @param triggerList the TI data list
      */
     @Override
     protected long getTime(final List<AbstractIntData> triggerList) {
         long tiTimeOffset = 0;
-        try {
-            if (RunManager.getRunManager().runExists() && RunManager.getRunManager().getTriggerConfig().getTiTimeOffset() != null) {
-                tiTimeOffset = (RunManager.getRunManager().getTriggerConfig().getTiTimeOffset() / timestampCycle) * timestampCycle;
-            }
-        } catch (IllegalStateException e) {
-            // May happen if RunManager is not initialized; just ignore.
+        if (currentTiTimeOffset != null) {
+            tiTimeOffset = (currentTiTimeOffset / timestampCycle) * timestampCycle;
         }
         for (final AbstractIntData data : triggerList) {
             if (data instanceof TIData) {

Modified: java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/SvtEventFlagger.java
 =============================================================================
--- java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/SvtEventFlagger.java	(original)
+++ java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/SvtEventFlagger.java	Tue Dec  1 15:55:47 2015
@@ -16,6 +16,7 @@
 import org.hps.conditions.svt.SvtAlignmentConstant;
 import org.hps.conditions.svt.SvtBiasConstant;
 import org.hps.conditions.svt.SvtMotorPosition;
+import org.hps.conditions.svt.SvtTimingConstants;
 import org.hps.record.svt.SvtHeaderDataInfo;
 import org.hps.record.triggerbank.AbstractIntData;
 import org.hps.record.triggerbank.HeadBankData;
@@ -35,9 +36,11 @@
     private static final double angleTolerance = 0.0001;
     SvtBiasConstant.SvtBiasConstantCollection svtBiasConstants = null;
     SvtMotorPosition.SvtMotorPositionCollection svtPositionConstants = null;
+    private SvtTimingConstants svtTimingConstants = null;
     private boolean biasGood = false;
     private boolean positionGood = false;
     private boolean burstmodeNoiseGood = false;
+    private boolean latencyGood = false;
     private double nominalAngleTop = 0;
     private double nominalAngleBottom = 0;
 
@@ -64,11 +67,24 @@
             }
         }
 
+        latencyGood = false;
+        if (svtTimingConstants != null) {
+            if (svtTimingConstants.getOffsetTime() <= 27) {
+                latencyGood = true;
+            } else {
+                if (((event.getTimeStamp() - 4 * svtTimingConstants.getOffsetPhase()) % 24) < 16) {
+                    latencyGood = true;
+                }
+            }
+//        System.out.format("%f %b\n", svtTimingConstants.getOffsetTime() + (((event.getTimeStamp() - 4 * svtTimingConstants.getOffsetPhase()) % 24) - 12), latencyGood);
+        }
+
         burstmodeNoiseGood = isBurstmodeNoiseGood(event);
 
         event.getIntegerParameters().put("svt_bias_good", new int[]{biasGood ? 1 : 0});
         event.getIntegerParameters().put("svt_position_good", new int[]{positionGood ? 1 : 0});
         event.getIntegerParameters().put("svt_burstmode_noise_good", new int[]{burstmodeNoiseGood ? 1 : 0});
+        event.getIntegerParameters().put("svt_latency_good", new int[]{latencyGood ? 1 : 0});
     }
 
     private Date getEventTimeStamp(EventHeader event) {
@@ -109,6 +125,13 @@
         } catch (Exception e) {
             svtPositionConstants = null;
         }
+
+        try {
+            svtTimingConstants = DatabaseConditionsManager.getInstance().getCachedConditions(SvtTimingConstants.SvtTimingConstantsCollection.class, "svt_timing_constants").getCachedData().get(0);
+        } catch (Exception e) {
+            svtTimingConstants = null;
+        }
+
     }
 
     private static boolean isBurstmodeNoiseGood(EventHeader event) {
@@ -162,92 +185,90 @@
 
     public static void voidAddHeaderCheckResultToMetaData(boolean ok, EventHeader lcsimEvent) {
         //System.out.println("adding svt header check ");
-        lcsimEvent.getIntegerParameters().put("svt_event_header_good", new int[]{ ok ? 1 : 0});
+        lcsimEvent.getIntegerParameters().put("svt_event_header_good", new int[]{ok ? 1 : 0});
         //if(lcsimEvent.hasItem("svt_event_header_good"))
         //        System.out.println("event header has the svt header check ");
         //else
         //    System.out.println("event header doesn't have the svt header check ");
     }
-    
+
     public static void AddHeaderInfoToMetaData(List<SvtHeaderDataInfo> headers, EventHeader lcsimEvent) {
         int[] svtHeaders = new int[headers.size()];
         int[] svtTails = new int[headers.size()];
-        for(int iSvtHeader=0; iSvtHeader < headers.size();++iSvtHeader) {
+        for (int iSvtHeader = 0; iSvtHeader < headers.size(); ++iSvtHeader) {
             svtHeaders[iSvtHeader] = headers.get(iSvtHeader).getHeader();
             svtTails[iSvtHeader] = headers.get(iSvtHeader).getTail();
-            
+
             lcsimEvent.getIntegerParameters().put("svt_event_header_roc" + headers.get(iSvtHeader).getNum(), new int[]{headers.get(iSvtHeader).getHeader()});
             lcsimEvent.getIntegerParameters().put("svt_event_tail_roc" + headers.get(iSvtHeader).getNum(), new int[]{headers.get(iSvtHeader).getTail()});
-            
-            
+
             int nMS = headers.get(iSvtHeader).getNumberOfMultisampleHeaders();
-            int[] multisampleHeadersArray = new int[4*nMS];
-            for(int iMS = 0; iMS < nMS; ++iMS ) {
+            int[] multisampleHeadersArray = new int[4 * nMS];
+            for (int iMS = 0; iMS < nMS; ++iMS) {
                 int[] multisampleHeader = headers.get(iSvtHeader).getMultisampleHeader(iMS);
-                System.arraycopy(multisampleHeader, 0, multisampleHeadersArray, iMS*4, multisampleHeader.length);
+                System.arraycopy(multisampleHeader, 0, multisampleHeadersArray, iMS * 4, multisampleHeader.length);
             }
             lcsimEvent.getIntegerParameters().put("svt_multisample_headers_roc" + headers.get(iSvtHeader).getNum(), multisampleHeadersArray);
         }
-        
-    }
-    
-    private static final Pattern rocIdPattern  = Pattern.compile("svt_.*_roc(\\d+)");
-    
+
+    }
+
+    private static final Pattern rocIdPattern = Pattern.compile("svt_.*_roc(\\d+)");
+
     public static int getRocFromSvtHeaderName(String seq) {
         Matcher m = rocIdPattern.matcher(seq);
-        if(m == null) 
+        if (m == null) {
             throw new RuntimeException("null matcher, don't think this should happen");
-        if( !m.matches() ) 
+        }
+        if (!m.matches()) {
             return -1;
-        else
-            return Integer.parseInt( m.group(1) );
-    }
-    
-    
-    
-    public static List<SvtHeaderDataInfo>  getHeaderInfoToMetaData(EventHeader lcsimEvent) {
-        Map<Integer, Integer> headers = new HashMap<Integer,Integer>();
-        Map<Integer, Integer> tails = new HashMap<Integer,Integer>();
-        Map<Integer, Integer[]> multisampleHeaders = new HashMap<Integer,Integer[]>();
-        
-        
-        for(Map.Entry<String, int[]> entry : lcsimEvent.getIntegerParameters().entrySet()) {
-            
+        } else {
+            return Integer.parseInt(m.group(1));
+        }
+    }
+
+    public static List<SvtHeaderDataInfo> getHeaderInfoToMetaData(EventHeader lcsimEvent) {
+        Map<Integer, Integer> headers = new HashMap<Integer, Integer>();
+        Map<Integer, Integer> tails = new HashMap<Integer, Integer>();
+        Map<Integer, Integer[]> multisampleHeaders = new HashMap<Integer, Integer[]>();
+
+        for (Map.Entry<String, int[]> entry : lcsimEvent.getIntegerParameters().entrySet()) {
+
             int roc = getRocFromSvtHeaderName(entry.getKey());
-            
-            if( roc == -1) {
+
+            if (roc == -1) {
                 continue;
             }
             //LOGGER.logger.fine("processing entry \"" + entry.getKey()+ "\"" + " for roc "  + roc);
             int[] value = entry.getValue();
-           
-            if(entry.getKey().contains("svt_event_header_roc"))
+
+            if (entry.getKey().contains("svt_event_header_roc")) {
                 headers.put(roc, value[0]);
-            
-            if(entry.getKey().contains("svt_event_tail_roc")) 
+            }
+
+            if (entry.getKey().contains("svt_event_tail_roc")) {
                 tails.put(roc, value[0]);
-                
+            }
+
             // really need to copy?
-            if(entry.getKey().contains("svt_multisample_headers_roc")) {
+            if (entry.getKey().contains("svt_multisample_headers_roc")) {
                 Integer[] tmp = ArrayUtils.toObject(value); //new Integer[value.length];
                 multisampleHeaders.put(roc, tmp);
             }
-                    
-        }
-        
+
+        }
+
         // create the new objects
         List<SvtHeaderDataInfo> headerDataInfo = new ArrayList<SvtHeaderDataInfo>();
-        for(Integer roc : headers.keySet()) {
+        for (Integer roc : headers.keySet()) {
             int header = headers.get(roc);
             int tail = tails.get(roc);
             Integer[] ms = multisampleHeaders.get(roc);
             headerDataInfo.add(new SvtHeaderDataInfo(roc, header, tail, ms));
         }
-        
-       return headerDataInfo;
-        
-    }
-    
-    
-    
+
+        return headerDataInfo;
+
+    }
+
 }

Modified: java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java
 =============================================================================
--- java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java	(original)
+++ java/branches/jeremy-dev/evio/src/main/java/org/hps/evio/TestRunTriggeredReconToLcio.java	Tue Dec  1 15:55:47 2015
@@ -310,7 +310,7 @@
                 }
             }
             if (ecalScoringPlaneHits != null) {
-                lcsimEvent.put(ecalScoringPlaneHitsCollectionName, ecalScoringPlaneHits, SimTrackerHit.class, 0);
+                lcsimEvent.put(ecalScoringPlaneHitsCollectionName, ecalScoringPlaneHits, SimTrackerHit.class, 0xc0000000);
                 if (verbosity >= 1) {
                     System.out.println("Adding " + ecalScoringPlaneHits.size() + " ECalTrackerHits");
                 }
@@ -333,7 +333,7 @@
                 }
             }
             if (triggerECalScoringPlaneHits != null) {
-                lcsimEvent.put(ecalScoringPlaneHitsCollectionName, triggerECalScoringPlaneHits, SimTrackerHit.class, 0);
+                lcsimEvent.put(ecalScoringPlaneHitsCollectionName, triggerECalScoringPlaneHits, SimTrackerHit.class, 0xc0000000);
                 if (verbosity >= 1) {
                     System.out.println("Adding " + triggerECalScoringPlaneHits.size() + " ECalTrackerHits");
                 }

Modified: java/branches/jeremy-dev/job/pom.xml
 =============================================================================
--- java/branches/jeremy-dev/job/pom.xml	(original)
+++ java/branches/jeremy-dev/job/pom.xml	Tue Dec  1 15:55:47 2015
@@ -19,5 +19,9 @@
             <groupId>org.hps</groupId>
             <artifactId>hps-detector-model</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.hps</groupId>
+            <artifactId>hps-run-database</artifactId>
+        </dependency>        
     </dependencies>
 </project>

Modified: java/branches/jeremy-dev/job/src/main/java/org/hps/job/JobManager.java
 =============================================================================
--- java/branches/jeremy-dev/job/src/main/java/org/hps/job/JobManager.java	(original)
+++ java/branches/jeremy-dev/job/src/main/java/org/hps/job/JobManager.java	Tue Dec  1 15:55:47 2015
@@ -1,10 +1,10 @@
 package org.hps.job;
-
-import java.io.InputStream;
 
 import org.hps.conditions.ConditionsDriver;
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.detector.svt.SvtDetectorSetup;
+import org.hps.run.database.RunManager;
+import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
 import org.lcsim.job.JobControlManager;
 import org.lcsim.util.Driver;
 
@@ -32,21 +32,43 @@
      */
     public JobManager() {
     }
-
+   
     /**
-     * Override setup so the conditions system can be reset.
+     * Initialize the conditions system for the job.
+     * <p>
+     * If detector and run are provided from the command line or conditions driver then the
+     * conditions system will be initialized and frozen.
      * 
-     * @param is the input stream containing config information
+     * @throws ConditionsNotFoundException if a condition is not found during initialization
      */
-    public void setup(InputStream is) {
+    protected void initializeConditions() throws ConditionsNotFoundException {
         
-        // Add class that will setup SVT detector with conditions data (this is awkward but has to be done someplace).
-        DatabaseConditionsManager.getInstance().addConditionsListener(new SvtDetectorSetup());
+        // Initialize the db conditions manager.
+        DatabaseConditionsManager dbManager = DatabaseConditionsManager.getInstance();
         
-        super.setup(is);
+        // Initialize run manager and add as listener on conditions system.
+        RunManager runManager = RunManager.getRunManager();
+        dbManager.addConditionsListener(runManager);
+        
+        // Add class that will setup SVT detector with conditions data.
+        dbManager.addConditionsListener(new SvtDetectorSetup());
                 
-        // Setup the conditions system if there is a ConditionsDriver present.
-        this.setupConditionsDriver();
+        // Call super method which will initialize conditions system if the detector and run were provided.
+        super.initializeConditions();
+        
+        // Setup from conditions driver (to be deleted soon).
+        if (!dbManager.isInitialized()) {
+            setupConditionsDriver();
+        } else {
+            // Command line options overrode the conditions driver.
+            LOGGER.config("conditions driver was overridden by command line options");
+        }
+        
+        if (dbManager.isInitialized()) {
+            // Assume conditions system should be frozen since detector and run were provided explicitly.
+            LOGGER.config("job manager freezing conditions system");
+            dbManager.freeze();
+        }
     }
     
     /**
@@ -70,7 +92,9 @@
      * This method will find the {@link org.hps.conditions.ConditionsDriver} in the list of Drivers registered with the
      * manager and then execute its initialization method, which may override the default behavior of the conditions
      * system.
+     * @deprecated Use command line options of {@link org.lcsim.job.JobControlManager} instead.
      */
+    @Deprecated
     private void setupConditionsDriver() {
         ConditionsDriver conditionsDriver = null;
         for (final Driver driver : this.getDriverAdapter().getDriver().drivers()) {
@@ -80,7 +104,7 @@
             }
         }
         if (conditionsDriver != null) {
-            LOGGER.config("initializing conditions Driver");            
+            LOGGER.config("initializing conditions Driver");
             conditionsDriver.initialize();
             LOGGER.warning("Conditions driver will be removed soon!");
         }

Modified: java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java
 =============================================================================
--- java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java	(original)
+++ java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SensorOccupancyPlotsDriver.java	Tue Dec  1 15:55:47 2015
@@ -13,8 +13,8 @@
 import hep.aida.IPlotterRegion;
 import hep.aida.IPlotterStyle;
 import hep.aida.ITree;
-import hep.aida.jfree.plotter.Plotter;
-import hep.aida.jfree.plotter.PlotterRegion;
+import hep.aida.ref.plotter.Plotter;
+import hep.aida.ref.plotter.PlotterRegion;
 import hep.aida.ref.rootwriter.RootFileStore;
 import hep.physics.vec.Hep3Vector;
 
@@ -33,6 +33,8 @@
 import org.lcsim.event.GenericObject;
 import org.lcsim.event.RawTrackerHit;
 import org.lcsim.geometry.Detector;
+import org.lcsim.recon.tracking.digitization.sisim.SiTrackerHitStrip1D;
+import org.lcsim.recon.tracking.digitization.sisim.TrackerHitType;
 import org.lcsim.util.Driver;
 import org.hps.record.triggerbank.AbstractIntData;
 import org.hps.record.triggerbank.TIData;
@@ -46,9 +48,9 @@
 public class SensorOccupancyPlotsDriver extends Driver {
 
     // TODO: Add documentation
-    static {
-        hep.aida.jfree.AnalysisFactory.register();
-    }
+    //static {
+    //    hep.aida.jfree.AnalysisFactory.register();
+    //}
 
     // Plotting
     private static ITree tree = null;
@@ -60,6 +62,8 @@
     private static Map<String, IPlotter> plotters = new HashMap<String, IPlotter>();
     private static Map<String, IHistogram1D> occupancyPlots = new HashMap<String, IHistogram1D>();
     private static Map<String, IHistogram1D> positionPlots = new HashMap<String, IHistogram1D>();
+    private static Map<String, IHistogram1D> clusterPositionPlots = new HashMap<String, IHistogram1D>();
+    private static Map<String, IHistogram1D> clusterPositionPlotCounts = new HashMap<String, IHistogram1D>();
     private static Map<String, int[]> occupancyMap = new HashMap<String, int[]>();
     private static Map<String, IHistogram1D> maxSamplePositionPlots = new HashMap<String, IHistogram1D>();
 
@@ -69,6 +73,7 @@
     private static final String SUBDETECTOR_NAME = "Tracker";
     private String rawTrackerHitCollectionName = "SVTRawTrackerHits";
     private String triggerBankCollectionName = "TriggerBank";
+    private String stripClusterCollectionName = "StripClusterer_SiTrackerHitStrip1D";
 
     String rootFile = null;
 
@@ -100,6 +105,10 @@
 
     private boolean dropSmallHitEvents = true;
 
+    private boolean enableClusterTimeCuts = true;
+    private double clusterTimeCutMax = 4.0;
+    private double clusterTimeCutMin = -4.0;
+    
     public SensorOccupancyPlotsDriver() {
         maxSampleStatus = new SystemStatusImpl(Subsystem.SVT, "Checks that SVT is timed in (max sample plot)", true);
         maxSampleStatus.setStatus(StatusCode.UNKNOWN, "Status is unknown.");
@@ -305,6 +314,8 @@
 
             if (enablePositionPlots) {
                 positionPlots.get(sensor.getName()).reset();
+                clusterPositionPlots.get(sensor.getName()).reset();
+                clusterPositionPlotCounts.get(sensor.getName()).reset();
             }
 
             if (enableMaxSamplePlots) {
@@ -341,7 +352,9 @@
 //            this.resetPlots();
 //            return; 
 //        }
-        tree = analysisFactory.createTreeFactory().create();
+        //tree = analysisFactory.createTreeFactory().create();
+        tree = AIDA.defaultInstance().tree();
+        tree.cd("/");//        aida.tree().cd("/");
         histogramFactory = analysisFactory.createHistogramFactory(tree);
 
         // Create the plotter and regions.  A region is created for each
@@ -354,6 +367,8 @@
         if (enablePositionPlots) {
             plotters.put("Occupancy vs Position", plotterFactory.create("Occupancy vs Position"));
             plotters.get("Occupancy vs Position").createRegions(6, 6);
+            plotters.put("Cluster occupancy vs Position", plotterFactory.create("Cluster occupancy vs Position"));
+            plotters.get("Cluster occupancy vs Position").createRegions(6, 6);
         }
 
         if (enableMaxSamplePlots) {
@@ -373,13 +388,23 @@
                 if (sensor.isTopLayer()) {
                     positionPlots.put(sensor.getName(),
                             histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy vs Position", 1000, 0, 60));
+                    clusterPositionPlots.put(sensor.getName(),
+                            histogramFactory.createHistogram1D(sensor.getName() + " - Cluster occupancy vs Position", 1000, 0, 60));
+                    clusterPositionPlotCounts.put(sensor.getName(),
+                            histogramFactory.createHistogram1D(sensor.getName() + " - Cluster count vs Position", 1000, 0, 60));
                 } else {
                     positionPlots.put(sensor.getName(),
                             histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy vs Position", 1000, -60, 0));
+                    clusterPositionPlots.put(sensor.getName(),
+                            histogramFactory.createHistogram1D(sensor.getName() + " - Cluster occupancy vs Position", 1000, -60, 0));
+                    clusterPositionPlotCounts.put(sensor.getName(),
+                            histogramFactory.createHistogram1D(sensor.getName() + " - Cluster count vs Position", 1000, -60, 0));
                 }
 
                 plotters.get("Occupancy vs Position").region(SvtPlotUtils.computePlotterRegion(sensor))
                         .plot(positionPlots.get(sensor.getName()), this.createOccupancyPlotStyle("Distance from Beam [mm]", sensor, false));
+                plotters.get("Cluster occupancy vs Position").region(SvtPlotUtils.computePlotterRegion(sensor))
+                .plot(clusterPositionPlots.get(sensor.getName()), this.createOccupancyPlotStyle("Distance from Beam [mm]", sensor, false));
             }
             occupancyMap.put(sensor.getName(), new int[640]);
 
@@ -393,11 +418,12 @@
 
         for (IPlotter plotter : plotters.values()) {
             for (int regionN = 0; regionN < plotter.numberOfRegions(); regionN++) {
-                PlotterRegion region = ((PlotterRegion) ((Plotter) plotter).region(regionN));
-                if (region.getPlottedObjects().isEmpty()) {
-                    continue;
-                }
-                region.getPanel().addMouseListener(new PopupPlotterListener(region));
+                //Plotter l;
+                //PlotterRegion region = ((PlotterRegion) ((Plotter) plotter).region(regionN));
+                //if (region..getPlottedObjects().isEmpty()) {
+                //    continue;
+                //}
+                //region.getPanel().addMouseListener(new PopupPlotterListener(region));
             }
             plotter.show();
         }
@@ -494,6 +520,22 @@
                 maxSamplePositionPlots.get(((HpsSiSensor) rawHit.getDetectorElement()).getName()).fill(maxSamplePositionFound);
             }
         }
+        
+        // Fill the strip cluster counts if available
+        if(event.hasCollection(SiTrackerHitStrip1D.class, stripClusterCollectionName)) {
+            List<SiTrackerHitStrip1D> stripHits1D = event.get(SiTrackerHitStrip1D.class, stripClusterCollectionName);
+            for(SiTrackerHitStrip1D h : stripHits1D) {
+                SiTrackerHitStrip1D global = h.getTransformedHit(TrackerHitType.CoordinateSystem.GLOBAL);
+                Hep3Vector pos_global = global.getPositionAsVector();
+                if(enableClusterTimeCuts) {
+                    if( h.getTime() < clusterTimeCutMax && h.getTime() > clusterTimeCutMin) 
+                        clusterPositionPlotCounts.get(((HpsSiSensor) h.getRawHits().get(0).getDetectorElement()).getName()).fill(pos_global.y());
+                } else
+                    clusterPositionPlotCounts.get(((HpsSiSensor) h.getRawHits().get(0).getDetectorElement()).getName()).fill(pos_global.y());
+            }
+        }
+        
+        
 
         if (enableMaxSamplePlots && eventCount > maxSampleMonitorStart && eventCount % maxSampleMonitorPeriod == 0) {
             checkMaxSample();
@@ -521,6 +563,17 @@
                         positionPlots.get(sensor.getName()).fill(stripPosition, stripOccupancy);
                     }
                 }
+                if(enablePositionPlots) {
+                    clusterPositionPlots.get(sensor.getName()).reset();
+                    IHistogram1D h = clusterPositionPlotCounts.get(sensor.getName());
+                    for(int bin=0; bin<h.axis().bins(); ++bin) {
+                        int y = h.binEntries(bin);
+                        double stripClusterOccupancy = (double) y / (double) eventCount;
+                        double x = h.axis().binCenter(bin);
+                        clusterPositionPlots.get(sensor.getName()).fill(x,stripClusterOccupancy);
+                    }
+                }
+                
             }
         }
 

Modified: java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtPlotUtils.java
 =============================================================================
--- java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtPlotUtils.java	(original)
+++ java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/drivers/svt/SvtPlotUtils.java	Tue Dec  1 15:55:47 2015
@@ -1,14 +1,30 @@
 package org.hps.monitoring.drivers.svt;
 
+import hep.aida.IBaseHistogram;
+import hep.aida.IFitFactory;
+import hep.aida.IFitResult;
+import hep.aida.IFitter;
+import hep.aida.IFunction;
+import hep.aida.IFunctionFactory;
+import hep.aida.IHistogram1D;
+import hep.aida.IPlotter;
 import hep.aida.IPlotterFactory;
 import hep.aida.IPlotterStyle;
+import hep.aida.ref.plotter.style.registry.IStyleStore;
+import hep.aida.ref.plotter.style.registry.StyleRegistry;
+
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.event.RawTrackerHit;
+import org.lcsim.geometry.compact.converter.HPSTrackerBuilder;
+import org.lcsim.util.aida.AIDA;
 
 /**
  *
@@ -16,6 +32,9 @@
  */
 public class SvtPlotUtils {
 
+    private static final Logger logger = Logger.getLogger(SvtPlotUtils.class.getSimpleName());
+    static private AIDA aida = AIDA.defaultInstance();
+    
     public static int computePlotterRegion(HpsSiSensor sensor) {
 
         if (sensor.getLayerNumber() < 7) {
@@ -44,6 +63,35 @@
         return -1;
     }
 
+    public static int computePlotterRegionAxialOnly(HpsSiSensor sensor) {
+        int l =  HPSTrackerBuilder.getLayerFromVolumeName(sensor.getName());
+        if(!sensor.isAxial()) throw new RuntimeException("not axial.");
+        if( l < 4 ) {
+            if (sensor.isTopLayer()) {
+                return 6 * (l - 1);
+            } else {
+                return 6 * (l - 1) + 1;
+            }
+        } else {
+            if (sensor.isTopLayer()) {
+                if (sensor.getSide() == HpsSiSensor.POSITRON_SIDE) {
+                    return 6 * (l - 4) + 2;
+                } else {
+                    return 6 * (l - 4) + 3;
+                }
+            } else if (sensor.isBottomLayer()) {
+                if (sensor.getSide() == HpsSiSensor.POSITRON_SIDE) {
+                    return 6 * (l - 4) + 4;
+                } else {
+                    return 6 * (l - 4) + 5;
+                }
+            }
+        }
+
+        return -1;
+    }
+
+    
     /**
      * Create a plotter style.
      *
@@ -140,4 +188,65 @@
         }
         return true;
     }
+    
+    public static IFitResult performGaussianFit(IHistogram1D histogram) {
+        IFunctionFactory functionFactory = aida.analysisFactory().createFunctionFactory(null);
+        IFitFactory fitFactory = aida.analysisFactory().createFitFactory();
+        IFunction function = functionFactory.createFunctionByName("Example Fit", "G");
+        IFitter fitter = fitFactory.createFitter("chi2", "jminuit");
+        double[] parameters = new double[3];
+        parameters[0] = histogram.maxBinHeight();
+        parameters[1] = histogram.mean();
+        parameters[2] = histogram.rms();
+        function.setParameters(parameters);
+        IFitResult fitResult = null;
+         Logger minuitLogger = Logger.getLogger("org.freehep.math.minuit");
+        minuitLogger.setLevel(Level.OFF);
+        minuitLogger.info("minuit logger test");
+        
+        try {
+            fitResult = fitter.fit(histogram, function);
+        } catch (RuntimeException e) {
+           logger.warning("fit failed");
+        }
+        return fitResult;
+    }
+    
+    /*
+     *  puts a function on a plotter region with a  style
+     *  copied from org.hps.monitoring.drivers.ecal.EcalMonitoringUtilities.java
+     */
+
+    public static void plot(IPlotter plotter, IFunction function, IPlotterStyle style, int region) {
+        if (style == null)
+            style = getPlotterStyle(function);
+        logger.info("Putting function in region " + region);
+        if(style != null)
+            plotter.region(region).plot(function, style);
+        else
+            plotter.region(region).plot(function);
+    }
+
+    
+    /*
+     *  gets default plotter style for a function type
+     *  copied from org.hps.monitoring.drivers.ecal.EcalMonitoringUtilities.java
+     */
+    public static IPlotterStyle getPlotterStyle(IFunction func) {
+        StyleRegistry styleRegistry = StyleRegistry.getStyleRegistry();
+        IStyleStore store = styleRegistry.getStore("DefaultStyleStore");
+        if(store == null) {
+            int n = styleRegistry.getAvailableStoreNames().length;
+            if(n==0) return null;
+            else store = styleRegistry.getStore(styleRegistry.getAvailableStoreNames()[0]);
+        }
+        IPlotterStyle style = null;
+        style = store.getStyle("DefaultFunctionStyle");
+        if (style == null) {
+            int n = store.getAllStyleNames().length;
+            if(n==0) return null;
+            else style = store.getStyle(store.getAllStyleNames()[0]);
+        }
+        return style;
+    }
 }

Modified: java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalLedSequenceMonitor.java
 =============================================================================
--- java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalLedSequenceMonitor.java	(original)
+++ java/branches/jeremy-dev/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalLedSequenceMonitor.java	Tue Dec  1 15:55:47 2015
@@ -63,7 +63,7 @@
     private static final String dbTableName = "ecal_led_calibrations";
     private static final int runNumberMax = 9999;
     private static final int nDrivers = 8;
-    private static final int nSteps = 56;
+    private static final int nSteps = 100; //should be 56 but here is to avoid seg fault
     
    
 
@@ -746,6 +746,8 @@
                 led_calibrations.getCollectionId(), runNumber, runNumberMax, dbTableName, dbTableName, 
                 "Generated by LedAnalysis from Run #"+runNumber, dbTag);
         conditionsRecord.setConnection(conditionsManager.getConnection());
+        tableMetaData = conditionsManager.findTableMetaData("conditions");
+        conditionsRecord.setTableMetaData(tableMetaData);
         conditionsRecord.insert();
 
         System.out.println("Upload to DB done");

Modified: java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/EventFlagFilter.java
 =============================================================================
--- java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/EventFlagFilter.java	(original)
+++ java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/EventFlagFilter.java	Tue Dec  1 15:55:47 2015
@@ -11,7 +11,7 @@
  */
 public class EventFlagFilter extends EventReconFilter {
 
-    String[] flagNames = {"svt_bias_good", "svt_position_good", "svt_burstmode_noise_good", "svt_event_header_good"};
+    String[] flagNames = {"svt_bias_good", "svt_position_good", "svt_burstmode_noise_good", "svt_event_header_good", "svt_latency_good"};
 
     public void setFlagNames(String[] flagNames) {
         this.flagNames = flagNames;

Modified: java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/PulserTriggerFilterDriver.java
 =============================================================================
--- java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/PulserTriggerFilterDriver.java	(original)
+++ java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/PulserTriggerFilterDriver.java	Tue Dec  1 15:55:47 2015
@@ -7,7 +7,14 @@
 import org.hps.record.scalers.ScalerData;
 import org.hps.record.triggerbank.AbstractIntData;
 import org.hps.record.triggerbank.TIData;
-
+/**
+ * Keep pulser triggered events.
+ * Also keep EPICS events, and Scaler events.
+ * Drop all other events.
+ * 
+ * @author baltzell
+ *
+ */
 public class PulserTriggerFilterDriver extends Driver
 {
   public void process(EventHeader event) {

Modified: java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/V0CandidateFilter.java
 =============================================================================
--- java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/V0CandidateFilter.java	(original)
+++ java/branches/jeremy-dev/recon/src/main/java/org/hps/recon/filtering/V0CandidateFilter.java	Tue Dec  1 15:55:47 2015
@@ -2,15 +2,17 @@
 
 import static java.lang.Math.abs;
 import java.util.List;
+import org.hps.recon.ecal.cluster.ClusterUtilities;
+import org.hps.recon.particle.ReconParticleDriver;
 import org.hps.record.epics.EpicsData;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.ReconstructedParticle;
 
 /**
- * Class to strip off trident candidates. Currently defined as: e+ e- events with
- * tracks matched to clusters. Neither electron can be a full-energy candidate
- * (momentum less than _fullEnergyCut [0.85GeV]). The Ecal cluster times must be 
- * within _timingCut [2.5ns] of each other.
+ * Class to strip off trident candidates. Currently defined as: e+ e- events
+ * with tracks. If the tight constraint is enabled, tracks must be matched to
+ * clusters and the Ecal cluster times must be within _timingCut [2.5ns] of each
+ * other.
  *
  * @author Norman A Graf
  *
@@ -23,7 +25,6 @@
 
     private boolean _tight = false;
     private boolean _keepEpicsDataEvents = false;
-
 
     @Override
     protected void process(EventHeader event) {
@@ -40,7 +41,7 @@
             skipEvent();
         }
         List<ReconstructedParticle> V0Candidates = event.get(ReconstructedParticle.class, _V0CandidateCollectionName);
-        if (V0Candidates.size() == 0) {
+        if (V0Candidates.isEmpty()) {
             skipEvent();
         }
 
@@ -49,34 +50,33 @@
             if (V0Candidates.size() != 2) {
                 skipEvent();
             }
-        }
+            for (ReconstructedParticle rp : V0Candidates) {
 
-        for (ReconstructedParticle rp : V0Candidates) {
+                ReconstructedParticle electron;
+                ReconstructedParticle positron;
 
-            ReconstructedParticle e1 = null;
-            ReconstructedParticle e2 = null;
+                List<ReconstructedParticle> fsParticles = rp.getParticles();
+                if (fsParticles.size() != 2) {
+                    skipEvent();
+                }
+                // require both electrons to be associated with an ECal cluster
+                electron = fsParticles.get(ReconParticleDriver.ELECTRON);
+                if (electron.getClusters().isEmpty()) {
+                    skipEvent();
+                }
+                positron = fsParticles.get(ReconParticleDriver.POSITRON);
+                if (positron.getClusters().isEmpty()) {
+                    skipEvent();
+                }
 
-            List<ReconstructedParticle> electrons = rp.getParticles();
-            if (electrons.size() != 2) {
-                skipEvent();
-            }
-            // require both electrons to be associated with an ECal cluster
-            e1 = electrons.get(0);
-            if (e1.getClusters().size() == 0) {
-                skipEvent();
-            }
-            e2 = electrons.get(1);
-            if (e2.getClusters().size() == 0) {
-                skipEvent();
-            }
+                // calorimeter cluster timing cut
+                // first CalorimeterHit in the list is the seed crystal
+                double t1 = ClusterUtilities.getSeedHitTime(electron.getClusters().get(0));
+                double t2 = ClusterUtilities.getSeedHitTime(positron.getClusters().get(0));
 
-            // calorimeter cluster timing cut
-            // first CalorimeterHit in the list is the seed crystal
-            double t1 = e1.getClusters().get(0).getCalorimeterHits().get(0).getTime();
-            double t2 = e2.getClusters().get(0).getCalorimeterHits().get(0).getTime();
-
-            if (abs(t1 - t2) > _clusterTimingCut) {
-                skipEvent();
+                if (abs(t1 - t2) > _clusterTimingCut) {
+                    skipEvent();
+                }
             }
         }
         incrementEventPassed();
@@ -109,14 +109,13 @@
     public void setTightConstraint(boolean b) {
         _tight = b;
     }
-    
+
     /**
      * Setting this true keeps ALL events containing EPICS data
      *
      * @param b
      */
-    public void setKeepEpicsDataEvents(boolean b)
-    {
+    public void setKeepEpicsDataEvents(boolean b) {
         _keepEpicsDataEvents = b;
     }
 }

Added: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractLoopAdapter.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractLoopAdapter.java	(added)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractLoopAdapter.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,52 @@
+package org.hps.record;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.freehep.record.loop.AbstractLoopListener;
+import org.freehep.record.loop.LoopEvent;
+import org.freehep.record.loop.LoopListener;
+import org.freehep.record.loop.RecordEvent;
+import org.freehep.record.loop.RecordListener;
+
+public abstract class AbstractLoopAdapter<RecordType> extends AbstractLoopListener implements RecordListener, LoopListener {
+
+    private Logger LOGGER = Logger.getLogger(AbstractLoopAdapter.class.getPackage().getName());
+    
+    private List<AbstractRecordProcessor<RecordType>> processors = new ArrayList<AbstractRecordProcessor<RecordType>>();
+           
+    @Override
+    public void recordSupplied(RecordEvent recordEvent) {
+        //LOGGER.info("recordSupplied " + recordEvent.toString());
+        final RecordType record = (RecordType) recordEvent.getRecord();
+        for (final AbstractRecordProcessor<RecordType> processor : processors) {
+            try {
+                if (processor.isActive()) {
+                    //LOGGER.info("activating processor " + processor.getClass().getName());
+                    processor.process(record);
+                }
+            } catch (final Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
+    @Override
+    protected void finish(final LoopEvent event) {
+        for (final AbstractRecordProcessor<RecordType> processor : processors) {
+            processor.endJob();
+        }
+    }
+    
+    @Override
+    protected void start(final LoopEvent event) {
+        for (final AbstractRecordProcessor<RecordType> processor : processors) {
+            processor.startJob();
+        }
+    }
+    
+    void addProcessor(AbstractRecordProcessor<RecordType> processor) {
+        this.processors.add(processor);
+    }    
+}

Added: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordLoop.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordLoop.java	(added)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordLoop.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,36 @@
+package org.hps.record;
+
+import java.util.Collection;
+
+import org.freehep.record.loop.DefaultRecordLoop;
+
+public abstract class AbstractRecordLoop<RecordType> extends DefaultRecordLoop {
+    
+    protected AbstractLoopAdapter<RecordType> adapter;
+    
+    public void addProcessors(Collection<AbstractRecordProcessor<RecordType>> processors) {
+        for (AbstractRecordProcessor<RecordType> processor : processors) {
+            adapter.addProcessor(processor);
+        }
+    }
+    
+    public void addProcessor(AbstractRecordProcessor<RecordType> processor) {
+        adapter.addProcessor(processor);
+    }
+    
+    /**
+     * Loop over events from the source.
+     *
+     * @param number the number of events to process or -1L for all events from the source
+     * @return the number of records that were processed
+     */
+    public long loop(final long number) {
+        if (number < 0L) {
+            this.execute(Command.GO, true);
+        } else {
+            this.execute(Command.GO_N, number, true);
+            this.execute(Command.STOP);
+        }
+        return this.getSupplied();
+    }
+}

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/AbstractRecordProcessor.java	Tue Dec  1 15:55:47 2015
@@ -10,6 +10,8 @@
  */
 public abstract class AbstractRecordProcessor<RecordType> implements RecordProcessor<RecordType> {
 
+    private boolean active = true;
+    
     /**
      * End of job action.
      */
@@ -59,4 +61,12 @@
     @Override
     public void suspend() {
     }
+    
+    protected void setActive(boolean active) {
+        this.active = active;
+    }
+    
+    public boolean isActive() {
+        return this.active;
+    }
 }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/RecordProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/RecordProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/RecordProcessor.java	Tue Dec  1 15:55:47 2015
@@ -45,4 +45,11 @@
      * Suspend processing action.
      */
     void suspend();
+    
+    /**
+     * Return <code>true</code> if processor is active.
+     * 
+     * @return <code>true</code> if processor is active
+     */
+    boolean isActive();
 }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigDriver.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigDriver.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigDriver.java	Tue Dec  1 15:55:47 2015
@@ -1,16 +1,36 @@
 package org.hps.record.daqconfig;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.lcsim.event.EventHeader;
 import org.lcsim.util.Driver;
 
 /**
- * Class <code>DAQConfigDriver</code> is responsible for checking events
- * for DAQ configuration settings, and then passing them to the associated
+ * Class <code>DAQConfigDriver</code> is responsible for accessing the
+ * DAQ configuration settings, and then passing them to the associated
  * class <code>ConfigurationManager</code> so that they can be accessed
  * by other classes.<br/>
  * <br/>
+ * The driver may accomplish this by two means. By default, it will
+ * check each event for an <code>EvioDAQParser</code> object which
+ * contains all of the DAQ configuration information and pass this to
+ * the <code>ConfigurationManager</code>. It will continue to update
+ * the <code>ConfigurationManager</code> during the run if new parser
+ * objects appear, and can thusly account for changing DAQ conditions.
+ * <br/><br/>
+ * The driver may also be set to read a DAQ configuration from text
+ * files containing the DAQ configuration bank information. To enable
+ * this mode, the parameter <code>readDataFiles</code> must be set to
+ * <code>true</code> and the parameters <code>runNumber</code> and also
+ * <code>filepath</code> must be defined. <code>runNumber</code> defines
+ * the run number of the configuration to be loaded and the parameter
+ * <code>filepath</code> defines the location of the data file repository.
+ * <br/><br/>
  * This driver must be included in the driver chain if any other drivers
  * in the chain rely on <code>ConfigurationManager</code>, as it can
  * not be initialized otherwise.
@@ -19,15 +39,88 @@
  * @see ConfigurationManager
  */
 public class DAQConfigDriver extends Driver {
+	private int runNumber = -1;
+	private String filepath = null;
+	private boolean firstEvent = true;
+	private boolean readDataFiles = false;
+	private File[] dataFiles = new File[3];
+	private int[] crateNumber = { 46, 37, 39 };
+	
+	/**
+	 * Verifies the parameter <code>filepath</code> for the data file
+	 * repository and checks that appropriate data files exist for the
+	 * requested run number if the driver is set to read from data files.
+	 * Otherwise, this does nothing.
+	 */
+	@Override
+	public void startOfData() {
+		// Check whether to use stored data files or the EvIO data stream
+		// as the source of the DAQ settings. Nothing needs to be done
+		// in the latter case.
+		if(readDataFiles) {
+			// The user must define a data file prefix and repository
+			// location for this option to be used.
+			if(filepath == null) {
+				throw new NullPointerException("DAQ settings repository filepath must be defined.");
+			} if(runNumber == -1) {
+				throw new NullPointerException("Run number must be defined.");
+			}
+			
+			// Verify that the repository actually exist.
+			File repository = new File(filepath);
+			if(!repository.exists() || !repository.isDirectory()) {
+				throw new IllegalArgumentException("Repository location \"" + filepath + "\" must be an existing directory.");
+			}
+			
+			// Define the data file objects.
+			for(int i = 0; i < dataFiles.length; i++) {
+				try {
+					dataFiles[i] = new File(repository.getCanonicalPath() + "/" + runNumber + "_" + crateNumber[i] + ".txt");
+				} catch(IOException e) {
+					throw new RuntimeException("Error resolving absolute repository filepath.");
+				}
+			}
+			
+			// Verify that the data files actually exist.
+			for(File dataFile : dataFiles) {
+				if(!dataFile.exists() || !dataFile.canRead()) {
+					throw new IllegalArgumentException("Data file \"" + dataFile.getName() + "\" does not exist or can not be read.");
+				}
+			}
+		}
+	}
+	
     /**
      * Checks an event for the DAQ configuration banks and passes them
-     * to the <code>ConfigurationManager</code>.
-     * @param - The event to check.
+     * to the <code>ConfigurationManager</code> if the driver is set to
+     * read from the EvIO data stream. Otherwise, this will parse the
+     * data files on the first event and then do nothing.
+     * @param event - The current LCIO event.
      */
     @Override
     public void process(EventHeader event) {
+    	// If this is the first event and data files are to be read,
+    	// import the data files and generate the DAQ information.
+    	if(firstEvent && readDataFiles) {
+    		// Get the data files in the form of a data array.
+    		String[][] data;
+    		try { data = getDataFileArrays(dataFiles); }
+    		catch(IOException e) {
+				throw new RuntimeException("An error occurred when processing the data files.");
+			}
+    		
+    		// Instantiate an EvIO DAQ parser and feed it the data.
+    		EvioDAQParser daqConfig = new EvioDAQParser();
+    		for(int i = 0; i < dataFiles.length; i++) {
+        		daqConfig.parse(crateNumber[i], runNumber, data[i]);
+    		}
+    		
+    		// Update the configuration manager.
+    		ConfigurationManager.updateConfiguration(daqConfig);
+    	}
+    	
         // Check if a trigger configuration bank exists.
-        if(event.hasCollection(EvioDAQParser.class, "TriggerConfig")) {
+    	if(!readDataFiles && event.hasCollection(EvioDAQParser.class, "TriggerConfig")) {
             // Get the trigger configuration bank. There should only be
             // one in the list.
             List<EvioDAQParser> configList = event.get(EvioDAQParser.class, "TriggerConfig");
@@ -37,5 +130,87 @@
             // configuration object.
             ConfigurationManager.updateConfiguration(daqConfig);
         }
+        
+        // Note that it is no longer the first event.
+        firstEvent = false;
+    }
+	
+    /**
+     * Converts DAQ configuration data files into an array of strings
+     * where each array entry represents a line in the configuration
+     * file. The first array index of the returned object corresponds
+     * to the file, and the second array index corresponds to the line.
+     * @param dataFiles - An array of <code>File</code> objects pointing
+     * to the data files that are to be converted. These are expected
+     * to be plain text files.
+     * @return Returns a two-dimensional array of <code>String</code>
+     * objects where the first array index corresponds to the object
+     * of the same index in the <code>File</code> array and the second
+     * array index corresponds to the lines in the file referenced by
+     * the <code>File</code> object.
+     * @throws IOException Occurs if there is an issue with accessing
+     * or reading the objects in the objects referred to by the files
+     * pointed to in the <code>dataFiles</code> array.
+     */
+	private static final String[][] getDataFileArrays(File[] dataFiles) throws IOException {
+		// Create file readers to process the data files.
+		FileReader[] fr = new FileReader[dataFiles.length];
+		BufferedReader[] reader = new BufferedReader[dataFiles.length];
+		for(int i = 0; i < dataFiles.length; i++) {
+			fr[i] = new FileReader(dataFiles[i]);
+			reader[i] = new BufferedReader(fr[i]);
+		}
+		
+		// Generate String arrays where each entry in the array is
+		// a line from the data file.
+		String[][] data = new String[dataFiles.length][0];
+		for(int i = 0; i < dataFiles.length; i++) {
+			// Create a list to hold the raw strings.
+			List<String> rawData = new ArrayList<String>();
+			
+			// Add each line from the current data file to the list
+			// as a single entry.
+			String curLine = null;
+			while((curLine = reader[i].readLine()) != null) {
+				rawData.add(curLine);
+			}
+			
+			// Convert the list into a String array.
+			data[i] = rawData.toArray(new String[rawData.size()]);
+		}
+		
+		// Return the data array.
+		return data;
+	}
+    
+	/**
+	 * Sets the run number of the DAQ configuration being processed.
+	 * This is only used when reading from data files.
+	 * @param run - The run number of the data files to be used.
+	 */
+    public void setRunNumber(int run) {
+    	runNumber = run;
+    }
+    
+    /**
+     * Sets the location of the DAQ configuration data files. This is
+     * only used when reading from the data files.
+     * @param filepath - The file path of the data file repository.
+     */
+    public void setDataFileRepository(String filepath) {
+    	this.filepath = filepath;
+    }
+    
+    /**
+     * Sets whether or not to read the DAQ configuration directly from
+     * the EvIO data stream or whether to read the configuration from
+     * data files. Parameters <code>runNumber</code> and <code>filepath</code>
+     * must also be defined if this is set to <code>true</code>.
+     * @param state - <code>true</code> indicates that the configuration
+     * should be read from data files, and <code>false</code> that it
+     * should be read from the EvIO stream.
+     */
+    public void setReadDataFiles(boolean state) {
+    	readDataFiles = state;
     }
 }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigEvioProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigEvioProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/DAQConfigEvioProcessor.java	Tue Dec  1 15:55:47 2015
@@ -1,65 +1,129 @@
 package org.hps.record.daqconfig;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
-import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.record.evio.EvioBankTag;
 import org.hps.record.evio.EvioEventProcessor;
 import org.hps.record.evio.EvioEventUtilities;
 import org.jlab.coda.jevio.BaseStructure;
 import org.jlab.coda.jevio.EvioEvent;
-import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
 
 /**
- * Modified from code in {@link org.hps.evio.TriggerConfigEvioReader} to extract trigger
- * config without an output LCSim event.
+ * Copied and modified from code in {@link org.hps.evio.TriggerConfigEvioReader} to extract DAQ config without
+ * needing an output LCSim event.
+ * <p>
+ * Only the last valid DAQ config object will be saved.
  * 
  * @author Jeremy McCormick, SLAC
  */
 public class DAQConfigEvioProcessor extends EvioEventProcessor {
-           
-    private List<EvioDAQParser> triggerConfig = new ArrayList<EvioDAQParser>();
 
+    private Logger LOGGER = Logger.getLogger(DAQConfigEvioProcessor.class.getPackage().getName());
+        
+    private DAQConfig daqConfig = null;
+    
+    private Map<Integer, String> stringData = new HashMap<Integer, String>();
+    
+    private Integer run = null;
+
+    /**
+     * Process EVIO events to extract DAQ config data.
+     */
     @Override
-    public void process(EvioEvent evioEvent) {        
+    public void process(EvioEvent evioEvent) {       
+        try {            
+            // Initialize the run number if necessary.
+            if (run == null) {
+                try {
+                    run = EvioEventUtilities.getRunNumber(evioEvent);
+                    LOGGER.info("run " + run);
+                } catch (NullPointerException e) {
+                }
+            }
+
+            // Can only start parsing DAQ banks once the run is set.
+            if (run != null) {
+                
+                // Parse config data from the EVIO banks.
+                EvioDAQParser evioParser = parseEvioData(evioEvent);
+            
+                // Was there a valid config created from the EVIO event?
+                if (evioParser != null) {            
+                    // Set the current DAQ config object.
+                    ConfigurationManager.updateConfiguration(evioParser);
+                    daqConfig = ConfigurationManager.getInstance();
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Error parsing DAQ config from EVIO.", e);
+        }
+    }
+    
+    /**
+     * Parse DAQ config from an EVIO event.
+     * 
+     * @param evioEvent the EVIO event
+     * @return a parser object if the event has valid config data; otherwise <code>null</code>
+     */
+    private EvioDAQParser parseEvioData(EvioEvent evioEvent) {
+        EvioDAQParser parser = null;
+        int configBanks = 0;
         for (BaseStructure bank : evioEvent.getChildrenList()) {
-            if (bank.getChildCount() <= 0)
+            if (bank.getChildCount() <= 0) {
                 continue;
+            }
             int crate = bank.getHeader().getTag();
             for (BaseStructure subBank : bank.getChildrenList()) {
                 if (EvioBankTag.TRIGGER_CONFIG.equals(subBank)) {
-                    if (subBank.getStringData() == null) {                        
-                        throw new RuntimeException("Trigger config bank is missing string data.");
+                    if (subBank.getStringData() == null) {
+                        LOGGER.warning("Trigger config bank is missing string data.");
+                    } else {
+                        try { 
+                            if (parser == null) {
+                                parser = new EvioDAQParser();
+                                stringData.clear();
+                            }
+                            LOGGER.fine("raw string data" + subBank.getStringData()[0]);
+                            stringData.put(crate, subBank.getStringData()[0]);
+                            LOGGER.info("Parsing DAQ config from crate " + crate + ".");
+                            parser.parse(crate, run, subBank.getStringData());
+                            ++configBanks;
+                        } catch (Exception e) {
+                            LOGGER.log(Level.WARNING, "Failed to parse DAQ config.", e);
+                        }
                     }
-                    createTriggerConfig(evioEvent, crate, subBank);
                 }
             }
         }
+        if (configBanks >= 4 || parser == null) {
+            if (parser != null) {
+                LOGGER.info("DAQ config was created from event " + evioEvent.getEventNumber() + " with " + configBanks + " banks.");
+            }
+            return parser;
+        } else {
+            LOGGER.warning("Not enough banks were found to build DAQ config.");
+            return null;
+        }
+    }
+
+    /**
+     * Get the DAQ config.
+     * 
+     * @return the DAQ config
+     */
+    public DAQConfig getDAQConfig() {
+        return this.daqConfig;
     }
     
-    private void createTriggerConfig(EvioEvent evioEvent, int crate, BaseStructure subBank) {
-        
-        // Get run number from EVIO event.
-        int runNumber = EvioEventUtilities.getRunNumber(evioEvent);
-        
-        // Initialize the conditions system if necessary as the DAQ config parsing classes use it.
-        DatabaseConditionsManager conditionsManager = DatabaseConditionsManager.getInstance();
-        if (!conditionsManager.isInitialized() || conditionsManager.getRun() != runNumber) {
-            try {
-                conditionsManager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml");
-                DatabaseConditionsManager.getInstance().setDetector("HPS-dummy-detector", runNumber);
-            } catch (ConditionsNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        
-        // Create the trigger config from the EVIO data.
-        triggerConfig = new ArrayList<EvioDAQParser>();
-        triggerConfig.add(new EvioDAQParser());
-        triggerConfig.get(0).parse(
-                crate, 
-                runNumber, 
-                subBank.getStringData());
+    /**
+     * Get a map of bank number to its string data for the current config.
+     * 
+     * @return a map of bank to trigger config data
+     */
+    public Map<Integer, String> getTriggerConfigData() {
+        return this.stringData;
     }
 }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java	Tue Dec  1 15:55:47 2015
@@ -39,7 +39,7 @@
     private final EpicsEvioProcessor processor = new EpicsEvioProcessor();
 
     /**
-     * Create an EPICs log.
+     * Create a processor that will make a list of EPICS data.
      */
     public EpicsRunProcessor() {
     }
@@ -66,9 +66,9 @@
 
         // Add EPICS data to the collection.
         if (this.currentEpicsData != null) {
-            LOGGER.info("adding EPICS data for run " + this.currentEpicsData.getEpicsHeader().getRun() + " and timestamp " 
-                    + this.currentEpicsData.getEpicsHeader().getTimestamp() + " with seq " 
-                    + this.currentEpicsData.getEpicsHeader().getSequence());
+            LOGGER.info("Adding EPICS data with run " + this.currentEpicsData.getEpicsHeader().getRun() + "; timestamp " 
+                    + this.currentEpicsData.getEpicsHeader().getTimestamp() + "; seq "
+                    + this.currentEpicsData.getEpicsHeader().getSequence() + ".");
             this.epicsDataSet.add(this.currentEpicsData);
         }
     }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioDetectorConditionsProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioDetectorConditionsProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioDetectorConditionsProcessor.java	Tue Dec  1 15:55:47 2015
@@ -39,21 +39,28 @@
      */
     @Override
     public void process(final EvioEvent evioEvent) throws Exception {
+        
         // Get the head head bank from event.
         final BaseStructure headBank = EvioEventUtilities.getHeadBank(evioEvent);
 
-        // Is the head bank present?
-        if (headBank != null) {
+        // Initialize from head bank.
+        if (headBank != null) {            
+            initializeConditions(headBank.getIntData()[1]);
+        }
+        
+        // Initialize from PRESTART.
+        if (EventTagConstant.PRESTART.matches(evioEvent)) {
+            int runNumber = EvioEventUtilities.getControlEventData(evioEvent)[1];
+            initializeConditions(runNumber);
+        }
+    }
 
-            // Get the run number from the head bank.
-            final int runNumber = headBank.getIntData()[1];
-
-            // Initialize the conditions system from the detector name and run number.
-            try {
-                ConditionsManager.defaultInstance().setDetector(this.detectorName, runNumber);
-            } catch (final ConditionsNotFoundException e) {
-                throw new RuntimeException("Error setting up conditions from EVIO head bank.", e);
-            }
+    private void initializeConditions(final int runNumber) {
+        // Initialize the conditions system from the detector name and run number.
+        try {
+            ConditionsManager.defaultInstance().setDetector(this.detectorName, runNumber);
+        } catch (final ConditionsNotFoundException e) {
+            throw new RuntimeException("Error setting up conditions from EVIO head bank.", e);
         }
     }
 
@@ -65,6 +72,7 @@
      * @param evioEvent the <code>EvioEvent</code> to process
      */
     @Override
+    // FIXME: not activated by EvioLoop
     public void startRun(final EvioEvent evioEvent) {
         // System.out.println("EvioDetectorConditionsProcessor.startRun");
         if (EvioEventUtilities.isPreStartEvent(evioEvent)) {

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioEventUtilities.java	Tue Dec  1 15:55:47 2015
@@ -110,23 +110,18 @@
     /**
      * Get the run number from an EVIO event.
      *
-     * @return the run number
-     * @throws IllegalArgumentException if event does not have a head bank
-     */
-    public static int getRunNumber(final EvioEvent event) {
+     * @return the run number or <code>null</code> if not present in event
+     */
+    public static Integer getRunNumber(final EvioEvent event) {
         if (isControlEvent(event)) {
             return getControlEventData(event)[1];
         } else if (isPhysicsEvent(event)) {
             final 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());
-        }
+            } 
+        } 
+        return null;
     }
 
     /**

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java	Tue Dec  1 15:55:47 2015
@@ -12,10 +12,8 @@
 import org.jlab.coda.jevio.EvioReader;
 
 /**
- * A basic implementation of an <tt>AbstractRecordSource</tt> for supplying <tt>EvioEvent</tt> objects to a loop from a
- * list of EVIO files.
- * <p>
- * Unlike the LCIO record source, it has no rewind or indexing capabilities.
+ * A basic implementation of an <code>AbstractRecordSource</code> for supplying <code>EvioEvent</code> objects to a 
+ * loop from a list of EVIO files.
  *
  * @author Jeremy McCormick, SLAC
  */
@@ -159,10 +157,8 @@
      */
     private void openReader() {
         try {
-            System.out.println("Opening reader for file " + this.files.get(this.fileIndex) + " ...");
-            // FIXME: this should use the reader directly and cached paths should be managed externally
+            // FIXME: This should use the reader directly and MSS paths should be transformed externally.
             this.reader = EvioFileUtilities.open(this.files.get(this.fileIndex), true);
-            System.out.println("Done opening file.");
         } catch (EvioException | IOException e) {
             throw new RuntimeException(e);
         }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java	Tue Dec  1 15:55:47 2015
@@ -25,28 +25,7 @@
      * Milliseconds constant for conversion to/from second.
      */
     private static final long MILLISECONDS = 1000L;
-
-    /**
-     * Get a cached file path, assuming that the input file path is on the JLAB MSS e.g. it starts with "/mss".
-     * If the file is not on the JLAB MSS an error will be thrown.
-     * <p>
-     * If the file is already on the cache disk just return the same file.
-     *
-     * @param mssFile the MSS file path
-     * @return the cached file path (prepends "/cache" to the path)
-     * @throws IllegalArgumentException if the file is not on the MSS (e.g. path does not start with "/mss")
-     */
-    public static File getCachedFile(final File mssFile) {
-        if (!isMssFile(mssFile)) {
-            throw new IllegalArgumentException("File " + mssFile.getPath() + " is not on the JLab MSS.");
-        }
-        File cacheFile = mssFile;
-        if (!isCachedFile(mssFile)) {
-            cacheFile = new File("/cache" + mssFile.getAbsolutePath());
-        }        
-        return cacheFile;
-    }
-
+   
     /**
      * Get the run number from the file name.
      *
@@ -74,26 +53,6 @@
     }
 
     /**
-     * Return <code>true</code> if this is a file on the cache disk e.g. the path starts with "/cache".
-     *
-     * @param file the file
-     * @return <code>true</code> if the file is a cached file
-     */
-    public static boolean isCachedFile(final File file) {
-        return file.getPath().startsWith("/cache");
-    }
-
-    /**
-     * Return <code>true</code> if this file is on the JLAB MSS e.g. the path starts with "/mss".
-     *
-     * @param file the file
-     * @return <code>true</code> if the file is on the MSS
-     */
-    public static boolean isMssFile(final File file) {
-        return file.getPath().startsWith("/mss");
-    }
-
-    /**
      * Open an EVIO file using an <code>EvioReader</code> in memory mapping mode.
      *
      * @param file the EVIO file
@@ -116,14 +75,10 @@
      */
     public static EvioReader open(final File file, final boolean sequential) throws IOException, EvioException {
         LOGGER.info("opening " + file.getPath() + " in " + (sequential ? "sequential" : "mmap" + " mode"));
-        File openFile = file;
-        if (isMssFile(file)) {
-            openFile = getCachedFile(file);
-        }
         final long start = System.currentTimeMillis();
-        final EvioReader reader = new EvioReader(openFile, false, sequential);
+        final EvioReader reader = new EvioReader(file, false, sequential);
         final long end = System.currentTimeMillis() - start;
-        LOGGER.info("opened " + openFile.getPath() + " in " + (double) end / (double) MILLISECONDS + " seconds in "
+        LOGGER.info("opened " + file.getPath() + " in " + (double) end / (double) MILLISECONDS + " seconds in "
                 + (sequential ? "sequential" : "mmap" + " mode"));
         return reader;
     }

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoop.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoop.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoop.java	Tue Dec  1 15:55:47 2015
@@ -1,52 +1,24 @@
 package org.hps.record.evio;
 
-import org.freehep.record.loop.DefaultRecordLoop;
+import org.hps.record.AbstractRecordLoop;
+import org.jlab.coda.jevio.EvioEvent;
 
 /**
  * Implementation of a Freehep <code>RecordLoop</code> for EVIO data.
  *
  * @author Jeremy McCormick, SLAC
  */
-public class EvioLoop extends DefaultRecordLoop {
-
-    /**
-     * The record adapter.
-     */
-    private final EvioLoopAdapter adapter = new EvioLoopAdapter();
+public class EvioLoop extends AbstractRecordLoop<EvioEvent> {
 
     /**
      * Create a new record loop.
      */
     public EvioLoop() {
+        this.adapter = new EvioLoopAdapter();
         this.addLoopListener(adapter);
         this.addRecordListener(adapter);
     }
-
-    /**
-     * Add an EVIO event processor to the adapter which will be activated for every EVIO event that is processed.
-     *
-     * @param evioEventProcessor the EVIO processor to add
-     */
-    public void addEvioEventProcessor(final EvioEventProcessor evioEventProcessor) {
-        adapter.addEvioEventProcessor(evioEventProcessor);
-    }
-
-    /**
-     * Loop over events from the source.
-     *
-     * @param number the number of events to process or -1L for all events from the source
-     * @return the number of records that were processed
-     */
-    public long loop(final long number) {
-        if (number < 0L) {
-            this.execute(Command.GO, true);
-        } else {
-            this.execute(Command.GO_N, number, true);
-            this.execute(Command.STOP);
-        }
-        return this.getSupplied();
-    }
-
+  
     /**
      * Set the EVIO data source.
      *

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java	Tue Dec  1 15:55:47 2015
@@ -1,14 +1,6 @@
 package org.hps.record.evio;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Logger;
-
-import org.freehep.record.loop.AbstractLoopListener;
-import org.freehep.record.loop.LoopEvent;
-import org.freehep.record.loop.LoopListener;
-import org.freehep.record.loop.RecordEvent;
-import org.freehep.record.loop.RecordListener;
+import org.hps.record.AbstractLoopAdapter;
 import org.jlab.coda.jevio.EvioEvent;
 
 /**
@@ -16,79 +8,5 @@
  *
  * @author Jeremy McCormick, SLAC
  */
-public final class EvioLoopAdapter extends AbstractLoopListener implements RecordListener, LoopListener {
-
-    /**
-     * Initialize the logger.
-     */
-    private static final Logger LOGGER = Logger.getLogger(EvioLoopAdapter.class.getPackage().getName());
-
-    /**
-     * List of event processors to activate.
-     */
-    private final List<EvioEventProcessor> processors = new ArrayList<EvioEventProcessor>();
-
-    /**
-     * Create a new loop adapter.
-     */
-    EvioLoopAdapter() {
-    }
-
-    /**
-     * Add an EVIO processor to the adapter.
-     *
-     * @param processor the EVIO processor to add to the adapter
-     */
-    void addEvioEventProcessor(final EvioEventProcessor processor) {
-        LOGGER.info("adding " + processor.getClass().getName() + " to EVIO processors");
-        this.processors.add(processor);
-    }
-
-    /**
-     * Implementation of the finish hook which activates the {@link EvioEventProcessor#endJob()} method of all
-     * registered processors.
-     */
-    @Override
-    protected void finish(final LoopEvent event) {
-        LOGGER.info("finish");
-        for (final EvioEventProcessor processor : processors) {
-            processor.endJob();
-        }
-    }
-
-    /**
-     * Primary event processing method that activates the {@link EvioEventProcessor#process(EvioEvent)} method of all
-     * registered processors.
-     *
-     * @param recordEvent the record event to process which should have an EVIO event
-     * @throws IllegalArgumentException if the record is the wrong type
-     */
-    @Override
-    public void recordSupplied(final RecordEvent recordEvent) {
-        final Object record = recordEvent.getRecord();
-        if (record instanceof EvioEvent) {
-            final EvioEvent evioEvent = EvioEvent.class.cast(record);
-            for (final EvioEventProcessor processor : processors) {
-                try {
-                    processor.process(evioEvent);
-                } catch (final Exception e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        } else {
-            throw new IllegalArgumentException("The supplied record has the wrong type: " + record.getClass());
-        }
-    }
-
-    /**
-     * Implementation of the start hook which activates the {@link EvioEventProcessor#startJob()} method of all
-     * registered processors.
-     */
-    @Override
-    protected void start(final LoopEvent event) {
-        LOGGER.info("start");
-        for (final EvioEventProcessor processor : processors) {
-            processor.startJob();
-        }
-    }
+public final class EvioLoopAdapter extends AbstractLoopAdapter<EvioEvent> {
 }

Added: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigData.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigData.java	(added)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigData.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,175 @@
+package org.hps.record.svt;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+
+/**
+ * Represents the four SVT status banks from EVIO sync events containing XML config data.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public class SvtConfigData {
+        
+    /**
+     * Helper for ROC bank tag information and data array indices.
+     */
+    public enum RocTag {
+        
+        /** Data bank. */
+        DATA(51, 0, 1),
+        /** Control bank. */
+        CONTROL(66, 2, 3);
+        
+        private int tag;
+        private int configIndex;
+        private int statusIndex;
+                
+        RocTag(int tag, int configIndex, int statusIndex) {
+            this.tag = tag;
+            this.configIndex = configIndex;
+            this.statusIndex = statusIndex;
+        }
+        
+        int configIndex() {
+            return configIndex;
+        }
+        
+        int statusIndex() {
+            return statusIndex;
+        }
+        
+        /**
+         * Get the ROC tag from an int value.
+         * 
+         * @param tag the tag's int value
+         * @return the matching <code>RocTag</code>
+         * @throws IllegalArgumentException if <code>tag</code> is not valid
+         */
+        static RocTag fromTag(int tag) {
+            if (tag == DATA.tag) {
+                return DATA;
+            } else if (tag == CONTROL.tag) {
+                return CONTROL;
+            } else {
+                throw new IllegalArgumentException("Unknown tag " + tag + " for ROC.");
+            }
+        }
+    }
+    
+    // Unix timestamp from the closest head bank.
+    private int timestamp;
+    
+    // The config data strings.
+    private String[] data = new String[4];
+        
+    public SvtConfigData(int timestamp) {
+        this.timestamp = timestamp;
+    }
+    
+    public void setData(RocTag rocTag, String data) {
+        if (data.contains("<config>")) {
+            setConfigData(rocTag, data);
+        }
+        if (data.contains("<status>")) {
+            setStatusData(rocTag, data);
+        }
+    }
+    
+    public void setConfigData(RocTag rocTag, String configData) {
+        if (rocTag.equals(RocTag.DATA)) {
+            data[RocTag.DATA.configIndex()] = configData;
+        } else {
+            data[RocTag.CONTROL.configIndex()] = configData;
+        }
+    }
+    
+    public void setStatusData(RocTag rocTag, String statusData) {
+        if (rocTag.equals(RocTag.DATA)) {
+            data[RocTag.DATA.statusIndex()] = statusData;
+        } else {
+            data[RocTag.CONTROL.statusIndex()] = statusData;
+        }
+    }
+    
+    public String getConfigData(RocTag roc) {
+        if (roc.equals(RocTag.DATA)) {
+            return data[RocTag.DATA.configIndex()];
+        } else {
+            return data[RocTag.CONTROL.configIndex()];
+        }
+    }
+    
+    public String getStatusData(RocTag roc) {
+        if (roc.equals(RocTag.DATA)) {
+            return data[RocTag.DATA.statusIndex()];
+        } else {
+            return data[RocTag.CONTROL.statusIndex()];
+        }
+    }
+    
+    public Document toXmlDocument() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<svt>" + '\n');
+        if (getConfigData(RocTag.DATA) != null) {
+            sb.append(getConfigData(RocTag.DATA));
+        } else {
+            sb.append("<config/>" + '\n');
+        }
+        if (getStatusData(RocTag.DATA) != null) {
+            sb.append(getStatusData(RocTag.DATA));
+        } else {
+            sb.append("<status/>" + '\n');
+        }
+        if (getConfigData(RocTag.CONTROL) != null) {
+            sb.append(getConfigData(RocTag.CONTROL));                
+        } else {
+            sb.append("<config/>" + '\n');
+        }
+        if (getStatusData(RocTag.CONTROL) != null) {
+            sb.append(getStatusData(RocTag.CONTROL));   
+        } else {
+            sb.append("<status/>" + '\n');
+        }
+        sb.append("</svt> + '\n'");
+        Document document = null;
+        try {
+            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+            document = builder.parse(new InputSource(new StringReader(sb.toString())));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return document;
+    }
+    
+    public String toXmlString() {
+        Document document = toXmlDocument();
+        String output = null;
+        try {
+            TransformerFactory tf = TransformerFactory.newInstance();
+            Transformer transformer = tf.newTransformer();
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            StringWriter writer = new StringWriter();
+            transformer.transform(new DOMSource(document), new StreamResult(writer));
+            output = writer.getBuffer().toString().replaceAll("\n|\r", "");
+        } catch (Exception e) {
+            throw new RuntimeException();
+        }
+        return output;
+    }
+    
+    public int getTimestamp() {
+        return timestamp;
+    }
+}

Added: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigEvioProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigEvioProcessor.java	(added)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/svt/SvtConfigEvioProcessor.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,87 @@
+package org.hps.record.svt;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.hps.record.evio.EvioEventProcessor;
+import org.hps.record.evio.EvioEventUtilities;
+import org.hps.record.svt.SvtConfigData.RocTag;
+import org.jlab.coda.jevio.BaseStructure;
+import org.jlab.coda.jevio.EvioEvent;
+
+/**
+ * Get a list of SVT config data from an EVIO data stream.
+ * 
+ * @author Jeremy McCormick, SLAC
+ * @see SvtConfigData
+ */
+public class SvtConfigEvioProcessor extends EvioEventProcessor {
+
+    private static Logger LOGGER = Logger.getLogger(SvtConfigEvioProcessor.class.getPackage().getName());
+
+    private static final int DATA_TAG = 51;
+    private static final int CONTROL_TAG = 66;
+    private static final int CONFIG_TAG = 57614;
+
+    private List<SvtConfigData> configs = new ArrayList<SvtConfigData>();
+
+    private int timestamp = 0;
+    
+    public void process(EvioEvent evioEvent) {
+        SvtConfigData config = null;
+        BaseStructure headBank = EvioEventUtilities.getHeadBank(evioEvent);
+        int configBanks = 0;
+        if (headBank != null) {
+            if (headBank.getIntData()[0] != 0) {
+                timestamp = headBank.getIntData()[0];
+                LOGGER.info("set timestamp " + timestamp);
+            }
+        }
+        for (BaseStructure bank : evioEvent.getChildrenList()) {
+            if (bank.getHeader().getTag() == DATA_TAG || bank.getHeader().getTag() == CONTROL_TAG) {
+                if (bank.getChildrenList() != null) {
+                    for (BaseStructure subBank : bank.getChildrenList()) {
+                        if (subBank.getHeader().getTag() == CONFIG_TAG) {
+                            String[] stringData = subBank.getStringData();
+                            if (stringData == null) {
+                                LOGGER.warning("string data is null");
+                                if (subBank.getRawBytes() != null) {
+                                    LOGGER.info("raw byte array len " + subBank.getRawBytes().length);
+                                    LOGGER.info("cnv to string data" + '\n' + new String(subBank.getRawBytes(), StandardCharsets.UTF_8));
+                                } else {
+                                    LOGGER.warning("Raw byte array is null.");
+                                }
+                            } else {
+                                if (config == null) {
+                                    config = new SvtConfigData(timestamp);
+                                }
+                                if (stringData.length > 0) {
+                                    if (!stringData[0].trim().isEmpty()) {
+                                        LOGGER.info("Adding SVT config data with len " + stringData[0].length() + " ..." + '\n' + stringData[0]);
+                                        config.setData(RocTag.fromTag(bank.getHeader().getTag()), stringData[0]);
+                                        ++configBanks;
+                                    } else {
+                                        LOGGER.warning("String data has no XML content.");
+                                    }
+                                } else {
+                                    LOGGER.warning("String data has zero len.");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } 
+        if (config != null) {
+            LOGGER.info("Adding SVT config " + evioEvent.getEventNumber() + " with " + configBanks 
+                    + " banks from event " + evioEvent.getEventNumber());
+            this.configs.add(config);
+        }
+    }
+
+    public List<SvtConfigData> getSvtConfigs() {
+        return configs;
+    }
+}

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetEvioProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetEvioProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetEvioProcessor.java	Tue Dec  1 15:55:47 2015
@@ -55,13 +55,18 @@
             }
         }
     }
+    
+    public long getTiTimeOffset() {
+        final long offsetRange = maxOffset - minOffset;
+        if (offsetRange > minRange && nOutliers < maxOutliers) {
+            return minOffset;
+        } else {
+            return 0L;
+        }
+    }
 
     public void updateTriggerConfig(final TriggerConfig triggerConfig) {
-        final long offsetRange = maxOffset - minOffset;
-        if (offsetRange > minRange && nOutliers < maxOutliers) {
-            triggerConfig.put(TriggerConfigVariable.TI_TIME_OFFSET, minOffset);
-        } else {
-            triggerConfig.put(TriggerConfigVariable.TI_TIME_OFFSET, 0L);
-        }
+        long tiTimeOffset = getTiTimeOffset();
+        triggerConfig.put(TriggerConfigVariable.TI_TIME_OFFSET, tiTimeOffset);
     }
 }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDaoImpl.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDaoImpl.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDaoImpl.java	Tue Dec  1 15:55:47 2015
@@ -97,12 +97,12 @@
                 deleteEpicsData.setInt(1, headerId);
                 int rowsAffected = deleteEpicsData.executeUpdate();
                 if (rowsAffected == 0) {
-                    throw new SQLException("Deletion of EPICS data failed; no rows affect.");
+                    throw new SQLException("Deletion of EPICS data failed; no rows affected.");
                 }
                 deleteHeader.setInt(1, headerId);
                 rowsAffected = deleteHeader.executeUpdate();
                 if (rowsAffected == 0) {
-                    throw new SQLException("Deletion of EPICS header failed; no rows affect.");
+                    throw new SQLException("Deletion of EPICS header failed; no rows affected.");
                 }
             }
 
@@ -137,7 +137,7 @@
      * Get EPICS data by run.
      *
      * @param run the run number
-     * @param epicsType the type of EPICS data (1s or 10s)
+     * @param epicsType the type of EPICS data (2s or 20s)
      * @return the EPICS data
      */
     @Override
@@ -238,11 +238,11 @@
                     insertStatement.setDouble(parameterIndex, value);
                     ++parameterIndex;
                 }
-                final int dataRowsCreated = insertStatement.executeUpdate();                
+                final int dataRowsCreated = insertStatement.executeUpdate();
                 if (dataRowsCreated == 0) {
                     throw new SQLException("Creation of EPICS data failed; no rows affected.");
                 }
-                LOGGER.info("inserted EPICS data with run " + epicsHeader.getRun() + ", seq " + epicsHeader.getSequence() + "timestamp " 
+                LOGGER.fine("inserted EPICS data with run " + epicsHeader.getRun() + "; seq " + epicsHeader.getSequence() + "; timestamp " 
                         + epicsHeader.getTimestamp());
                 insertStatement.close();
             }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsType.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsType.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsType.java	Tue Dec  1 15:55:47 2015
@@ -3,34 +3,34 @@
 import org.hps.record.epics.EpicsData;
 
 /**
- * Enum for representing different types of EPICS data in the run database, of which there are currently two (1s and
- * 10s).
+ * Enum for representing different types of EPICS data in the run database, of which there are currently two (2s and
+ * 20s).
  *
  * @author Jeremy McCormick, SLAC
  */
 public enum EpicsType {
 
     /**
-     * 10S EPICS data.
+     * 20S EPICS data.
      */
-    EPICS_10S(10),
+    EPICS_20s(10),
     /**
-     * 1S EPICS data.
+     * 2S EPICS data.
      */
-    EPICS_1S(1);
+    EPICS_2s(1);
 
     /**
      * Get the type from an int.
      *
      * @param type the type from an int
      * @return the type from an int
-     * @throws IllegalArgumentException if <code>type</code> is invalid (not 1 or 10)
+     * @throws IllegalArgumentException if <code>type</code> is invalid (not 2 or 20)
      */
     public static EpicsType fromInt(final int type) {
-        if (type == EPICS_1S.type) {
-            return EPICS_1S;
-        } else if (type == EPICS_10S.type) {
-            return EPICS_10S;
+        if (type == EPICS_2s.type) {
+            return EPICS_2s;
+        } else if (type == EPICS_20s.type) {
+            return EPICS_20s;
         } else {
             throw new IllegalArgumentException("The type code is invalid (must be 1 or 10): " + type);
         }
@@ -44,9 +44,9 @@
     public static EpicsType getEpicsType(final EpicsData epicsData) {
         // FIXME: The type argument should be set on creation which would make this key check unnecessary.
         if (epicsData.getKeys().contains("MBSY2C_energy")) {
-            return EpicsType.EPICS_1S;
+            return EpicsType.EPICS_2s;
         } else {
-            return EpicsType.EPICS_10S;
+            return EpicsType.EPICS_20s;
         }
     }
 

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsVariable.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsVariable.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsVariable.java	Tue Dec  1 15:55:47 2015
@@ -2,7 +2,7 @@
 
 /**
  * Information about an EPICS variable including its name in the EPICS database, column name for the run database,
- * description of the variable, and type (either 1s or 10s).
+ * description of the variable, and type (either 2s or 20s).
  * <p>
  * This class is used to represent data from the <i>epics_variables</i> table in the run database.
  *

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseBuilder.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseBuilder.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,740 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.hps.conditions.database.ConnectionParameters;
+import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.run.RunSpreadsheet;
+import org.hps.conditions.run.RunSpreadsheet.RunData;
+import org.hps.datacat.client.DatacatClient;
+import org.hps.datacat.client.DatacatClientFactory;
+import org.hps.datacat.client.Dataset;
+import org.hps.datacat.client.DatasetSite;
+import org.hps.record.AbstractRecordProcessor;
+import org.hps.record.daqconfig.DAQConfigEvioProcessor;
+import org.hps.record.epics.EpicsData;
+import org.hps.record.epics.EpicsRunProcessor;
+import org.hps.record.evio.EventTagConstant;
+import org.hps.record.evio.EvioEventUtilities;
+import org.hps.record.evio.EvioFileSource;
+import org.hps.record.evio.EvioFileUtilities;
+import org.hps.record.evio.EvioLoop;
+import org.hps.record.scalers.ScalerData;
+import org.hps.record.scalers.ScalerUtilities;
+import org.hps.record.scalers.ScalerUtilities.LiveTimeIndex;
+import org.hps.record.scalers.ScalersEvioProcessor;
+import org.hps.record.svt.SvtConfigData;
+import org.hps.record.svt.SvtConfigEvioProcessor;
+import org.hps.record.triggerbank.AbstractIntData.IntBankDefinition;
+import org.hps.record.triggerbank.HeadBankData;
+import org.hps.record.triggerbank.TiTimeOffsetEvioProcessor;
+import org.jlab.coda.jevio.BaseStructure;
+import org.jlab.coda.jevio.EvioEvent;
+import org.jlab.coda.jevio.EvioException;
+import org.jlab.coda.jevio.EvioReader;
+import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
+
+/**
+ * Builds a complete {@link RunSummary} object from various data sources, including the data catalog and the run
+ * spreadsheet, so that it is ready to be inserted into the run database using the DAO interfaces.  This class also 
+ * extracts EPICS and scaler records from the EVIO data for insertion into run database tables.
+ * <p>
+ * The setters and some other methods follow the builder pattern and so can be chained by the caller.
+ * 
+ * @author Jeremy McCormick, SLAC
+ * @see RunSummary
+ * @see RunSummaryImpl
+ */
+final class RunDatabaseBuilder {
+
+    /**
+     * Package logger.
+     */
+    private static final Logger LOGGER = Logger.getLogger(RunDatabaseBuilder.class.getPackage().getName());
+
+    /**
+     * Database connection.
+     */
+    private ConnectionParameters connectionParameters;
+
+    /**
+     * Data catalog client API.
+     */
+    private DatacatClient datacatClient;
+
+    /**
+     * Detector name for initializing conditions system.
+     */
+    private String detectorName;
+
+    /**
+     * Dry run to not perform database updates (off by default).
+     */
+    private boolean dryRun = false;
+
+    /**
+     * List of EPICS data from the run.
+     */
+    private List<EpicsData> epicsData;
+
+    /**
+     * Map of EVIO files to their dataset objects.
+     */
+    private Map<File, Dataset> evioDatasets;
+
+    /**
+     * List of EVIO files.
+     */
+    private List<File> evioFiles;
+
+    /**
+     * Allow replacement of information in the database (off by default).
+     */
+    private boolean replace = false;
+
+    /**
+     * Run summary to be updated.
+     */
+    private RunSummaryImpl runSummary;
+
+    /**
+     * List of scaler data from the run.
+     */
+    private List<ScalerData> scalerData;
+
+    /**
+     * Skip full EVIO file processing (off by default).
+     */
+    private boolean skipEvioProcessing = false;
+
+    /**
+     * Path to run spreadsheet CSV file (not used by default).
+     */
+    private File spreadsheetFile;
+
+    /**
+     * List of SVT configuration bank data.
+     */
+    private List<SvtConfigData> svtConfigs;
+        
+    /**
+     * Create an empty run summary.
+     * 
+     * @param run the run number
+     * @return the empty run summary
+     */
+    RunDatabaseBuilder createRunSummary(int run) {
+        runSummary = new RunSummaryImpl(run);
+        return this;
+    }
+
+    /**
+     * Find EVIO files in the data catalog.
+     */
+    private void findEvioDatasets() {
+        LOGGER.info("finding EVIO datasets for run " + getRun());
+        
+        // Metadata to return from search.
+        final Set<String> metadata = new LinkedHashSet<String>();
+        metadata.add("runMin");
+        metadata.add("eventCount");
+        
+        // Initialize map of files to datasets.
+        evioDatasets = new HashMap<File, Dataset>();
+        
+        // Find datasets in the datacat using a search.
+        final List<Dataset> datasets = datacatClient.findDatasets(
+                "data/raw",
+                "fileFormat eq 'EVIO' AND dataType eq 'RAW' AND runMin eq " + getRun(), 
+                metadata);
+        if (datasets.isEmpty()) {
+            // No files for the run in datacat is a fatal error.
+            throw new IllegalStateException("No EVIO datasets for run " + getRun() + " were found in the data catalog.");
+        }
+        
+        // Map file to dataset.
+        for (final Dataset dataset : datasets) {
+            evioDatasets.put(new File(dataset.getLocations().get(0).getResource()), dataset);
+        }
+        
+        // Create the list of EVIO files.
+        evioFiles = new ArrayList<File>();
+        evioFiles.addAll(evioDatasets.keySet());
+        EvioFileUtilities.sortBySequence(evioFiles);
+        
+        LOGGER.info("found " + evioFiles.size() + " EVIO file(s) for run " + runSummary.getRun());
+    }
+   
+    /**
+     * Get the current run number from the run summary.
+     * 
+     * @return the run number from the run summary
+     */
+    int getRun() {
+        return runSummary.getRun();
+    }
+
+    /**
+     * Initialize the datacat client.
+     */
+    private void initializeDatacat() {
+
+        LOGGER.info("initializing data catalog client");
+
+        // DEBUG: use dev datacat server; prod should use default JLAB connection
+        datacatClient = new DatacatClientFactory().createClient("http://localhost:8080/datacat-v0.4-SNAPSHOT/r",
+                DatasetSite.SLAC, "HPS");
+    }
+
+    /**
+     * Insert the run data into the database using the current connection.
+     */
+    private void insertRun(Connection connection) {
+
+        LOGGER.info("inserting run " + runSummary.getRun() + " into db");
+
+        // Create DAO factory.
+        final RunDatabaseDaoFactory runFactory = new RunDatabaseDaoFactory(connection);
+
+        // Insert the run summary record.
+        LOGGER.info("inserting run summary");
+        runFactory.createRunSummaryDao().insertRunSummary(runSummary);
+
+        // Insert the EPICS data.
+        if (epicsData != null) {
+            LOGGER.info("inserting EPICS data");
+            runFactory.createEpicsDataDao().insertEpicsData(epicsData);
+        } else {
+            LOGGER.warning("no EPICS data to insert");
+        }
+
+        // Insert the scaler data.
+        if (scalerData != null) {
+            LOGGER.info("inserting scaler data");
+            runFactory.createScalerDataDao().insertScalerData(scalerData, getRun());
+        } else {
+            LOGGER.warning("no scaler data to insert");
+        }
+
+        // Insert SVT config data.
+        if (this.svtConfigs != null) {
+            LOGGER.info("inserting SVT config");
+            runFactory.createSvtConfigDao().insertSvtConfigs(svtConfigs, getRun());
+        } else {
+            LOGGER.warning("no SVT config to insert");
+        }
+        
+        try {
+            connection.close();
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, e.getMessage(), e);
+        }
+               
+        LOGGER.info("done inserting run " + getRun());
+    }
+    
+    /**
+     * Reload state for the current run number into this object (used for testing after a database insert).
+     * 
+     * @param load <code>true</code> if this method should be executed (skipped if <code>false</code>)
+     * @return this object
+     */
+    RunDatabaseBuilder load(boolean load) {
+        if (load) {
+            RunManager runManager = new RunManager(connectionParameters.createConnection());
+            runManager.setRun(getRun());
+
+            this.runSummary = RunSummaryImpl.class.cast(runManager.getRunSummary());
+
+            LOGGER.info("loaded run summary ..." + '\n' + runSummary);
+
+            epicsData = new ArrayList<EpicsData>();
+            epicsData.addAll(runManager.getEpicsData(EpicsType.EPICS_2s));
+            epicsData.addAll(runManager.getEpicsData(EpicsType.EPICS_20s));
+            LOGGER.info("loaded " + epicsData.size() + " EPICS records");
+
+            scalerData = runManager.getScalerData();
+            LOGGER.info("loaded " + scalerData.size() + " scaler records");
+
+            svtConfigs = runManager.getSvtConfigData();
+            LOGGER.info("loaded " + svtConfigs.size() + " SVT configurations");
+
+            runManager.closeConnection();
+        } else {
+            LOGGER.info("load is skipped");
+        }
+        
+        return this;
+    }
+    
+    /**
+     * Print summary information to the log.
+     */
+    private void printSummary() {
+        LOGGER.info("built run summary ..." + '\n' + runSummary.toString());
+        if (epicsData != null) {
+            LOGGER.info("found " + epicsData.size() + " EPICS data records");
+        } else {
+            LOGGER.info("no EPICS data");
+        }
+        if (scalerData != null) {
+            LOGGER.info("found " + scalerData.size() + " scalers");
+        } else {
+            LOGGER.info("no scaler data");
+        }
+        if (svtConfigs != null) {
+            for (SvtConfigData config : svtConfigs) {
+                try {
+                    LOGGER.info("SVT XML config with timestamp " + config.getTimestamp() + " ..." + config.toXmlString());
+                } catch (Exception e) {
+                    LOGGER.warning("Could not print config!  Probably bad string data.");
+                }
+            }
+        } else {
+            LOGGER.info("no SVT config");
+        }
+        if (runSummary.getTriggerConfigData() != null) {
+            for (Entry<Integer, String> entry : runSummary.getTriggerConfigData().entrySet()) {
+                LOGGER.info("trigger config data " + entry.getKey() + " ..." + entry.getValue());
+            }
+        } else {
+            LOGGER.info("no trigger config");
+        }
+    }    
+
+    /**
+     * Process all the EVIO files in the run and set information on the current run summary.
+     */
+    private void processEvioFiles() {
+
+        LOGGER.fine("processing EVIO files");
+
+        if (evioFiles == null || evioFiles.isEmpty()) {
+            throw new IllegalStateException("No EVIO files were found.");
+        }
+
+        if (detectorName == null) {
+            throw new IllegalStateException("The detector name was not set.");
+        }
+
+        // Initialize the conditions system.
+        try {
+            DatabaseConditionsManager dbManager = DatabaseConditionsManager.getInstance();
+            DatabaseConditionsManager.getInstance().setDetector(detectorName, runSummary.getRun());
+            dbManager.freeze();
+        } catch (ConditionsNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        // List of processors to execute in the job.
+        ArrayList<AbstractRecordProcessor<EvioEvent>> processors = new ArrayList<AbstractRecordProcessor<EvioEvent>>();
+
+        // Processor to get scaler data.
+        ScalersEvioProcessor scalersProcessor = new ScalersEvioProcessor();
+        scalersProcessor.setResetEveryEvent(false);
+        processors.add(scalersProcessor);
+
+        // Processor for calculating TI time offset.
+        TiTimeOffsetEvioProcessor tiProcessor = new TiTimeOffsetEvioProcessor();
+        processors.add(tiProcessor);
+
+        // Processor for getting DAQ config.
+        DAQConfigEvioProcessor daqProcessor = new DAQConfigEvioProcessor();
+        processors.add(daqProcessor);
+
+        // Processor for getting the SVT XML config.
+        SvtConfigEvioProcessor svtProcessor = new SvtConfigEvioProcessor();
+        processors.add(svtProcessor);
+
+        // Processor for getting EPICS data.
+        EpicsRunProcessor epicsProcessor = new EpicsRunProcessor();
+        processors.add(epicsProcessor);
+
+        // Run the job using the EVIO loop.
+        EvioLoop loop = new EvioLoop();
+        loop.addProcessors(processors);
+        EvioFileSource source = new EvioFileSource(evioFiles);
+        loop.setEvioFileSource(source);
+        loop.loop(-1);
+
+        // Set livetime field values.
+        updateLivetimes(scalersProcessor);
+
+        // Set TI time offset.
+        runSummary.setTiTimeOffset(tiProcessor.getTiTimeOffset());
+
+        // Set DAQ config object.
+        runSummary.setDAQConfig(daqProcessor.getDAQConfig());
+
+        // Set map of crate number to string trigger config data.
+        runSummary.setTriggerConfigData(daqProcessor.getTriggerConfigData());
+        LOGGER.info("found " + daqProcessor.getTriggerConfigData().size() + " valid SVT config events");
+
+        // Set EPICS data list.
+        epicsData = epicsProcessor.getEpicsData();
+
+        // Set scalers list.
+        scalerData = scalersProcessor.getScalerData();
+
+        // Set SVT config data strings.
+        svtConfigs = svtProcessor.getSvtConfigs();
+
+        LOGGER.info("done processing EVIO files");
+    }
+
+    /**
+     * Run the job to build the information for the database and perform an update (if not dry run).
+     * 
+     * @return this object
+     */
+    RunDatabaseBuilder run() {
+        
+        LOGGER.info("building run " + getRun());
+        
+        if (this.runSummary == null) {
+            throw new IllegalStateException("The run summary was never created.");
+        }        
+        
+        // Setup datacat client.
+        initializeDatacat();
+        
+        // Find EVIO datasets in the datacat.
+        findEvioDatasets();
+
+        // Set total number of files.
+        updateTotalFiles();
+
+        // Set GO and PRESTART timestamps.
+        updateStartTimestamps();
+
+        // Set END timestamp.
+        updateEndTimestamp();
+
+        // Set total number of events.
+        updateTotalEvents();
+
+        // Calculate trigger rate.
+        updateTriggerRate();
+                
+        // Run EVIO job if enabled.
+        if (!this.skipEvioProcessing) {
+            processEvioFiles();
+        } else {
+            LOGGER.info("EVIO file processing is skipped.");
+        }
+
+        // Get extra info from spreadsheet if enabled.
+        if (this.spreadsheetFile != null) {
+            updateFromSpreadsheet();
+        } else {
+            LOGGER.info("Run spreadsheet not used.");
+        }
+
+        // Print out summary info to the log before updating database.
+        printSummary();
+
+        if (!dryRun) {
+            // Update the database.
+            updateDatabase();
+        } else {
+            // Dry run so database is not updated.
+            LOGGER.info("Dry run enabled so no updates were performed.");
+        }
+        
+        return this;
+    }
+
+    /**
+     * Set the database connection to the run database.
+     * 
+     * @param connection the database connection to the run database
+     * @return this object
+     */
+    RunDatabaseBuilder setConnectionParameters(ConnectionParameters connectionParameters) {
+        this.connectionParameters = connectionParameters;
+        return this;
+    }
+
+    /**
+     * Set the detector name for initializing the conditions system.
+     * 
+     * @param detectorName the detector name for initializing the conditions system
+     * @return this object
+     */
+    RunDatabaseBuilder setDetectorName(String detectorName) {
+        this.detectorName = detectorName;
+        LOGGER.config("detector = " + this.detectorName);
+        return this;
+    }
+
+    /**
+     * Set dry run which will not update the database.
+     * 
+     * @param dryRun <code>true</code> to perform dry run
+     * @return this object
+     */
+    RunDatabaseBuilder setDryRun(boolean dryRun) {
+        this.dryRun = dryRun;
+        LOGGER.config("dryRun = " + this.dryRun);
+        return this;
+    }
+
+    /**
+     * Enable replacement of existing records in the database.
+     * 
+     * @param replace <code>true</code> to allow replacement of records
+     * @return this object
+     */
+    RunDatabaseBuilder setReplace(boolean replace) {
+        this.replace = replace;
+        LOGGER.config("replace = " + this.replace);
+        return this;
+    }
+
+    /**
+     * Set the path to the run spreadsheet CSV file from Google Docs.
+     * 
+     * @param spreadsheetFile spreadsheet CSV file (can be <code>null</code>)
+     * @return this object
+     */
+    RunDatabaseBuilder setSpreadsheetFile(File spreadsheetFile) {
+        this.spreadsheetFile = spreadsheetFile;
+        if (this.spreadsheetFile != null) {
+            LOGGER.config("spreadsheetFile = " + this.spreadsheetFile.getPath());
+        }
+        return this;
+    }
+    
+    /**
+     * Set whether full EVIO file processing should occur to extract EPICS data, etc. 
+     * <p>
+     * Even if this is disabled, the first and last EVIO files will still be processed
+     * for timestamps.
+     * 
+     * @param skipEvioFileProcessing <code>true</code> to disable full EVIO file processing
+     * @return this object
+     */
+    RunDatabaseBuilder skipEvioProcessing(boolean skipEvioProcessing) {
+        this.skipEvioProcessing = skipEvioProcessing;
+        LOGGER.config("skipEvioFileProcessing = " + this.skipEvioProcessing);
+        return this;
+    }
+
+    /**
+     * Update the database after the run information has been created.
+     */
+    private void updateDatabase() {
+
+        LOGGER.fine("updating the run database");
+        
+        // Initialize the run manager.
+        RunManager runManager = new RunManager(connectionParameters.createConnection());
+        runManager.setRun(runSummary.getRun());
+
+        // Does run exist?
+        if (runManager.runExists()) {
+            
+            LOGGER.info("run already exists");
+            
+            // If replacement is not enabled and run exists, then this is a fatal exception.
+            if (!replace) {
+                throw new RuntimeException("Run already exists (use -x option to enable replacement).");
+            }
+
+            // Delete the run so insert statements can be used to rebuild it.
+            LOGGER.info("deleting existing run");
+            runManager.deleteRun();
+        }
+
+        // Insert the run data into the database.
+        LOGGER.info("inserting the run data");
+        insertRun(runManager.getConnection());
+
+        // Close the database connection.
+        runManager.closeConnection();
+    }
+
+    /**
+     * Update the run summary's end timestamp.
+     */
+    private void updateEndTimestamp() {
+        LOGGER.info("updating end timestamp");
+        IntBankDefinition headBankDefinition = new IntBankDefinition(HeadBankData.class, new int[] {0x2e, 0xe10f});
+        File lastEvioFile = evioFiles.get(evioFiles.size() - 1);
+        EvioReader reader = null;
+        Integer endTimestamp = null;
+        try {
+            reader = EvioFileUtilities.open(lastEvioFile, true);
+            EvioEvent evioEvent = reader.parseNextEvent();
+            while (evioEvent != null) {
+                if (EventTagConstant.END.matches(evioEvent)) {
+                    endTimestamp = EvioEventUtilities.getControlEventData(evioEvent)[0];
+                    LOGGER.fine("found END timestamp " + endTimestamp);
+                    break;
+                }
+                BaseStructure headBank = headBankDefinition.findBank(evioEvent);
+                if (headBank != null) {
+                    if (headBank.getIntData()[0] != 0) {
+                        endTimestamp = headBank.getIntData()[0];
+                    }
+                }
+                evioEvent = reader.parseNextEvent();
+            }
+        } catch (IOException | EvioException e) {
+            throw new RuntimeException("Error reading first EVIO file.", e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Exception e) {
+                    LOGGER.log(Level.SEVERE, e.getMessage(), e);
+                }
+            }
+        }
+        runSummary.setEndTimestamp(endTimestamp);
+        LOGGER.fine("end timestamp set to " + endTimestamp);
+    }
+
+    /**
+     * Update the current run summary from information in the run spreadsheet.
+     * 
+     * @param spreadsheetFile file object pointing to the run spreadsheet (CSV format)
+     * @return this object
+     */
+    private void updateFromSpreadsheet() {       
+        LOGGER.fine("updating from spreadsheet file " + spreadsheetFile.getPath());
+        RunSpreadsheet runSpreadsheet = new RunSpreadsheet(spreadsheetFile);
+        RunData data = runSpreadsheet.getRunMap().get(runSummary.getRun());        
+        if (data != null) {
+            LOGGER.info("found run data ..." + '\n' + data.getRecord());
+            String triggerConfigName = data.getRecord().get("trigger_config");
+            if (triggerConfigName != null) {
+                runSummary.setTriggerConfigName(triggerConfigName);
+                LOGGER.info("set trigger config name <" + runSummary.getTriggerConfigName() + "> from spreadsheet");
+            }
+            String notes = data.getRecord().get("notes");
+            if (notes != null) {
+                runSummary.setNotes(notes);
+                LOGGER.info("set notes <" + runSummary.getNotes() + "> from spreadsheet");
+            }
+            String target = data.getRecord().get("target");
+            if (target != null) {
+                runSummary.setTarget(target);
+                LOGGER.info("set target <" + runSummary.getTarget() + "> from spreadsheet");
+            }
+        } else {
+            LOGGER.warning("No record for this run was found in spreadsheet.");
+        }
+    }
+
+    /**
+     * Calculate the DAQ livetime measurements from the last scaler data bank.
+     * 
+     * @param scalersProcessor the EVIO scaler data processor
+     */
+    private void updateLivetimes(ScalersEvioProcessor scalersProcessor) {
+        LOGGER.fine("updating livetime calculations");
+        ScalerData scalers = scalersProcessor.getCurrentScalerData();
+        if (scalers == null) {
+            throw new IllegalStateException("No scaler data was found by the EVIO processor.");
+        }
+        double[] livetimes = ScalerUtilities.getLiveTimes(scalers);
+        runSummary.setLivetimeClock(livetimes[LiveTimeIndex.CLOCK.ordinal()]);
+        runSummary.setLivetimeFcupTdc(livetimes[LiveTimeIndex.FCUP_TDC.ordinal()]);
+        runSummary.setLivetimeFcupTrg(livetimes[LiveTimeIndex.FCUP_TRG.ordinal()]);
+        LOGGER.info("clock livetime set to " + runSummary.getLivetimeClock());
+        LOGGER.info("fcup tdc livetime set to " + runSummary.getLivetimeFcupTdc());
+        LOGGER.info("fcup trg livetime set to " + runSummary.getLivetimeFcupTrg());
+    }
+
+    /**
+     * Update the starting timestamps from the first EVIO file.
+     */
+    private void updateStartTimestamps() {
+        LOGGER.fine("updating start timestamps");
+        File firstEvioFile = evioFiles.get(0);
+        int sequence = EvioFileUtilities.getSequenceFromName(firstEvioFile);
+        if (sequence != 0) {
+            LOGGER.warning("first file does not have sequence 0");
+        }
+        EvioReader reader = null;
+        try {
+            reader = EvioFileUtilities.open(firstEvioFile, true);
+            EvioEvent evioEvent = reader.parseNextEvent();
+            Integer prestartTimestamp = null;
+            Integer goTimestamp = null;
+            while (evioEvent != null) {
+                if (EventTagConstant.PRESTART.matches(evioEvent)) {
+                    prestartTimestamp = EvioEventUtilities.getControlEventData(evioEvent)[0];
+                } else if (EventTagConstant.GO.matches(evioEvent)) {
+                    goTimestamp = EvioEventUtilities.getControlEventData(evioEvent)[0];
+                }
+                if (prestartTimestamp != null && goTimestamp != null) {
+                    break;
+                }
+                evioEvent = reader.parseNextEvent();
+            }
+            runSummary.setPrestartTimestamp(prestartTimestamp);
+            runSummary.setGoTimestamp(goTimestamp);
+        } catch (IOException | EvioException e) {
+            throw new RuntimeException("Error reading first EVIO file.", e);
+        } finally {
+            try {
+                reader.close();
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage(), e);
+            }
+        }
+        LOGGER.info("PRESTART timestamp set to " + runSummary.getPrestartTimestamp());
+        LOGGER.info("GO timestamp set to " + runSummary.getGoTimestamp());
+    }
+
+    /**
+     * Update the total number of events.
+     */
+    private void updateTotalEvents() {
+        LOGGER.fine("updating total events");
+        int totalEvents = 0;
+        for (Entry<File, Dataset> entry : evioDatasets.entrySet()) {
+            totalEvents += entry.getValue().getLocations().get(0).getEventCount();
+        }
+        runSummary.setTotalEvents(totalEvents);
+        LOGGER.info("total events set to " + runSummary.getTotalEvents());
+    }
+
+    /**
+     * Update the total number of EVIO files in the run.
+     */
+    private void updateTotalFiles() {
+        LOGGER.fine("updating total files");
+        // Set number of files from datacat query.
+        runSummary.setTotalFiles(evioFiles.size());
+        LOGGER.info("total files set to " + runSummary.getTotalFiles());
+    }
+
+    /**
+     * Update the trigger rate.
+     */
+    private void updateTriggerRate() {
+        LOGGER.fine("updating trigger rate");
+        if (runSummary.getEndTimestamp() != null && runSummary.getGoTimestamp() != null) {
+            double triggerRate = ((double) runSummary.getTotalEvents() / ((double) runSummary.getEndTimestamp() - (double) runSummary
+                    .getGoTimestamp())) / 1000.;
+            runSummary.setTriggerRate(triggerRate);
+            LOGGER.info("trigger rate set to " + runSummary.getTriggerRate());
+        } else {
+            LOGGER.warning("Skipped trigger rate calculation because END or GO timestamp is missing.");
+        }
+    }
+}

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseCommandLine.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseCommandLine.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseCommandLine.java	Tue Dec  1 15:55:47 2015
@@ -1,76 +1,39 @@
 package org.hps.run.database;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Logger;
 
 import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.DefaultParser;
 import org.hps.conditions.database.ConnectionParameters;
-import org.hps.datacat.client.DatacatClient;
-import org.hps.datacat.client.DatacatClientFactory;
-import org.hps.datacat.client.Dataset;
-import org.hps.datacat.client.DatasetMetadata;
-import org.hps.record.evio.EvioFileUtilities;
 
 /**
- * Command line tool for updating the run database from EVIO files registered in the data catalog.
+ * Command line tool for inserting records into the run database.
  *
  * @author Jeremy McCormick, SLAC
  */
-public class RunDatabaseCommandLine {
-
-    /**
-     * Set of features supported by the tool.
-     */
-    static enum Feature {
-        /**
-         * Insert EPICS data.
-         */
-        EPICS,
-        /**
-         * Insert scaler data.
-         */
-        SCALERS,
-        /**
-         * Insert run summary.
-         */
-        SUMMARY,
-        /**
-         * Insert trigger config.
-         */
-        TRIGGER_CONFIG
-    }
-
-    /**
-     * Initialize the logger.
-     */
-    private static final Logger LOGGER = Logger.getLogger(RunDatabaseCommandLine.class.getPackage().getName());
-
+public final class RunDatabaseCommandLine {
+        
     /**
      * Command line options for the crawler.
      */
-    private static final Options OPTIONS = new Options();
+    private static final Options OPTIONS = new Options();    
 
     /**
      * Statically define the command options.
      */
     static {
-        OPTIONS.addOption("f", "feature", true, "enable a feature");
-        OPTIONS.addOption("p", "connection-properties", true, "database connection properties file (required)");
         OPTIONS.addOption("h", "help", false, "print help and exit (overrides all other arguments)");
         OPTIONS.addOption("r", "run", true, "run to update");
-        OPTIONS.addOption("u", "update", false, "allow updating existing run in the database");
+        OPTIONS.addOption("p", "connection-properties", true, "database connection properties file (required)");       
+        OPTIONS.addOption("D", "dry-run", false, "dry run which will not update the database");
+        OPTIONS.addOption("x", "replace", false, "allow deleting and replacing an existing run");
+        OPTIONS.addOption("s", "spreadsheet", true, "path to run database spreadsheet (CSV format)");
+        OPTIONS.addOption("d", "detector", true, "conditions system detector name");        
+        OPTIONS.addOption("N", "no-evio-processing", false, "skip processing of all EVIO files");
+        OPTIONS.addOption("L", "load", false, "load back run information after inserting (for debugging)");
     }
 
     /**
@@ -79,113 +42,57 @@
      * @param args the command line arguments
      */
     public static void main(final String args[]) {
+        // Parse command line options and run the job.
         new RunDatabaseCommandLine().parse(args).run();
     }
-
+    
     /**
-     * Allow updating of the database for existing runs.
+     * Enable dry run which will not update the run database.
      */
-    private boolean allowUpdates = false;
-
+    private boolean dryRun = false;
+    
     /**
-     * The set of enabled features.
+     * Run number.
      */
-    private final Set<Feature> features = new HashSet<Feature>();
-
+    private int run;
+    
     /**
-     * The run manager for interacting with the run db.
+     * Path to spreadsheet CSV file.
      */
-    private RunManager runManager;
-
+    private File spreadsheetFile = null;
+    
     /**
-     * Create a run processor from the current configuration.
-     *
-     * @return the run processor
+     * Name of detector for conditions system (default for Eng Run 2015 provided here).
      */
-    private RunProcessor createEvioRunProcessor(final RunSummaryImpl runSummary, final List<File> files) {
-
-        final RunProcessor runProcessor = new RunProcessor(runSummary, files);
-
-        if (features.contains(Feature.EPICS)) {
-            runProcessor.addEpicsProcessor();
-        }
-        if (features.contains(Feature.SCALERS)) {
-            runProcessor.addScalerProcessor();
-        }
-        if (features.contains(Feature.TRIGGER_CONFIG)) {
-            runProcessor.addTriggerTimeProcessor();
-        }
-
-        return runProcessor;
-    }
-
+    private String detectorName = "HPS-EngRun2015-Nominal-v3";
+    
     /**
-     * Get the list of EVIO files for the run.
-     *
-     * @param run the run number
-     * @return the list of EVIO files from the run
+     * Allow replacement of existing records.
      */
-    private Map<File, Dataset> getEvioFiles(final int run) {
-        final DatacatClient datacatClient = new DatacatClientFactory().createClient();
-        final Set<String> metadata = new HashSet<String>();
-        final Map<File, Dataset> files = new HashMap<File, Dataset>();
-        metadata.add("runMin");
-        metadata.add("eventCount");
-        metadata.add("fileNumber");
-        metadata.add("endTimestamp");
-        metadata.add("startTimestamp");
-        metadata.add("hasEnd");
-        metadata.add("hasPrestart");
-        final List<Dataset> datasets = datacatClient.findDatasets("data/raw",
-                "fileFormat eq 'EVIO' AND dataType eq 'RAW' AND runMin eq " + run, metadata);
-        if (datasets.isEmpty()) {
-            throw new IllegalStateException("No EVIO datasets for run " + run + " were found in the data catalog.");
-        }
-        for (final Dataset dataset : datasets) {
-            files.put(new File(dataset.getLocations().get(0).getResource()), dataset);
-        }
-        return files;
-    }
-
+    private boolean replace = false;
+    
     /**
-     * Insert information for a run into the database.
-     *
-     * @param runManager the run manager for interacting with the run db
-     * @param runSummary the run summary with information about the run
+     * Skip full EVIO file processing.
      */
-    private void insertRun(final RunManager runManager, final RunSummary runSummary) {
-
-        final RunDatabaseDaoFactory runFactory = new RunDatabaseDaoFactory(runManager.getConnection());
-
-        // Add the run summary record.
-        if (this.features.contains(Feature.SUMMARY)) {
-            LOGGER.info("inserting run summary");
-            runFactory.createRunSummaryDao().insertRunSummary(runSummary);
-        }
-
-        if (this.features.contains(Feature.EPICS)) {
-            LOGGER.info("inserting EPICS data");
-            runFactory.createEpicsDataDao().insertEpicsData(runSummary.getEpicsData());
-        }
-
-        if (this.features.contains(Feature.SCALERS)) {
-            LOGGER.info("inserting scaler data");
-            runFactory.createScalerDataDao().insertScalerData(runSummary.getScalerData(), runManager.getRun());
-        }
-
-        if (this.features.contains(Feature.TRIGGER_CONFIG)) {
-            LOGGER.info("inserting trigger config");
-            runFactory.createTriggerConfigDao().insertTriggerConfig(runSummary.getTriggerConfig(), runManager.getRun());
-        }
-    }
-
+    private boolean skipEvioProcessing = false;
+    
     /**
-     * Parse command line options and return reference to <code>this</code>.
+     * Load back run information after insert (for debugging).
+     */
+    private boolean load = false;
+    
+    /**
+     * Database connection parameters.
+     */
+    private ConnectionParameters connectionParameters = null;
+    
+    /**
+     * Parse command line options and return reference to <code>this</code> object.
      *
      * @param args the command line arguments
      * @return reference to this object
      */
-    RunDatabaseCommandLine parse(final String args[]) {
+    private RunDatabaseCommandLine parse(final String args[]) {
         try {
             final CommandLine cl = new DefaultParser().parse(OPTIONS, args);
 
@@ -204,43 +111,52 @@
                     throw new IllegalArgumentException("Connection properties file " + dbPropFile.getPath()
                             + " does not exist.");
                 }
-                final ConnectionParameters connectionParameters = ConnectionParameters.fromProperties(dbPropFile);
-                LOGGER.config("using " + dbPropPath + " for db connection properties");
-
-                runManager = new RunManager(connectionParameters.createConnection());
-
+                connectionParameters = ConnectionParameters.fromProperties(dbPropFile);
             } else {
                 // Database connection properties file is required.
-                throw new RuntimeException("Connection properties are required.");
+                throw new RuntimeException("Connection properties are a required argument.");
             }
 
-            Integer run = null;
+            // Run number.
             if (cl.hasOption("r")) {
                 run = Integer.parseInt(cl.getOptionValue("r"));
             } else {
                 throw new RuntimeException("The run number is required.");
             }
-            runManager.setRun(run);
-
-            if (cl.hasOption("f")) {
-                // Enable individual features.
-                for (final String arg : cl.getOptionValues("f")) {
-                    features.add(Feature.valueOf(arg));
+            
+            // Dry run.
+            if (cl.hasOption("D")) {
+                this.dryRun = true;
+            }
+            
+            // Run spreadsheet.
+            if (cl.hasOption("s")) {
+                this.spreadsheetFile = new File(cl.getOptionValue("s"));
+                if (!this.spreadsheetFile.exists()) {
+                    throw new RuntimeException("The run spreadsheet " + this.spreadsheetFile.getPath() + " is inaccessible or does not exist.");
                 }
-            } else {
-                // By default all features are enabled.
-                features.addAll(Arrays.asList(Feature.values()));
             }
-            for (final Feature feature : features) {
-                LOGGER.config("feature " + feature.name() + " is enabled.");
+            
+            // Detector name.
+            if (cl.hasOption("d")) {
+                this.detectorName = cl.getOptionValue("d");
             }
-
-            // Allow updates to existing runs in the db.
-            if (cl.hasOption("u")) {
-                this.allowUpdates = true;
-                LOGGER.config("updating or replacing existing run data is enabled");
+            
+            // Replace existing run.
+            if (cl.hasOption("x")) {
+                this.replace = true;
             }
-
+            
+            // Skip full EVIO processing.
+            if (cl.hasOption("N")) {
+                this.skipEvioProcessing = true;
+            }
+            
+            // Load back run info at end of job.
+            if (cl.hasOption("L")) {
+                this.load = true;
+            }
+            
         } catch (final ParseException e) {
             throw new RuntimeException(e);
         }
@@ -249,90 +165,19 @@
     }
 
     /**
-     * Run the job to update the information in the run database.
+     * Configure the builder from command line options and run the job to update the database.
      */
     private void run() {
-
-        LOGGER.info("starting");
-
-        final boolean runExists = runManager.runExists();
-
-        // Fail if run exists and updates are not allowed.
-        if (runExists && !allowUpdates) {
-            throw new IllegalStateException("The run " + runManager.getRun()
-                    + " already exists and updates are not allowed.");
-        }
-
-        // Get the run number configured from command line.
-        final int run = runManager.getRun();
-
-        // Get the list of EVIO files for the run using a data catalog query.
-        final Map<File, Dataset> fileDatasets = this.getEvioFiles(run);
-        final List<File> files = new ArrayList<File>(fileDatasets.keySet());
-        EvioFileUtilities.sortBySequence(files);
-
-        // Process the run's files to get information.
-        final RunSummaryImpl runSummary = new RunSummaryImpl(run);
-        final RunProcessor runProcessor = this.createEvioRunProcessor(runSummary, files);
-        try {
-            runProcessor.processRun();
-        } catch (final Exception e) {
-            throw new RuntimeException(e);
-        }
-
-        // Set number of files from datacat query.
-        runSummary.setTotalFiles(files.size());
-
-        // Set run start date.
-        this.setStartDate(fileDatasets, files, runSummary);
-
-        // Set run end date.
-        this.setEndDate(fileDatasets, files, runSummary);
-
-        // Delete existing run.
-        if (runExists) {
-            runManager.deleteRun();
-        }
-
-        // Insert run into database.
-        this.insertRun(runManager, runSummary);
-
-        // Close the database connection.
-        runManager.closeConnection();
-
-        LOGGER.info("done");
+        new RunDatabaseBuilder()
+            .createRunSummary(run)
+            .setDetectorName(detectorName)
+            .setConnectionParameters(connectionParameters)
+            .setDryRun(dryRun)
+            .setReplace(replace)
+            .skipEvioProcessing(skipEvioProcessing)
+            .setSpreadsheetFile(spreadsheetFile)
+            .run()
+            .load(load);
     }
-
-    /**
-     * Set the run end date.
-     *
-     * @param fileDatasets the run's datasets
-     * @param files the run's EVIO files
-     * @param runSummary the run summary
-     */
-    private void setEndDate(final Map<File, Dataset> fileDatasets, final List<File> files,
-            final RunSummaryImpl runSummary) {
-        final Dataset lastDataset = fileDatasets.get(files.get(files.size() - 1));
-        final DatasetMetadata metadata = lastDataset.getMetadata();
-        // System.out.println("endTimestamp: " + metadata.getLong("endTimestamp"));
-        final Date endDate = new Date(metadata.getLong("endTimestamp"));
-        // System.out.println("endDate: " + startDate);
-        runSummary.setEndDate(endDate);
-        runSummary.setEndOkay(metadata.getLong("hasEnd") == 0 ? false : true);
-    }
-
-    /**
-     * Set the run start date.
-     *
-     * @param fileDatasets the run's datasets
-     * @param files the run's EVIO files
-     * @param runSummary the run summary
-     */
-    private void setStartDate(final Map<File, Dataset> fileDatasets, final List<File> files,
-            final RunSummaryImpl runSummary) {
-        final Dataset firstDataset = fileDatasets.get(files.get(0));
-        final DatasetMetadata metadata = firstDataset.getMetadata();
-        final Date startDate = new Date(metadata.getLong("startTimestamp"));
-        runSummary.setStartDate(startDate);
-    }
+        
 }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseDaoFactory.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseDaoFactory.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseDaoFactory.java	Tue Dec  1 15:55:47 2015
@@ -5,12 +5,8 @@
 
 /**
  * Factory for creating database API objects for interacting with the run database.
- * <p>
- * This allows the implementation classes to be package protected as only public interfaces are returned by this class.
  *
  * @author Jeremy McCormick, SLAC
- * @see EpicsDataDao
- * @see EpicsVariableDao
  */
 final class RunDatabaseDaoFactory {
 
@@ -73,13 +69,13 @@
     ScalerDataDao createScalerDataDao() {
         return new ScalerDataDaoImpl(connection);
     }
-
+    
     /**
-     * Get the trigger config DAO.
-     *
-     * @return the trigger config DAO
+     * Get the SVT config DAO.
+     * 
+     * @return the SVT config DAO
      */
-    TriggerConfigDao createTriggerConfigDao() {
-        return new TriggerConfigDaoImpl(connection);
+    SvtConfigDao createSvtConfigDao() {
+        return new SvtConfigDaoImpl(connection);
     }
 }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunManager.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunManager.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunManager.java	Tue Dec  1 15:55:47 2015
@@ -8,7 +8,7 @@
 import org.hps.conditions.database.ConnectionParameters;
 import org.hps.record.epics.EpicsData;
 import org.hps.record.scalers.ScalerData;
-import org.hps.record.triggerbank.TriggerConfig;
+import org.hps.record.svt.SvtConfigData;
 import org.lcsim.conditions.ConditionsEvent;
 import org.lcsim.conditions.ConditionsListener;
 
@@ -23,13 +23,11 @@
      * Simple class for caching data.
      */
     private class DataCache {
-
-        List<EpicsData> epicsData;
-        RunSummary fullRunSummary;
-        Boolean runExists;
-        RunSummary runSummary;
-        List<ScalerData> scalerData;
-        TriggerConfig triggerConfig;
+        Boolean runExists = null;
+        RunSummary runSummary = null;
+        List<EpicsData> epicsData = null;
+        List<ScalerData> scalerData = null;
+        List<SvtConfigData> svtConfigData = null;
     }
 
     /**
@@ -142,12 +140,16 @@
      *
      * @param run the run number
      */
-    public void deleteRun() {
-        // Create object for updating run info in the database.
-        final RunSummaryDao runSummaryDao = factory.createRunSummaryDao();
-
-        // Delete run from the database.
-        runSummaryDao.deleteFullRun(run);
+    void deleteRun() {
+        
+        factory.createEpicsDataDao().deleteEpicsData(EpicsType.EPICS_2s, run);
+        factory.createEpicsDataDao().deleteEpicsData(EpicsType.EPICS_20s, run);
+        
+        factory.createScalerDataDao().deleteScalerData(run);
+        
+        factory.createSvtConfigDao().deleteSvtConfigs(run);
+        
+        factory.createRunSummaryDao().deleteRunSummary(run);
     }
 
     /**
@@ -185,24 +187,11 @@
     }
 
     /**
-     * Get the full run summary for the current run including scaler data, etc.
-     *
-     * @return the full run summary for the current run
-     */
-    public RunSummary getFullRunSummary() {
-        this.checkRunNumber();
-        if (this.dataCache.fullRunSummary == null) {
-            this.dataCache.fullRunSummary = factory.createRunSummaryDao().readFullRunSummary(this.run);
-        }
-        return this.dataCache.fullRunSummary;
-    }
-
-    /**
      * Get the current run number.
      *
      * @return the run number
      */
-    public int getRun() {
+    public int getCurrentRun() {
         return run;
     }
 
@@ -214,16 +203,7 @@
     public List<Integer> getRuns() {
         return new RunSummaryDaoImpl(this.connection).getRuns();
     }
-
-    /**
-     * Get the full list of summaries for all runs in the database without complex data like EPICS records.
-     *
-     * @return the full list of run summaries
-     */
-    public List<RunSummary> getRunSummaries() {
-        return this.factory.createRunSummaryDao().getRunSummaries();
-    }
-
+  
     /**
      * Get the run summary for the current run not including its sub-objects like scaler data.
      *
@@ -250,39 +230,21 @@
         }
         return this.dataCache.scalerData;
     }
-
-    /**
-     * Get the trigger config for the current run.
-     *
-     * @return the trigger config for the current run
-     */
-    public TriggerConfig getTriggerConfig() {
-        this.checkRunNumber();
-        if (this.dataCache.triggerConfig == null) {
-            LOGGER.info("loading trigger config for run " + this.run);
-            this.dataCache.triggerConfig = factory.createTriggerConfigDao().getTriggerConfig(run);
-        }
-        return this.dataCache.triggerConfig;
-    }
-
-    /**
-     * Update the database with information found from crawling the files.
-     *
-     * @param runs the list of runs to update
-     * @throws SQLException if there is a database query error
-     */
-    public void insertRun(final RunSummary runSummary) throws SQLException {
-        LOGGER.info("updating run database for run " + runSummary.getRun());
-
-        // Create object for updating run info in the database.
-        final RunSummaryDao runSummaryDao = factory.createRunSummaryDao();
-
-        // Insert run summary into database.
-        runSummaryDao.insertFullRunSummary(runSummary);
-
-        LOGGER.info("done updating run database");
-    }
-
+    
+    /**
+     * Get SVT configuration data.
+     * 
+     * @return the SVT configuration data
+     */
+    public List<SvtConfigData> getSvtConfigData() {
+        this.checkRunNumber();
+        if (this.dataCache.svtConfigData == null) {
+            LOGGER.info("loading SVT configuration data for run " + this.run);
+            this.dataCache.svtConfigData = factory.createSvtConfigDao().getSvtConfigs(run);
+        }
+        return this.dataCache.svtConfigData;
+    }
+     
     /**
      * Open a new database connection from the connection parameters if the current one is closed or <code>null</code>.
      * <p>
@@ -307,7 +269,7 @@
     public boolean runExists() {
         this.checkRunNumber();
         if (this.dataCache.runExists == null) {
-            this.dataCache.runExists = factory.createRunSummaryDao().runSummaryExists(this.run);
+            this.dataCache.runExists = factory.createRunSummaryDao().runExists(this.run);
         }
         return this.dataCache.runExists;
     }
@@ -319,10 +281,7 @@
      * @return <code>true</code> if the run exists in the database
      */
     boolean runExists(final int run) {
-        if (this.dataCache.runExists == null) {
-            this.dataCache.runExists = factory.createRunSummaryDao().runSummaryExists(run);
-        }
-        return this.dataCache.runExists;
+        return factory.createRunSummaryDao().runExists(run);
     }
 
     /**

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummary.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummary.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummary.java	Tue Dec  1 15:55:47 2015
@@ -1,137 +1,154 @@
 package org.hps.run.database;
 
-import java.io.File;
 import java.util.Date;
-import java.util.List;
+import java.util.Map;
 
-import org.hps.datacat.client.DatasetFileFormat;
-import org.hps.record.epics.EpicsData;
-import org.hps.record.scalers.ScalerData;
-import org.hps.record.triggerbank.TriggerConfig;
+import org.hps.record.daqconfig.DAQConfig;
 
 /**
- * This is an API for accessing run summary information which is persisted as a row in the <i>runs</i> table of the run
- * database.
+ * This is an API for accessing run summary information which is persisted as a row in the <i>run_summaries</i> table.
  * <p>
- * This information includes:
- * <ul>
- * <li>run number</li>
- * <li>start date</li>
- * <li>end date</li>
- * <li>number of events</li>
- * <li>number of EVIO files</li>
- * <li>whether the END event was found indicating that the DAQ did not crash</li>
- * <li>whether the run is considered good (all <code>true</code> for now)</li>
- * </ul>
- * <p>
- * It also references several complex objects including lists of {@link org.hps.record.epics.EpicsData} and
- * {@link org.hps.record.scalers.ScalerData} for the run, as well as a list of EVIO files.
+ * All timestamp fields use the Unix convention (seconds since the epoch).
  *
+ * @author Jeremy McCormick, SLAC
  * @see RunSummaryImpl
  * @see RunSummaryDao
  * @see RunSummaryDaoImpl
  * @see RunManager
- * 
- * @author Jeremy McCormick, SLAC
  */
 public interface RunSummary {
-  
+
+    /*
+     * Mapping of trigger config fields to crate numbers.
+     */
+    public static final int TRIGGER_CONFIG1 = 37;
+    public static final int TRIGGER_CONFIG2 = 39;
+    public static final int TRIGGER_CONFIG3 = 46;
+    public static final int TRIGGER_CONFIG4 = 58;
+
     /**
-     * Get the creation date of this run record.
+     * Get the creation date of this record.
      *
-     * @return the creation date of this run record
+     * @return the creation date of this record
      */
     Date getCreated();
 
     /**
-     * Get the end date.
-     *
-     * @return the end date
+     * Get the trigger config.
+     * 
+     * @return the trigger config
      */
-    Date getEndDate();
+    DAQConfig getDAQConfig();
 
     /**
-     * Return <code>true</code> if END event was found in the data.
-     *
-     * @return <code>true</code> if END event was in the data
+     * Get the END event timestamp or the timestamp from the last head bank if END is not present.
+     * 
+     * @return the last event timestamp
      */
-    boolean getEndOkay();
+    Integer getEndTimestamp();
 
     /**
-     * Get the EPICS data from the run.
-     *
-     * @return the EPICS data from the run
+     * Get the GO event timestamp.
+     * 
+     * @return the GO event timestamp
      */
-    List<EpicsData> getEpicsData();
+    Integer getGoTimestamp();
 
     /**
-     * Get the event rate (effectively the trigger rate) which is the total events divided by the number of seconds in
-     * the run.
-     *
-     * @return the event rate
+     * Get the livetime computed from the clock scaler.
+     * 
+     * @return the livetime computed from the clock scaler
      */
-    double getEventRate();
+    Double getLivetimeClock();
+
+    /**
+     * Get the livetime computed from the FCUP_TDC scaler.
+     * 
+     * @return the livetime computed from the FCUP_TDC scaler
+     */
+    Double getLivetimeFcupTdc();
+
+    /**
+     * Get the livetime computed from the FCUP_TRG scaler.
+     * 
+     * @return the livetime computed from the FCUP_TRG scaler
+     */
+    Double getLivetimeFcupTrg();
+
+    /**
+     * Get the notes for the run (from the run spreadsheet).
+     * 
+     * @return the notes for the run
+     */
+    String getNotes();
+
+    /**
+     * Get the PRESTART event timestamp.
+     * 
+     * @return the PRESTART event timestamp
+     */
+    Integer getPrestartTimestamp();
 
     /**
      * Get the run number.
      *
      * @return the run number
      */
-    int getRun();
+    Integer getRun();
+   
+    /**
+     * Get the target setting for the run (string from run spreadsheet).
+     * 
+     * @return the target setting for the run
+     */
+    String getTarget();
 
     /**
-     * Return <code>true</code> if the run was okay (no major errors or data corruption occurred).
-     *
-     * @return <code>true</code> if the run was okay
+     * Get the TI time offset in ns.
+     * 
+     * @return the TI time offset in ns
      */
-    boolean getRunOkay();
+    Long getTiTimeOffset();
 
     /**
-     * Get the scaler data of this run.
+     * Get the total number of events in the run.
      *
-     * @return the scaler data of this run
+     * @return the total number of events in the run
      */
-    List<ScalerData> getScalerData();
+    Integer getTotalEvents();
 
     /**
-     * Get the trigger config int values.
+     * Get the total number of EVIO files in this run.
      *
-     * @return the trigger config int values
+     * @return the total number of files in this run
      */
-    TriggerConfig getTriggerConfig();
+    Integer getTotalFiles();
 
     /**
-     * Get the start date.
-     *
-     * @return the start date
+     * Get a map of crate number to trigger config data.
+     * 
+     * @return the map of crate number to trigger config data
      */
-    Date getStartDate();
+    Map<Integer, String> getTriggerConfigData();
 
     /**
-     * Get the total events in the run.
-     *
-     * @return the total events in the run
+     * Get the trigger config name (from the run spreadsheet).
+     * 
+     * @return the trigger config name
      */
-    int getTotalEvents();
+    String getTriggerConfigName();
 
     /**
-     * Get the total number of EVIO files for this run.
-     *
-     * @return the total number of files for this run
+     * Get the trigger rate in KHz.
+     * 
+     * @return the trigger rate in KHz
      */
-    int getTotalFiles();
+    Double getTriggerRate();
 
     /**
-     * Get the number of seconds in the run which is the difference between the start and end times.
+     * Get the date when this record was last updated.
      *
-     * @return the total seconds in the run
-     */
-    long getTotalSeconds();
-
-    /**
-     * Get the date when this run record was last updated.
-     *
-     * @return the date when this run record was last updated
+     * @return the date when this record was last updated
      */
     Date getUpdated();
 }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDao.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDao.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDao.java	Tue Dec  1 15:55:47 2015
@@ -8,14 +8,7 @@
  * @author Jeremy McCormick, SLAC
  */
 interface RunSummaryDao {
-
-    /**
-     * Delete a run summary from the database including its referenced objects such as EPICS data.
-     *
-     * @param runSummary the run summary to delete
-     */
-    void deleteFullRun(int run);
-
+  
     /**
      * Delete a run summary by run number.
      *
@@ -36,14 +29,7 @@
      * @return the list of run numbers
      */
     List<Integer> getRuns();
-
-    /**
-     * Get a list of run summaries without loading their objects such as EPICS data.
-     *
-     * @return the list of run summaries
-     */
-    List<RunSummary> getRunSummaries();
-
+  
     /**
      * Get a run summary by run number without loading object state.
      *
@@ -51,36 +37,13 @@
      * @return the run summary object
      */
     RunSummary getRunSummary(int run);
-
+  
     /**
-     * Insert a list of run summaries along with its referenced objects such as scaler and EPICS data.
-     *
-     * @param runSummaryList the list of run summaries
-     * @param deleteExisting <code>true</code> to allow deletion and replacement of existing run summaries
-     */
-    void insertFullRunSummaries(List<RunSummary> runSummaryList, boolean deleteExisting);
-
-    /**
-     * Insert a run summary including all its objects.
-     *
-     * @param runSummary the run summary object
-     */
-    void insertFullRunSummary(RunSummary runSummary);
-
-    /**
-     * Insert a run summary but not its objects.
+     * Insert a run summary.
      *
      * @param runSummary the run summary object
      */
     void insertRunSummary(RunSummary runSummary);
-
-    /**
-     * Read a run summary and its objects such as scaler data.
-     *
-     * @param run the run number
-     * @return the full run summary
-     */
-    RunSummary readFullRunSummary(int run);
 
     /**
      * Return <code>true</code> if a run summary exists in the database.
@@ -88,12 +51,12 @@
      * @param run the run number
      * @return <code>true</code> if <code>run</code> exists in the database
      */
-    boolean runSummaryExists(int run);
+    boolean runExists(int run);
 
     /**
-     * Update a run summary but not its objects.
+     * Update a run summary.
      *
      * @param runSummary the run summary to update
      */
-    void updateRunSummary(RunSummary runSummary);
+    void updateRunSummary(RunSummary runSummary);    
 }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDaoImpl.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDaoImpl.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryDaoImpl.java	Tue Dec  1 15:55:47 2015
@@ -1,17 +1,15 @@
 package org.hps.run.database;
 
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.TimeZone;
+import java.util.Map;
 import java.util.logging.Logger;
-
-import org.hps.record.epics.EpicsData;
 
 /**
  * Implementation of database operations for {@link RunSummary} objects in the run database.
@@ -21,36 +19,35 @@
 final class RunSummaryDaoImpl implements RunSummaryDao {
 
     /**
-     * SQL query strings.
-     */
-    private static final class RunSummaryQuery {
-
-        /**
-         * Delete by run number.
-         */
-        private static final String DELETE_RUN = "DELETE FROM runs WHERE run = ?";
-        /**
-         * Insert a record for a run.
-         */
-        private static final String INSERT = "INSERT INTO runs (run, start_date, end_date, nevents, nfiles, end_ok, created) VALUES(?, ?, ?, ?, ?, ?, NOW())";
-        /**
-         * Select all records.
-         */
-        private static final String SELECT_ALL = "SELECT * from runs";
-        /**
-         * Select record by run number.
-         */
-        private static final String SELECT_RUN = "SELECT run, start_date, end_date, nevents, nfiles, end_ok, run_ok, updated, created FROM runs WHERE run = ?";
-        /**
-         * Update information for a run.
-         */
-        private static final String UPDATE_RUN = "UPDATE runs SET start_date, end_date, nevents, nfiles, end_ok, run_ok WHERE run = ?";
-    }
-
-    /**
-     * Eastern time zone.
-     */
-    private static Calendar CALENDAR = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+     * Expected number of string banks in trigger config.
+     */
+    private static final int TRIGGER_CONFIG_LEN = 4;
+
+    /**
+     * Delete by run number.
+     */
+    private static final String DELETE = "DELETE FROM run_summaries WHERE run = ?";
+        
+    /**
+     * Insert a record for a run.
+     */
+    private static final String INSERT = "INSERT INTO run_summaries (run, nevents, nfiles, prestart_timestamp,"
+            + " go_timestamp, end_timestamp, trigger_rate, trigger_config_name, trigger_config1, trigger_config2," 
+            + " trigger_config3, trigger_config4, ti_time_offset, livetime_clock, livetime_fcup_tdc, livetime_fcup_trg,"
+            + " target, notes, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
+                     
+    /**
+     * Select record by run number.
+     */
+    private static final String SELECT = "SELECT * FROM run_summaries WHERE run = ?";
+        
+    /**
+     * Update information for a run.
+     */
+    private static final String UPDATE = "UPDATE run_summaries SET nevents = ?, nfiles = ?, prestart_timestamp = ?,"
+            + " go_timestamp = ?, end_timestamp = ?, trigger_rate = ?, trigger_config_name = ?, trigger_config1 = ?,"
+            + " trigger_config2 = ?, trigger_config3 = ?, trigger_config4 = ?, ti_time_offset = ?, livetime_clock = ?,"
+            + " livetime_fcup_tdc = ?, livetime_fcup_trg = ?, target = ?, notes = ?, created WHERE run = ?";
 
     /**
      * Initialize the logger.
@@ -61,60 +58,24 @@
      * The database connection.
      */
     private final Connection connection;
-
-    /**
-     * The database API for EPICS data.
-     */
-    private EpicsDataDao epicsDataDao = null;
-
-    /**
-     * The database API for scaler data.
-     */
-    private ScalerDataDao scalerDataDao = null;
-
-    /**
-     * The database API for integer trigger config.
-     */
-    private TriggerConfigDao triggerConfigIntDao = null;
-
+  
     /**
      * Create a new DAO object for run summary information.
      *
      * @param connection the database connection
      */
     RunSummaryDaoImpl(final Connection connection) {
-        // Set the connection.
         if (connection == null) {
             throw new IllegalArgumentException("The connection is null.");
         }
+        try {
+            if (connection.isClosed()) {
+                throw new IllegalArgumentException("The connection is closed.");
+            }
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
         this.connection = connection;
-
-        // Setup DAO API objects for managing complex object state.
-        epicsDataDao = new EpicsDataDaoImpl(this.connection);
-        scalerDataDao = new ScalerDataDaoImpl(this.connection);
-        triggerConfigIntDao = new TriggerConfigDaoImpl(this.connection);
-    }
-
-    /**
-     * Delete a run from the database including its referenced objects such as EPICS data.
-     *
-     * @param runSummary the run summary to delete
-     */
-    @Override
-    public void deleteFullRun(int run) {
-
-        // Delete EPICS log.
-        this.epicsDataDao.deleteEpicsData(EpicsType.EPICS_1S, run);
-        this.epicsDataDao.deleteEpicsData(EpicsType.EPICS_10S, run);
-
-        // Delete scaler data.
-        this.scalerDataDao.deleteScalerData(run);
-
-        // Delete trigger config.
-        this.triggerConfigIntDao.deleteTriggerConfigInt(run);
-
-        // Finally delete the run summary information.
-        this.deleteRunSummary(run);
     }
 
     /**
@@ -126,7 +87,7 @@
     public void deleteRunSummary(final int run) {
         PreparedStatement preparedStatement = null;
         try {
-            preparedStatement = connection.prepareStatement(RunSummaryQuery.DELETE_RUN);
+            preparedStatement = connection.prepareStatement(DELETE);
             preparedStatement.setInt(1, run);
             preparedStatement.executeUpdate();
         } catch (final SQLException e) {
@@ -151,7 +112,7 @@
     public void deleteRunSummary(final RunSummary runSummary) {
         PreparedStatement preparedStatement = null;
         try {
-            preparedStatement = connection.prepareStatement(RunSummaryQuery.DELETE_RUN);
+            preparedStatement = connection.prepareStatement(DELETE);
             preparedStatement.setInt(1, runSummary.getRun());
             preparedStatement.executeUpdate();
         } catch (final SQLException e) {
@@ -177,7 +138,7 @@
         final List<Integer> runs = new ArrayList<Integer>();
         PreparedStatement preparedStatement = null;
         try {
-            preparedStatement = this.connection.prepareStatement("SELECT distinct(run) FROM runs ORDER BY run");
+            preparedStatement = this.connection.prepareStatement("SELECT distinct(run) FROM run_summaries ORDER BY run");
             final ResultSet resultSet = preparedStatement.executeQuery();
             while (resultSet.next()) {
                 final Integer run = resultSet.getInt(1);
@@ -196,47 +157,9 @@
         }
         return runs;
     }
-
-    /**
-     * Get a list of run summaries without loading their objects such as EPICS data.
-     *
-     * @return the list of run summaries
-     */
-    @Override
-    public List<RunSummary> getRunSummaries() {
-        PreparedStatement statement = null;
-        final List<RunSummary> runSummaries = new ArrayList<RunSummary>();
-        try {
-            statement = this.connection.prepareStatement(RunSummaryQuery.SELECT_ALL);
-            final ResultSet resultSet = statement.executeQuery();
-            while (resultSet.next()) {
-                final RunSummaryImpl runSummary = new RunSummaryImpl(resultSet.getInt("run"));
-                runSummary.setStartDate(resultSet.getTimestamp("start_date"));
-                runSummary.setEndDate(resultSet.getTimestamp("end_date"));
-                runSummary.setTotalEvents(resultSet.getInt("nevents"));
-                runSummary.setTotalFiles(resultSet.getInt("nfiles"));
-                runSummary.setEndOkay(resultSet.getBoolean("end_ok"));
-                runSummary.setRunOkay(resultSet.getBoolean("run_ok"));
-                runSummary.setUpdated(resultSet.getTimestamp("updated"));
-                runSummary.setCreated(resultSet.getTimestamp("created"));
-                runSummaries.add(runSummary);
-            }
-        } catch (final SQLException e) {
-            throw new RuntimeException(e);
-        } finally {
-            if (statement != null) {
-                try {
-                    statement.close();
-                } catch (final SQLException e) {
-                    e.printStackTrace();
-                }
-            }
-        }
-        return runSummaries;
-    }
-
-    /**
-     * Get a run summary by run number without loading object state.
+   
+    /**
+     * Get a run summary.
      *
      * @param run the run number
      * @return the run summary object
@@ -246,22 +169,32 @@
         PreparedStatement statement = null;
         RunSummaryImpl runSummary = null;
         try {
-            statement = this.connection.prepareStatement(RunSummaryQuery.SELECT_RUN);
+            statement = this.connection.prepareStatement(SELECT);
             statement.setInt(1, run);
             final ResultSet resultSet = statement.executeQuery();
             if (!resultSet.next()) {
-                throw new IllegalArgumentException("No record exists for run " + run + " in database.");
-            }
-
+                throw new IllegalArgumentException("Run " + run + " does not exist in database.");
+            }
             runSummary = new RunSummaryImpl(run);
-            runSummary.setStartDate(resultSet.getTimestamp("start_date"));
-            runSummary.setEndDate(resultSet.getTimestamp("end_date"));
             runSummary.setTotalEvents(resultSet.getInt("nevents"));
             runSummary.setTotalFiles(resultSet.getInt("nfiles"));
-            runSummary.setEndOkay(resultSet.getBoolean("end_ok"));
-            runSummary.setRunOkay(resultSet.getBoolean("run_ok"));
+            runSummary.setPrestartTimestamp(resultSet.getInt("prestart_timestamp"));
+            runSummary.setGoTimestamp(resultSet.getInt("go_timestamp"));
+            runSummary.setEndTimestamp(resultSet.getInt("end_timestamp"));
+            runSummary.setTriggerRate(resultSet.getDouble("trigger_rate"));
+            runSummary.setTriggerConfigName(resultSet.getString("trigger_config_name"));
+            Map<Integer, String> triggerConfigData = createTriggerConfigData(resultSet);
+            if (!triggerConfigData.isEmpty()) {
+                runSummary.setTriggerConfigData(triggerConfigData);
+            } 
+            runSummary.setTiTimeOffset(resultSet.getLong("ti_time_offset"));
+            runSummary.setLivetimeClock(resultSet.getDouble("livetime_clock"));
+            runSummary.setLivetimeFcupTdc(resultSet.getDouble("livetime_fcup_tdc"));
+            runSummary.setLivetimeFcupTrg(resultSet.getDouble("livetime_fcup_trg"));
+            runSummary.setTarget(resultSet.getString("target"));
+            runSummary.setNotes(resultSet.getString("notes"));
+            runSummary.setCreated(resultSet.getTimestamp("created"));
             runSummary.setUpdated(resultSet.getTimestamp("updated"));
-            runSummary.setCreated(resultSet.getTimestamp("created"));
         } catch (final SQLException e) {
             throw new RuntimeException(e);
         } finally {
@@ -277,182 +210,98 @@
     }
 
     /**
-     * Insert a list of run summaries along with their complex state such as referenced scaler and EPICS data.
-     *
-     * @param runSummaryList the list of run summaries
-     * @param deleteExisting <code>true</code> to allow deletion and replacement of existing run summaries
-     */
-    @Override
-    public void insertFullRunSummaries(final List<RunSummary> runSummaryList, final boolean deleteExisting) {
-
-        if (runSummaryList == null) {
-            throw new IllegalArgumentException("The run summary list is null.");
-        }
-        if (runSummaryList.isEmpty()) {
-            throw new IllegalArgumentException("The run summary list is empty.");
-        }
-
-        LOGGER.info("inserting " + runSummaryList.size() + " run summaries into database");
-
-        // Turn off auto commit.
-        try {
-            LOGGER.info("turning off auto commit");
-            this.connection.setAutoCommit(false);
-        } catch (final SQLException e) {
-            throw new RuntimeException(e);
-        }
-
-        // Loop over all runs found while crawling.
-        for (final RunSummary runSummary : runSummaryList) {
-
-            final int run = runSummary.getRun();
-
-            LOGGER.info("inserting run summary for run " + run + " into database");
-
-            // Does the run exist in the database already?
-            if (this.runSummaryExists(run)) {
-                // Is deleting existing rows allowed?
-                if (deleteExisting) {
-                    LOGGER.info("deleting existing run summary");
-                    // Delete the existing rows.
-                    this.deleteFullRun(runSummary.getRun());
-                } else {
-                    // Rows exist but updating is disallowed which is a fatal error.
-                    throw new IllegalStateException("Run " + runSummary.getRun()
-                            + " already exists and updates are disallowed.");
-                }
-            }
-
-            // Insert full run summary information including sub-objects.
-            LOGGER.info("inserting run summary");
-            this.insertFullRunSummary(runSummary);
-            LOGGER.info("run summary for " + run + " inserted successfully");
-
-            try {
-                // Commit the transaction for the run.
-                LOGGER.info("committing transaction");
-                this.connection.commit();
-            } catch (final SQLException e1) {
-                try {
-                    LOGGER.severe("rolling back transaction");
-                    // Rollback the transaction if there was an error.
-                    this.connection.rollback();
-                } catch (final SQLException e2) {
-                    throw new RuntimeException(e2);
-                }
-            }
-
-            LOGGER.info("done inserting run summary " + run);
-        }
-
-        try {
-            LOGGER.info("turning auto commit on");
-            // Turn auto commit back on.
-            this.connection.setAutoCommit(true);
-        } catch (final SQLException e) {
-            e.printStackTrace();
-        }
-
-        LOGGER.info("done inserting run summaries");
-    }
-
-    /**
-     * Insert a run summary including all its objects.
-     *
-     * @param runSummary the run summary object to insert
-     */
-    @Override
-    public void insertFullRunSummary(final RunSummary runSummary) {
-
-        if (runSummary == null) {
-            throw new IllegalArgumentException("The run summary is null.");
-        }
-        
-        // Insert basic run log info.
-        this.insertRunSummary(runSummary);
-
-        // Insert EPICS data.
-        if (runSummary.getEpicsData() != null && !runSummary.getEpicsData().isEmpty()) {
-            LOGGER.info("inserting " + runSummary.getEpicsData().size() + " EPICS records");
-            epicsDataDao.insertEpicsData(runSummary.getEpicsData());
+     * Create trigger config data from result set.
+     * 
+     * @param resultSet the result set with the run summary record
+     * @return the trigger config data as a map of bank number to string data
+     * @throws SQLException if there is an error querying the database
+     */
+    private Map<Integer, String> createTriggerConfigData(final ResultSet resultSet) throws SQLException {
+        Map<Integer, String> triggerConfigData = new LinkedHashMap<Integer, String>();
+        Clob clob = resultSet.getClob("trigger_config1");            
+        if (clob != null) {
+            triggerConfigData.put(RunSummary.TRIGGER_CONFIG1, clob.getSubString(1, (int) clob.length()));
+        }
+        clob = resultSet.getClob("trigger_config2");
+        if (clob != null) {
+            triggerConfigData.put(RunSummary.TRIGGER_CONFIG2, clob.getSubString(1, (int) clob.length()));
+        }
+        clob = resultSet.getClob("trigger_config3");
+        if (clob != null) {
+            triggerConfigData.put(RunSummary.TRIGGER_CONFIG3, clob.getSubString(1, (int) clob.length()));
+        }
+        clob = resultSet.getClob("trigger_config4");
+        if (clob != null) {
+            triggerConfigData.put(RunSummary.TRIGGER_CONFIG4, clob.getSubString(1, (int) clob.length()));
+        }
+        return triggerConfigData;
+    }
+      
+    /**
+     * Insert a run summary.
+     *
+     * @param runSummary the run summary object
+     */
+    @Override
+    public void insertRunSummary(final RunSummary runSummary) {
+        PreparedStatement preparedStatement = null;        
+        try {
+            preparedStatement = connection.prepareStatement(INSERT);                       
+            preparedStatement.setInt(1, runSummary.getRun());
+            preparedStatement.setInt(2, runSummary.getTotalEvents());
+            preparedStatement.setInt(3, runSummary.getTotalFiles());
+            preparedStatement.setInt(4, runSummary.getPrestartTimestamp());
+            preparedStatement.setInt(5, runSummary.getGoTimestamp());
+            preparedStatement.setInt(6, runSummary.getEndTimestamp());
+            preparedStatement.setDouble(7, runSummary.getTriggerRate());
+            preparedStatement.setString(8, runSummary.getTriggerConfigName());
+            Map<Integer, String> triggerData = runSummary.getTriggerConfigData();
+            prepareTriggerData(preparedStatement, triggerData);
+            preparedStatement.setLong(13, runSummary.getTiTimeOffset());
+            preparedStatement.setDouble(14, runSummary.getLivetimeClock());
+            preparedStatement.setDouble(15, runSummary.getLivetimeFcupTdc());
+            preparedStatement.setDouble(16, runSummary.getLivetimeFcupTrg());
+            preparedStatement.setString(17, runSummary.getTarget());
+            preparedStatement.setString(18, runSummary.getNotes());
+            LOGGER.fine(preparedStatement.toString());
+            preparedStatement.executeUpdate();
+        } catch (final SQLException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                } catch (final SQLException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * Set trigger config data on prepared statement.
+     * @param preparedStatement the prepared statement
+     * @param triggerData the trigger config data
+     * @throws SQLException if there is an error querying the database
+     */
+    private void prepareTriggerData(PreparedStatement preparedStatement, Map<Integer, String> triggerData)
+            throws SQLException {
+        if (triggerData != null && !triggerData.isEmpty()) {
+            if (triggerData.size() != TRIGGER_CONFIG_LEN) {
+                throw new IllegalArgumentException("The trigger config data has the wrong length.");
+            }
+            preparedStatement.setBytes(9, triggerData.get(RunSummary.TRIGGER_CONFIG1).getBytes());
+            preparedStatement.setBytes(10, triggerData.get(RunSummary.TRIGGER_CONFIG2).getBytes());
+            preparedStatement.setBytes(11, triggerData.get(RunSummary.TRIGGER_CONFIG3).getBytes());
+            preparedStatement.setBytes(12, triggerData.get(RunSummary.TRIGGER_CONFIG4).getBytes());
         } else {
-            LOGGER.warning("no EPICS data to insert");
-        }
-
-        // Insert scaler data.
-        if (runSummary.getScalerData() != null && !runSummary.getScalerData().isEmpty()) {
-            LOGGER.info("inserting " + runSummary.getScalerData().size() + " scaler data records");
-            scalerDataDao.insertScalerData(runSummary.getScalerData(), runSummary.getRun());
-        } else {
-            LOGGER.warning("no scaler data to insert");
-        }
-
-        // Insert trigger config.
-        if (runSummary.getTriggerConfig() != null && !runSummary.getTriggerConfig().isEmpty()) {
-            LOGGER.info("inserting " + runSummary.getTriggerConfig().size() + " trigger config variables");
-            triggerConfigIntDao.insertTriggerConfig(runSummary.getTriggerConfig(), runSummary.getRun());
-        } else {
-            LOGGER.warning("no trigger config to insert");
-        }
-    }
-
-    /**
-     * Insert a run summary but not its objects.
-     *
-     * @param runSummary the run summary object
-     */
-    @Override
-    public void insertRunSummary(final RunSummary runSummary) {
-        PreparedStatement preparedStatement = null;
-        try {
-            preparedStatement = connection.prepareStatement(RunSummaryQuery.INSERT);
-            preparedStatement.setInt(1, runSummary.getRun());
-            preparedStatement.setTimestamp(2, new java.sql.Timestamp(runSummary.getStartDate().getTime()), CALENDAR);
-            preparedStatement.setTimestamp(3, new java.sql.Timestamp(runSummary.getEndDate().getTime()), CALENDAR);
-            preparedStatement.setInt(4, runSummary.getTotalEvents());
-            preparedStatement.setInt(5, runSummary.getTotalFiles());
-            preparedStatement.setBoolean(6, runSummary.getEndOkay());
-            preparedStatement.executeUpdate();
-        } catch (final SQLException e) {
-            throw new RuntimeException(e);
-        } finally {
-            if (preparedStatement != null) {
-                try {
-                    preparedStatement.close();
-                } catch (final SQLException e) {
-                    e.printStackTrace();
-                }
-            }
-        }
-    }
-
-    /**
-     * Read a run summary and its objects such as scaler data.
-     *
-     * @param run the run number
-     * @return the full run summary
-     */
-    @Override
-    public RunSummary readFullRunSummary(final int run) {
-
-        // Read main run summary but not referenced objects.
-        final RunSummaryImpl runSummary = (RunSummaryImpl) this.getRunSummary(run);
-
-        // Read EPICS data and set on RunSummary.
-        final List<EpicsData> epicsDataList = new ArrayList<EpicsData>();
-        epicsDataList.addAll(epicsDataDao.getEpicsData(EpicsType.EPICS_1S, run));
-        epicsDataList.addAll(epicsDataDao.getEpicsData(EpicsType.EPICS_10S, run));
-        runSummary.setEpicsData(epicsDataList);
-
-        // Read scaler data and set on RunSummary.
-        runSummary.setScalerData(scalerDataDao.getScalerData(run));
-
-        // Read trigger config.
-        runSummary.setTriggerConfig(triggerConfigIntDao.getTriggerConfig(run));
-
-        return runSummary;
-    }
-
+            preparedStatement.setBytes(9, null);
+            preparedStatement.setBytes(10, null);
+            preparedStatement.setBytes(11, null);
+            preparedStatement.setBytes(12, null);
+        }
+    }
+   
     /**
      * Return <code>true</code> if a run summary exists in the database for the run number.
      *
@@ -460,10 +309,10 @@
      * @return <code>true</code> if run exists in the database
      */
     @Override
-    public boolean runSummaryExists(final int run) {
-        PreparedStatement preparedStatement = null;
-        try {
-            preparedStatement = connection.prepareStatement("SELECT run FROM runs where run = ?");
+    public boolean runExists(final int run) {
+        PreparedStatement preparedStatement = null;
+        try {
+            preparedStatement = connection.prepareStatement("SELECT run FROM run_summaries where run = ?");
             preparedStatement.setInt(1, run);
             final ResultSet rs = preparedStatement.executeQuery();
             return rs.first();
@@ -481,7 +330,7 @@
     }
 
     /**
-     * Update a run summary but not its complex state.
+     * Update a run summary.
      *
      * @param runSummary the run summary to update
      */
@@ -489,14 +338,37 @@
     public void updateRunSummary(final RunSummary runSummary) {
         PreparedStatement preparedStatement = null;
         try {
-            preparedStatement = connection.prepareStatement(RunSummaryQuery.UPDATE_RUN);
-            preparedStatement.setTimestamp(1, new java.sql.Timestamp(runSummary.getStartDate().getTime()), CALENDAR);
-            preparedStatement.setTimestamp(2, new java.sql.Timestamp(runSummary.getEndDate().getTime()), CALENDAR);
-            preparedStatement.setInt(3, runSummary.getTotalEvents());
-            preparedStatement.setInt(4, runSummary.getTotalFiles());
-            preparedStatement.setBoolean(5, runSummary.getEndOkay());
-            preparedStatement.setBoolean(6, runSummary.getRunOkay());
-            preparedStatement.setInt(7, runSummary.getRun());
+            preparedStatement = connection.prepareStatement(UPDATE);                       
+            preparedStatement.setInt(1, runSummary.getTotalEvents());
+            preparedStatement.setInt(2, runSummary.getTotalFiles());
+            preparedStatement.setInt(3, runSummary.getPrestartTimestamp());
+            preparedStatement.setInt(4, runSummary.getGoTimestamp());
+            preparedStatement.setInt(5, runSummary.getEndTimestamp());
+            preparedStatement.setDouble(6, runSummary.getTriggerRate());
+            preparedStatement.setString(7, runSummary.getTriggerConfigName());
+            Map<Integer, String> triggerData = runSummary.getTriggerConfigData();
+            if (triggerData != null && !triggerData.isEmpty()) {
+                if (triggerData.size() != 4) {
+                    throw new IllegalArgumentException("The trigger config data has the wrong length.");
+                }
+                preparedStatement.setBytes(8, triggerData.get(RunSummary.TRIGGER_CONFIG1).getBytes());
+                preparedStatement.setBytes(9, triggerData.get(RunSummary.TRIGGER_CONFIG2).getBytes());
+                preparedStatement.setBytes(10, triggerData.get(RunSummary.TRIGGER_CONFIG3).getBytes());
+                preparedStatement.setBytes(11, triggerData.get(RunSummary.TRIGGER_CONFIG4).getBytes());
+            } else {
+                preparedStatement.setBytes(8, null);
+                preparedStatement.setBytes(9, null);
+                preparedStatement.setBytes(10, null);
+                preparedStatement.setBytes(11, null);
+            }
+            preparedStatement.setLong(12, runSummary.getTiTimeOffset());
+            preparedStatement.setDouble(13, runSummary.getLivetimeClock());
+            preparedStatement.setDouble(14, runSummary.getLivetimeFcupTdc());
+            preparedStatement.setDouble(15, runSummary.getLivetimeFcupTrg());
+            preparedStatement.setString(16, runSummary.getTarget());
+            preparedStatement.setString(17, runSummary.getNotes());
+            preparedStatement.setInt(18, runSummary.getRun());
+            LOGGER.fine(preparedStatement.toString());
             preparedStatement.executeUpdate();
         } catch (final SQLException e) {
             throw new RuntimeException(e);
@@ -509,5 +381,5 @@
                 }
             }
         }
-    }
+    }      
 }

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryImpl.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryImpl.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunSummaryImpl.java	Tue Dec  1 15:55:47 2015
@@ -1,40 +1,19 @@
 package org.hps.run.database;
 
-import java.io.File;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.TimeZone;
-
-import org.hps.datacat.client.DatasetFileFormat;
-import org.hps.record.epics.EpicsData;
-import org.hps.record.scalers.ScalerData;
-import org.hps.record.triggerbank.TriggerConfig;
+import java.util.Map.Entry;
+
+import org.hps.record.daqconfig.ConfigurationManager;
+import org.hps.record.daqconfig.DAQConfig;
+import org.hps.record.daqconfig.EvioDAQParser;
 
 /**
  * Implementation of {@link RunSummary} for retrieving information from the run database.
  *
  * @author Jeremy McCormick, SLAC
  */
-public final class RunSummaryImpl implements RunSummary {
-
-    /**
-     * Default date display format.
-     */
-    private static final DateFormat DATE_DISPLAY = new SimpleDateFormat();
-
-    static {
-        /**
-         * Set default time zone for display to East Coast (JLAB) where data was
-         * taken.
-         */
-        DATE_DISPLAY.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("America/New_York")));
-    }
+final class RunSummaryImpl implements RunSummary {
 
     /**
      * Date this record was created.
@@ -42,60 +21,90 @@
     private Date created;
 
     /**
-     * End date of run.
-     */
-    private Date endDate;
-
-    /**
-     * This is <code>true</code> if the END event is found in the data.
-     */
-    private boolean endOkay;
-
-    /**
-     * The EPICS data from the run.
-     */
-    private List<EpicsData> epicsDataList;
+     * DAQ config object built from string data.
+     */
+    private DAQConfig daqConfig;
+
+    /**
+     * Timestamp of END event.
+     */
+    private Integer endTimestamp;
+
+    /**
+     * Timestamp of GO event.
+     */
+    private Integer goTimestamp;
+
+    /**
+     * Clock livetime calculation.
+     */
+    private Double livetimeClock;
+
+    /**
+     * FCup TDC livetime calculation.
+     */
+    private Double livetimeTdc;
+
+    /**
+     * FCup TRG livetime calculation.
+     */
+    private Double livetimeTrg;
+
+    /**
+     * Notes about the run (from spreadsheet).
+     */
+    private String notes;
+
+    /**
+     * Timestamp of PRESTART event.
+     */
+    private Integer prestartTimestamp;
 
     /**
      * The run number.
      */
-    private final int run;
-
-    /**
-     * Flag to indicate run was okay.
-     */
-    private boolean runOkay = true;
-
-    /**
-     * The scaler data for the run.
-     */
-    private List<ScalerData> scalerDataList;
-
-    /**
-     * The trigger data for the run.
-     */
-    private TriggerConfig triggerConfig;
-
-    /**
-     * Start date of run.
-     */
-    private Date startDate;
+    private final Integer run;
+
+    /**
+     * Target setup (string from run spreadsheet).
+     */
+    private String target;
+
+    /**
+     * TI time offset in ns.
+     */
+    private Long tiTimeOffset;
 
     /**
      * The total events found in the run across all files.
      */
-    private int totalEvents = -1;
+    private Integer totalEvents;
 
     /**
      * The total number of files in the run.
      */
-    private int totalFiles = 0;
+    private Integer totalFiles;
+
+    /**
+     * Map of crate number to trigger config string data.
+     */
+    private Map<Integer, String> triggerConfigData;
+
+    /**
+     * Get the name of the trigger config file.
+     */
+    private String triggerConfigName;
+
+    /**
+     * Trigger rate in KHz.
+     */
+    private double triggerRate;
 
     /**
      * Date when the run record was last updated.
      */
     private Date updated;
-    
+
     /**
      * Create a run summary.
      *
@@ -105,209 +114,157 @@
         this.run = run;
     }
 
-    /**
-     * Get the creation date of this run record.
-     *
-     * @return the creation date of this run record
-     */
+    @Override
     public Date getCreated() {
         return this.created;
     }
 
-    /**
-     * Get the end date.
-     *
-     * @return the end date
-     */
-    public Date getEndDate() {
-        return endDate;
-    }
-
-    /**
-     * Return <code>true</code> if END event was found in the data.
-     *
-     * @return <code>true</code> if END event was in the data
-     */
-    public boolean getEndOkay() {
-        return this.endOkay;
-    }
-
-    /**
-     * Get the EPICS data from the run.
-     *
-     * @return the EPICS data from the run
-     */
-    public List<EpicsData> getEpicsData() {
-        return this.epicsDataList;
-    }
-
-    /**
-     * Get the event rate (effectively the trigger rate) which is the total
-     * events divided by the number of seconds in the run.
-     *
-     * @return the event rate
-     */
-    public double getEventRate() {
-        if (this.getTotalEvents() <= 0) {
-            throw new RuntimeException("Total events is zero or invalid.");
-        }
-        return (double) this.getTotalEvents() / (double) this.getTotalSeconds();
-    }
-
-    /**
-     * Get the run number.
-     *
-     * @return the run number
-     */
-    public int getRun() {
+    @Override
+    public DAQConfig getDAQConfig() {
+        return this.daqConfig;
+    }
+
+    @Override
+    public Integer getEndTimestamp() {
+        return endTimestamp;
+    }
+
+    @Override
+    public Integer getGoTimestamp() {
+        return goTimestamp;
+    }
+
+    @Override
+    public Double getLivetimeClock() {
+        return this.livetimeClock;
+    }
+
+    @Override
+    public Double getLivetimeFcupTdc() {
+        return this.livetimeTdc;
+    }
+
+    @Override
+    public Double getLivetimeFcupTrg() {
+        return this.livetimeTrg;
+    }
+
+    @Override
+    public String getNotes() {
+        return this.notes;
+    }
+
+    @Override
+    public Integer getPrestartTimestamp() {
+        return prestartTimestamp;
+    }
+
+    @Override
+    public Integer getRun() {
         return this.run;
     }
 
-    /**
-     * Return <code>true</code> if the run was okay (no major errors or data
-     * corruption occurred).
-     *
-     * @return <code>true</code> if the run was okay
-     */
-    public boolean getRunOkay() {
-        return this.runOkay;
-    }
-
-    /**
-     * Get the scaler data of this run.
-     *
-     * @return the scaler data of this run
-     */
-    public List<ScalerData> getScalerData() {
-        return this.scalerDataList;
-    }
-
-    /**
-     * Get the trigger config of this run.
-     *
-     * @return the trigger config of this run
-     */
-    public TriggerConfig getTriggerConfig() {
-        return triggerConfig;
-    }
-
-    /**
-     * Get the start date.
-     *
-     * @return the start date
-     */
-    public Date getStartDate() {
-        return startDate;
-    }
-
-    /**
-     * Get the total events in the run.
-     *
-     * @return the total events in the run
-     */
-    public int getTotalEvents() {
+    @Override
+    public String getTarget() {
+        return this.target;
+    }
+
+    @Override
+    public Long getTiTimeOffset() {
+        return this.tiTimeOffset;
+    }
+
+    @Override
+    public Integer getTotalEvents() {
         return this.totalEvents;
     }
 
-    /**
-     * Get the total number of files for this run.
-     *
-     * @return the total number of files for this run
-     */
-    public int getTotalFiles() {
+    @Override
+    public Integer getTotalFiles() {
         return this.totalFiles;
     }
 
-    /**
-     * Get the number of seconds in the run which is the difference between the
-     * start and end times.
-     *
-     * @return the total seconds in the run
-     */
-    public long getTotalSeconds() {
-        return (endDate.getTime() - startDate.getTime()) / 1000;
-    }
-
-    /**
-     * Get the date when this run record was last updated.
-     *
-     * @return the date when this run record was last updated
-     */
+    @Override
+    public Map<Integer, String> getTriggerConfigData() {
+        return this.triggerConfigData;
+    }
+
+    @Override
+    public String getTriggerConfigName() {
+        return this.triggerConfigName;
+    }
+
+    @Override
+    public Double getTriggerRate() {
+        return this.triggerRate;
+    }
+
+    @Override
     public Date getUpdated() {
         return updated;
     }
-    
-    /**
-     * Set the creation date of the run record.
-     *
-     * @param created the creation date of the run record
-     */
-    void setCreated(final Date created) {
+
+    /**
+     * Load DAQ config object from trigger config string data.
+     */
+    private void loadDAQConfig() {
+        if (this.triggerConfigData != null && !this.triggerConfigData.isEmpty()) {
+            EvioDAQParser parser = new EvioDAQParser();
+            for (Entry<Integer, String> entry : this.triggerConfigData.entrySet()) {
+                parser.parse(entry.getKey(), this.getRun(), new String[] {entry.getValue()});
+            }
+            ConfigurationManager.updateConfiguration(parser);
+            daqConfig = ConfigurationManager.getInstance();
+        }
+    }
+
+    void setCreated(Date created) {
         this.created = created;
     }
 
-    /**
-     * Set the start date.
-     *
-     * @param startDate the start date
-     */
-    void setEndDate(final Date endDate) {
-        this.endDate = endDate;
-    }
-
-    /**
-     * Set if end is okay.
-     *
-     * @param endOkay <code>true</code> if end is okay
-     */
-    void setEndOkay(final boolean endOkay) {
-        this.endOkay = endOkay;
-    }
-   
-    /**
-     * Set the EPICS data for the run.
-     *
-     * @param epics the EPICS data for the run
-     */
-    void setEpicsData(final List<EpicsData> epicsDataList) {
-        this.epicsDataList = epicsDataList;
-    }
-    
-    /**
-     * Set whether the run was "okay" meaning the data is usable for physics
-     * analysis.
-     *
-     * @param runOkay <code>true</code> if the run is okay
-     */
-    void setRunOkay(final boolean runOkay) {
-        this.runOkay = runOkay;
-    }
-
-    /**
-     * Set the scaler data of the run.
-     *
-     * @param scalerData the scaler data
-     */
-    void setScalerData(final List<ScalerData> scalerDataList) {
-        this.scalerDataList = scalerDataList;
-    }
-
-    /**
-     * Set the trigger config of the run.
-     *
-     * @param triggerConfig the trigger config
-     */
-    void setTriggerConfig(final TriggerConfig triggerConfig) {
-        this.triggerConfig = triggerConfig;
-    }
-
-    /**
-     * Set the start date.
-     *
-     * @param startDate the start date
-     */
-    void setStartDate(final Date startDate) {
-        this.startDate = startDate;
+    void setDAQConfig(DAQConfig daqConfig) {
+        this.daqConfig = daqConfig;
+    }
+
+    void setEndTimestamp(Integer endTimestamp) {
+        this.endTimestamp = endTimestamp;
+    }
+
+    void setGoTimestamp(Integer goTimestamp) {
+        this.goTimestamp = goTimestamp;
+    }
+
+    void setLivetimeClock(Double livetimeClock) {
+        this.livetimeClock = livetimeClock;
+    }
+
+    void setLivetimeFcupTdc(Double livetimeTdc) {
+        this.livetimeTdc = livetimeTdc;
+    }
+
+    void setLivetimeFcupTrg(Double livetimeTrg) {
+        this.livetimeTrg = livetimeTrg;
+    }
+
+    void setNotes(String notes) {
+        this.notes = notes;
+    }
+
+    void setPrestartTimestamp(Integer prestartTimestamp) {
+        this.prestartTimestamp = prestartTimestamp;
+    }
+
+    void setTarget(String target) {
+        this.target = target;
+    }
+
+    /**
+     * Set the TI time offset in ns.
+     * 
+     * @param tiTimeOffset the TIM time offset in ns
+     */
+    void setTiTimeOffset(Long tiTimeOffset) {
+        this.tiTimeOffset = tiTimeOffset;
     }
 
     /**
@@ -315,7 +272,7 @@
      *
      * @param totalEvents the total number of physics events in the run
      */
-    void setTotalEvents(final int totalEvents) {
+    void setTotalEvents(final Integer totalEvents) {
         this.totalEvents = totalEvents;
     }
 
@@ -324,37 +281,68 @@
      *
      * @param totalFiles the total number of EVIO files in the run
      */
-    void setTotalFiles(final int totalFiles) {
+    void setTotalFiles(final Integer totalFiles) {
         this.totalFiles = totalFiles;
     }
 
     /**
-     * Set the date when this run record was last updated.
-     *
-     * @param updated the date when the run record was last updated
-     */
-    void setUpdated(final Date updated) {
+     * Build the DAQ config from the trigger config string data.
+     * 
+     * @param triggerConfigData a map of crate number to the trigger config string data from the bank
+     */
+    void setTriggerConfigData(Map<Integer, String> triggerConfigData) {
+        this.triggerConfigData = triggerConfigData;
+        // Load DAQ config if not already set.
+        if (daqConfig == null) {
+            loadDAQConfig();
+        }
+    }
+
+    /**
+     * Set the trigger config file.
+     * 
+     * @param triggerConfigName the trigger config file
+     */
+    void setTriggerConfigName(String triggerConfigName) {
+        this.triggerConfigName = triggerConfigName;
+    }
+
+    /**
+     * Set the trigger rate in KHz.
+     * 
+     * @param triggerRate the trigger rate in KHz
+     */
+    void setTriggerRate(Double triggerRate) {
+        this.triggerRate = triggerRate;
+    }
+
+    void setUpdated(Date updated) {
         this.updated = updated;
     }
-    
-    /**
-     * Convert this object to a string.
-     *
+
+    /**
+     * Convert the object to a string.
+     * 
      * @return this object converted to a string
      */
     @Override
     public String toString() {
         return "RunSummary { " 
                 + "run: " + this.getRun() 
-                + ", startDate: " + (this.getStartDate() != null ? DATE_DISPLAY.format(this.getStartDate()) : null)
-                + ", endDate: " + (this.getEndDate() != null ? DATE_DISPLAY.format(this.getEndDate()) : null) 
-                + ", totalEvents: " + this.getTotalEvents()
-                + ", totalFiles: " + this.getTotalFiles() 
-                + ", endOkay: " + this.getEndOkay() 
-                + ", runOkay: "
-                + this.getRunOkay() 
-                + ", updated: " + this.getUpdated() 
+                + ", events: " + this.getTotalEvents() 
+                + ", files: " + this.getTotalFiles() 
                 + ", created: " + this.getCreated() 
+                + ", updated: " + this.getUpdated()
+                + ", prestartTimestamp: " + this.getPrestartTimestamp()
+                + ", goTimestamp: " + this.getGoTimestamp()
+                + ", endTimestamp: " + this.getEndTimestamp()
+                + ", triggerConfigFile: " + this.getTriggerConfigName()
+                + ", DAQConfig: " + (this.getDAQConfig() != null ? true : false)
+                + ", triggerRate: " + this.getTriggerRate()
+                + ", livetimeClock: " + this.getLivetimeClock()
+                + ", livetimeTdc: " + this.getLivetimeFcupTdc()
+                + ", livetimeTrg: " + this.getLivetimeFcupTrg()
+                + ", tiTimeOffset: " + this.getTiTimeOffset() 
                 + " }";
     }
 }

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDao.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDao.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDao.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,36 @@
+package org.hps.run.database;
+
+import java.util.List;
+
+import org.hps.record.svt.SvtConfigData;
+
+/**
+ * Database API for accessing SVT configuration in run database.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public interface SvtConfigDao {
+   
+    /**
+     * Insert SVT configurations.
+     * 
+     * @param svtConfigs the list of SVT configurations
+     * @param run the run number
+     */
+    void insertSvtConfigs(List<SvtConfigData> svtConfigs, int run);
+    
+    /**
+     * Get the list of SVT configurations for the run.
+     * 
+     * @param run the run number
+     * @return the list of SVT configurations
+     */
+    List<SvtConfigData> getSvtConfigs(int run);
+    
+    /**
+     * Delete SVT configurations for the run.
+     * 
+     * @param run the run number
+     */
+    void deleteSvtConfigs(int run);
+}

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDaoImpl.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDaoImpl.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SvtConfigDaoImpl.java	Tue Dec  1 15:55:47 2015
@@ -0,0 +1,146 @@
+package org.hps.run.database;
+
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hps.record.svt.SvtConfigData;
+import org.hps.record.svt.SvtConfigData.RocTag;
+
+/**
+ * Implementation of SVT configuration database operations.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public class SvtConfigDaoImpl implements SvtConfigDao {
+
+    private Connection connection = null;
+    
+    private static final String INSERT = 
+            "INSERT INTO svt_configs (run, timestamp, config1, status1, config2, status2) VALUES (?, ?, ?, ?, ?, ?)"; 
+    
+    private static final String SELECT = 
+            "SELECT * FROM svt_configs WHERE run = ?";
+    
+    SvtConfigDaoImpl(Connection connection) {
+        if (connection == null) {
+            throw new IllegalArgumentException("The connection is null.");
+        }
+        this.connection = connection;
+    }
+    
+    @Override
+    public void insertSvtConfigs(List<SvtConfigData> svtConfigs, int run) {
+        PreparedStatement preparedStatement = null;
+        try {
+            preparedStatement = connection.prepareStatement(INSERT);
+            for (SvtConfigData config : svtConfigs) {
+                preparedStatement.setInt(1, run);
+                preparedStatement.setInt(2, config.getTimestamp());
+                if (config.getConfigData(RocTag.DATA) != null) {
+                    preparedStatement.setBytes(3, config.getConfigData(RocTag.DATA).getBytes());
+                } else {
+                    preparedStatement.setBytes(3, null);
+                }
+                if (config.getStatusData(RocTag.DATA) != null) {
+                    preparedStatement.setBytes(4, config.getStatusData(RocTag.DATA).getBytes());
+                } else {
+                    preparedStatement.setBytes(4, null);
+                }
+                if (config.getConfigData(RocTag.CONTROL) != null) {
+                    preparedStatement.setBytes(5, config.getConfigData(RocTag.CONTROL).getBytes());
+                } else {
+                    preparedStatement.setBytes(6, null);
+                }
+                if (config.getStatusData(RocTag.CONTROL) != null) {
+                    preparedStatement.setBytes(7, config.getConfigData(RocTag.CONTROL).getBytes());
+                } else {
+                    preparedStatement.setBytes(8, null);
+                }
+                preparedStatement.executeUpdate();
+            }
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                } catch (final SQLException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+    
+    @Override
+    public List<SvtConfigData> getSvtConfigs(int run) {
+        List<SvtConfigData> svtConfigList = new ArrayList<SvtConfigData>();
+        PreparedStatement preparedStatement = null;
+        try {
+            preparedStatement = connection.prepareStatement(SELECT);
+            preparedStatement.setInt(1, run);
+            ResultSet resultSet = preparedStatement.executeQuery();
+            while (resultSet.next()) {
+                
+                SvtConfigData config = new SvtConfigData(resultSet.getInt("timestamp"));
+                                
+                Clob clob = resultSet.getClob("config1");
+                if (clob != null) {
+                    config.setConfigData(RocTag.DATA, clob.getSubString(1, (int) clob.length()));
+                }
+                
+                clob = resultSet.getClob("status1");
+                if (clob != null) {
+                    config.setStatusData(RocTag.DATA, clob.getSubString(1, (int) clob.length()));
+                }
+                
+                clob = resultSet.getClob("config2");
+                if (clob != null) { 
+                    config.setConfigData(RocTag.CONTROL, clob.getSubString(1, (int) clob.length()));
+                }
+                
+                clob = resultSet.getClob("status2");
+                if (clob != null) {
+                    config.setStatusData(RocTag.CONTROL, clob.getSubString(1, (int) clob.length()));
+                }                
+                
+                svtConfigList.add(config);
+            }
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                } catch (final SQLException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return svtConfigList;
+    }
+    
+    @Override
+    public void deleteSvtConfigs(int run) {
+        PreparedStatement preparedStatement = null;
+        try {
+            preparedStatement = connection.prepareStatement("DELETE FROM svt_configs WHERE run = ?");
+            preparedStatement.setInt(1, run);
+            preparedStatement.executeUpdate();
+        } catch (final SQLException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                } catch (final SQLException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}

Modified: java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/monitoring/EcalLedSequenceStandalone.lcsim
 =============================================================================
--- java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/monitoring/EcalLedSequenceStandalone.lcsim	(original)
+++ java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/monitoring/EcalLedSequenceStandalone.lcsim	Tue Dec  1 15:55:47 2015
@@ -38,7 +38,7 @@
 	           <doFullAnalysis>false</doFullAnalysis>
 	           <skipMin>0.25</skipMin>
 	           <skipInitial>0.05</skipInitial>    
-	           <useRawEnergy>false</useRawEnergy>
+	           <useRawEnergy>true</useRawEnergy>
 	           <energyCut>1</energyCut>
 	           <nEventsMin>300</nEventsMin>
 	           <evnMinDraw>0.</evnMinDraw>

Modified: java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/readout/HPSReconNoReadout.lcsim
 =============================================================================
--- java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/readout/HPSReconNoReadout.lcsim	(original)
+++ java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/readout/HPSReconNoReadout.lcsim	Tue Dec  1 15:55:47 2015
@@ -1,6 +1,11 @@
 
 <lcsim xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" 
        xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/lcsim/1.0/lcsim.xsd">
+<!--
+    <control>
+        <numberOfEvents>20000</numberOfEvents>
+    </control>
+-->
     <execute>
         <driver name="EventMarkerDriver"/>   
         <driver name="ReconClusterer" />
@@ -46,7 +51,17 @@
             <ecalClusterCollectionName>EcalClustersCorr</ecalClusterCollectionName>        
             <trackCollectionNames>MatchedTracks GBLTracks</trackCollectionNames>
         </driver>  
-        <driver name="GBLOutputDriver" type="org.hps.recon.tracking.gbl.GBLOutputDriver"/>      
+        <driver name="GBLOutputDriver" type="org.hps.recon.tracking.gbl.GBLOutputDriver">
+            <debug>0</debug>
+            <isMC>false</isMC>
+            <gblFileName>${outputFile}.gbl</gblFileName>
+            <addBeamspot>false</addBeamspot>
+            <beamspotScatAngle>0.005</beamspotScatAngle>
+            <beamspotWidthZ>0.05</beamspotWidthZ>
+            <beamspotWidthY>0.2</beamspotWidthY>
+            <beamspotTiltZOverY>0.26</beamspotTiltZOverY>
+            <beamspotPosition>0.0 -0.11 -0.05</beamspotPosition>
+        </driver> 
         <driver name="GBLRefitterDriver" type="org.hps.recon.tracking.gbl.HpsGblRefitter"/>
         <driver name="LCIOWriter" type="org.lcsim.util.loop.LCIODriver">
             <outputFilePath>${outputFile}</outputFilePath>

Modified: java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015FullReconMC_Pass2.lcsim
 =============================================================================
--- java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015FullReconMC_Pass2.lcsim	(original)
+++ java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015FullReconMC_Pass2.lcsim	Tue Dec  1 15:55:47 2015
@@ -119,7 +119,7 @@
             <rmsTimeCut>8.0</rmsTimeCut>
         </driver>       
         <driver name="MergeTrackCollections" type="org.hps.recon.tracking.MergeTrackCollections" />
-        <driver name="GBLOutputDriver" type="org.hps.recon.tracking.gbl.GBLOutputDriver"/>             
+        <driver name="GBLOutputDriver" type="org.hps.recon.tracking.gbl.GBLOutputDriver"/>
         <driver name="GBLRefitterDriver" type="org.hps.recon.tracking.gbl.HpsGblRefitter"/>
         <driver name="EcalRawConverter" type="org.hps.recon.ecal.EcalRawConverterDriver">
             <ecalCollectionName>EcalCalHits</ecalCollectionName>
@@ -153,5 +153,6 @@
         <driver name="AidaSaveDriver" type="org.lcsim.job.AidaSaveDriver">
             <outputFileName>${outputFile}.root</outputFileName>
         </driver>
+        
     </drivers>
 </lcsim>

Modified: java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullReconMC_Pass2_Gbl.lcsim
 =============================================================================
--- java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullReconMC_Pass2_Gbl.lcsim	(original)
+++ java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullReconMC_Pass2_Gbl.lcsim	Tue Dec  1 15:55:47 2015
@@ -166,10 +166,11 @@
         <driver name="AidaSaveDriver" type="org.lcsim.job.AidaSaveDriver">
             <outputFileName>${outputFile}.root</outputFileName>
         </driver>
+        <!--
         <driver name="TriggerTurnOnDriver" type="org.hps.analysis.trigger.TriggerTurnOnDriver">
             <isMC>True</isMC>
         </driver>
         <driver name="TriggerTurnOnSSPDriver" type="org.hps.analysis.trigger.TriggerTurnOnSSPDriver"/>
-        
+        -->
     </drivers>
 </lcsim>

Modified: java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullRecon_Pass2_Gbl.lcsim
 =============================================================================
--- java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullRecon_Pass2_Gbl.lcsim	(original)
+++ java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/EngineeringRun2015FullRecon_Pass2_Gbl.lcsim	Tue Dec  1 15:55:47 2015
@@ -144,6 +144,12 @@
             <debug>0</debug>
             <isMC>false</isMC>
             <gblFileName>${outputFile}.gbl</gblFileName>
+            <addBeamspot>false</addBeamspot>
+            <beamspotScatAngle>0.005</beamspotScatAngle>
+            <beamspotWidthZ>0.05</beamspotWidthZ>
+            <beamspotWidthY>0.2</beamspotWidthY>
+            <beamspotTiltZOverY>0.26</beamspotTiltZOverY>
+            <beamspotPosition>0.0 -0.11 -0.05</beamspotPosition>
         </driver> 
         <driver name="GBLRefitterDriver" type="org.hps.recon.tracking.gbl.HpsGblRefitter">
             <debug>false</debug>
@@ -159,7 +165,7 @@
         <driver name="GblResidualEcalDriver" type="org.hps.users.phansson.ECalExtrapolationDriver"/>   
         <driver name="TrackExtrapolationTestDriver" type="org.hps.users.phansson.TrackExtrapolationTestDriver"/>   
         <driver name="TrackingReconstructionPlots" type="org.hps.users.phansson.TrackingReconstructionPlots">
-            <showPlots>True</showPlots>
+            <showPlots>False</showPlots>
         </driver>
         <driver name="TimerDriver1" type="org.hps.util.TimerDriver"/>
         <driver name="GeomChecker" type="org.hps.users.phansson.TrackingGeometryChecker"/>

Copied: java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/Occupancy.lcsim (from r3968, java/trunk/steering-files/src/main/resources/org/hps/steering/users/phansson/Occupancy.lcsim)
 =============================================================================
--- java/trunk/steering-files/src/main/resources/org/hps/steering/users/phansson/Occupancy.lcsim	(original)
+++ java/branches/jeremy-dev/steering-files/src/main/resources/org/hps/steering/users/phansson/Occupancy.lcsim	Tue Dec  1 15:55:47 2015
@@ -59,8 +59,11 @@
         <driver name="SensorOccupancyDriver" type="org.hps.monitoring.drivers.svt.SensorOccupancyPlotsDriver">
             <enablePositionPlots>True</enablePositionPlots>
             <eventRefreshRate>100</eventRefreshRate>
-            <enableTriggerFilter>True</enableTriggerFilter>
-            <filterPair1Triggers>True</filterPair1Triggers>
+            <enableTriggerFilter>False</enableTriggerFilter>
+            <filterPair1Triggers>False</filterPair1Triggers>
+            <filterPulserTriggers>True</filterPulserTriggers>
+            <timeWindowWeight>3.0</timeWindowWeight>
+            <maxSamplePosition>1</maxSamplePosition>
         </driver>
 
     </drivers>

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/TrackUtils.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/TrackUtils.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/TrackUtils.java	Tue Dec  1 15:55:47 2015
@@ -785,24 +785,37 @@
         return !isTopTrack(htf);
     }
 
+    
     /**
      * Transform MCParticle into a Helix object. Note that it produces the helix
      * parameters at nominal x=0 and assumes that there is no field at x<0
      *
      * @param mcp MC particle to be transformed
-     * @return helix object based on the MC particle
+     * @return {@link HelicalTrackFit} object based on the MC particle
      */
     public static HelicalTrackFit getHTF(MCParticle mcp, double Bz) {
-        boolean debug = true;
-        if (debug) {
-            System.out.printf("getHTF\n");
-            System.out.printf("mcp org %s mc p %s\n", mcp.getOrigin().toString(), mcp.getMomentum().toString());
-        }
-        Hep3Vector org = CoordinateTransformations.transformVectorToTracking(mcp.getOrigin());
+        return getHTF(mcp,mcp.getOrigin(),Bz);
+    }
+        
+    
+    
+    /**
+     * Transform MCParticle into a Helix object. Note that it produces the helix
+     * parameters at nominal x=0 and assumes that there is no field at x<0
+     *
+     * @param mcp MC particle to be transformed
+     * @param org origin to be used for the track 
+     * @return {@link HelicalTrackFit} object based on the MC particle
+     */
+    public static HelicalTrackFit getHTF(MCParticle mcp, Hep3Vector origin, double Bz) {
+        boolean debug = false;
+        
+        if (debug) System.out.printf("getHTF\nmcp org %s origin used %s mc p %s\n", mcp.getOrigin().toString(),origin.toString(), mcp.getMomentum().toString());
+        
+        Hep3Vector org = CoordinateTransformations.transformVectorToTracking(origin);
         Hep3Vector p = CoordinateTransformations.transformVectorToTracking(mcp.getMomentum());
 
-        if (debug)
-            System.out.printf("mcp org %s mc p %s (trans)\n", org.toString(), p.toString());
+        if (debug) System.out.printf("mcp org %s mc p %s (trans)\n", org.toString(), p.toString());
 
         // Move to x=0 if needed
         double targetX = BeamlineConstants.DIPOLE_EDGELOW_TESTRUN;
@@ -820,8 +833,7 @@
             // old.toString(),p.toString(),org.toString());
         }
 
-        if (debug)
-            System.out.printf("mcp org %s mc p %s (trans2)\n", org.toString(), p.toString());
+        if (debug) System.out.printf("mcp org %s mc p %s (trans2)\n", org.toString(), p.toString());
 
         HelixParamCalculator helixParamCalculator = new HelixParamCalculator(p, org, -1 * ((int) mcp.getCharge()), Bz);
         double par[] = new double[5];
@@ -831,8 +843,7 @@
         par[HelicalTrackFit.curvatureIndex] = 1.0 / helixParamCalculator.getRadius();
         par[HelicalTrackFit.z0Index] = helixParamCalculator.getZ0();
         HelicalTrackFit htf = getHTF(par);
-        System.out.printf("d0 %f z0 %f R %f phi %f lambda %s\n",
-                htf.dca(), htf.z0(), htf.R(), htf.phi0(), htf.slope());
+        if(debug) System.out.printf("d0 %f z0 %f R %f phi %f lambda %s\n", htf.dca(), htf.z0(), htf.R(), htf.phi0(), htf.slope());
         return htf;
     }
 

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutput.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutput.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutput.java	Tue Dec  1 15:55:47 2015
@@ -58,15 +58,16 @@
     private final MaterialSupervisor materialManager;
     private final MultipleScattering _scattering;
     private final double _beamEnergy = 1.1; //GeV
-    private boolean AprimeEvent = false; // do extra checks
     private boolean hasXPlanes = false;
     private boolean addBeamspot = false;
     private double beamspotTiltZOverY = 0; //Math.PI/180* 15;
     private double beamspotScatAngle = 0.000001;
-    // beam spot with in tracking frame
+    // beam spot, in tracking frame
     private double beamspotWidthZ = 0.05;
     private double beamspotWidthY = 0.150;
-    double beamspotPosition[] = {0,0,0};
+    private double beamspotPosition[] = {0,0,0};
+    // human readable ID for beam spot     
+    private final int iBeamspotHit = -1; 
     
 
     /**
@@ -138,10 +139,6 @@
         }
     }
 
-    void setAPrimeEventFlag(boolean flag) {
-        this.AprimeEvent = flag;
-    }
-
     void setXPlaneFlag(boolean flag) {
         this.hasXPlanes = flag;
     }
@@ -168,25 +165,61 @@
 
         // Find the truth particle of the track
         MCParticle mcp = null;
-
+        MCParticle ap = null;
+        
+        // MC processing
         if (isMC) {
+            
+            // find the truth particle for this track
             mcp = getMatchedTruthParticle(trk);
 
+            // check if this is an A' event
+            for(MCParticle part : mcParticles) {
+                if(Math.abs(part.getPDGID()) == 622) {
+                    ap = part;
+                    break;
+                }
+            }
+            
             if (mcp == null) {
                 System.out.printf("%s: WARNING!! no truth particle found in event!\n", this.getClass().getSimpleName());
                 this.printMCParticles(mcParticles);
                 //System.exit(1);
-            } else if (_debug > 0) {
-                System.out.printf("%s: truth particle (pdgif %d ) found in event!\n", this.getClass().getSimpleName(), mcp.getPDGID());
-            }
-
-            if (AprimeEvent) {
-                checkAprimeTruth(mcp, mcParticles);
-            }
-        }
+            } else {
+                if (_debug > 0) System.out.printf("%s: truth particle (pdgif %d ) found in event!\n", this.getClass().getSimpleName(), mcp.getPDGID());
+
+                // If this is an A' event, do some more checks
+                if ( ap != null) {
+                    // A few MC files have broken links b/w parents-daughters
+                    // This causes the MC particle to come from the origin even if the decay happen somewhere else
+                    if(this.getAprimeDecayProducts(mcParticles).size()>0) {
+                        //do a full check
+                        checkAprimeTruth(mcp, mcParticles);
+                    }
+                }
+            }
+        }        
 
         // Get track parameters from MC particle 
-        HelicalTrackFit htfTruth = (isMC && mcp != null) ? TrackUtils.getHTF(mcp, -1.0 * this.bFieldVector.z()) : null;
+        HelicalTrackFit htfTruth = null;
+        
+        if( isMC && mcp != null) {
+            // check if we should be using a different origin than the particle tells us
+            Hep3Vector mcp_origin;
+            if( ap != null) {
+                // There is an A' here. Use its origin if different
+                if (_debug > 0) System.out.printf("%s: A' found with origin  %s compared to particle %s (diff: %s)\n", this.getClass().getSimpleName(), ap.getOrigin().toString(), mcp.getOrigin().toString(), VecOp.sub(ap.getOrigin(), mcp.getOrigin()).toString());
+                if(VecOp.sub(ap.getOrigin(), mcp.getOrigin()).magnitude() > 0.00001)
+                    mcp_origin = ap.getOrigin();
+                else
+                    mcp_origin = mcp.getOrigin();
+            } else {
+                // No A', use particle origin
+                mcp_origin = mcp.getOrigin();
+            }
+
+            htfTruth = TrackUtils.getHTF(mcp,mcp_origin, -1.0 * this.bFieldVector.z());
+        }
 
         // Use the truth helix as the initial track for GBL?
         //htf = htfTruth;
@@ -274,7 +307,6 @@
         
         
         int istrip = 0;
-        final int iBeamspotHit = -1; // human readable ID for beam spot 
         int beamSpotMillepedeId = 98; // just a random int number that I came up with
         
         for (int ihit = -1; ihit != hits.size(); ++ihit) {
@@ -282,7 +314,7 @@
             HelicalTrackHit hit = null;
             HelicalTrackCross htc = null;
             List<HelicalTrackStrip> strips;
-            List<MCParticle> hitMCParticles = null;
+            List<MCParticle> hitMCParticles = new ArrayList<MCParticle>();
             Hep3Vector correctedHitPosition = null;
 
             // Add beamspot first
@@ -390,7 +422,7 @@
                 SimTrackerHit simHit = simHitsLayerMap.get(strip.layer());
 
                 if (isMC) {
-                    if (simHit == null) {
+                    if (simHit == null && ihit != iBeamspotHit) {
                         System.out.printf("%s: no sim hit for strip hit at layer %d\n", this.getClass().getSimpleName(), strip.layer());
                         System.out.printf("%s: it as %d mc particles associated with it:\n", this.getClass().getSimpleName(), hitMCParticles.size());
                         for (MCParticle particle : hitMCParticles) {
@@ -676,14 +708,57 @@
     }
 
     
+    
+    private List<MCParticle> getAprimeDecayProducts(List<MCParticle> mcParticles) {
+        List<MCParticle> pair = new ArrayList<MCParticle>();
+        for (MCParticle mcp : mcParticles) {
+            if (mcp.getGeneratorStatus() != MCParticle.FINAL_STATE) {
+                continue;
+            }
+            boolean hasAprimeParent = false;
+            for (MCParticle parent : mcp.getParents()) {
+                if (Math.abs(parent.getPDGID()) == 622) {
+                    hasAprimeParent = true;
+                }
+            }
+            if (hasAprimeParent) {
+                pair.add(mcp);
+            }
+        }
+        
+        return pair;
+
+    }
+
    
 
     private void checkAprimeTruth(MCParticle mcp, List<MCParticle> mcParticles) {
+        
         List<MCParticle> mcp_pair = getAprimeDecayProducts(mcParticles);
 
+        
+        if (mcp_pair.size() != 2) {
+            System.out.printf("%s: ERROR this event has %d mcp with 622 as parent!!??  \n", this.getClass().getSimpleName(), mcp_pair.size());
+            this.printMCParticles(mcParticles);
+            System.exit(1);
+        }
+        if (Math.abs(mcp_pair.get(0).getPDGID()) != 11 || Math.abs(mcp_pair.get(1).getPDGID()) != 11) {
+            System.out.printf("%s: ERROR decay products are not e+e-? \n", this.getClass().getSimpleName());
+            this.printMCParticles(mcParticles);
+            System.exit(1);
+        }
+        if (mcp_pair.get(0).getPDGID() * mcp_pair.get(1).getPDGID() > 0) {
+            System.out.printf("%s: ERROR decay products have the same sign? \n", this.getClass().getSimpleName());
+            this.printMCParticles(mcParticles);
+            System.exit(1);
+        }
+        
+        
+        
         if (_debug > 0) {
             double invMassTruth = Math.sqrt(Math.pow(mcp_pair.get(0).getEnergy() + mcp_pair.get(1).getEnergy(), 2) - VecOp.add(mcp_pair.get(0).getMomentum(), mcp_pair.get(1).getMomentum()).magnitudeSquared());
             double invMassTruthTrks = getInvMassTracks(TrackUtils.getHTF(mcp_pair.get(0), -1.0 * this.bFieldVector.z()), TrackUtils.getHTF(mcp_pair.get(1), -1.0 * this.bFieldVector.z()));
+            
             System.out.printf("%s: invM = %f\n", this.getClass().getSimpleName(), invMassTruth);
             System.out.printf("%s: invMTracks = %f\n", this.getClass().getSimpleName(), invMassTruthTrks);
         }
@@ -894,40 +969,6 @@
         return chi2.e(0, 0);
     }
 
-    private List<MCParticle> getAprimeDecayProducts(List<MCParticle> mcParticles) {
-        List<MCParticle> pair = new ArrayList<MCParticle>();
-        for (MCParticle mcp : mcParticles) {
-            if (mcp.getGeneratorStatus() != MCParticle.FINAL_STATE) {
-                continue;
-            }
-            boolean hasAprimeParent = false;
-            for (MCParticle parent : mcp.getParents()) {
-                if (Math.abs(parent.getPDGID()) == 622) {
-                    hasAprimeParent = true;
-                }
-            }
-            if (hasAprimeParent) {
-                pair.add(mcp);
-            }
-        }
-        if (pair.size() != 2) {
-            System.out.printf("%s: ERROR this event has %d mcp with 622 as parent!!??  \n", this.getClass().getSimpleName(), pair.size());
-            this.printMCParticles(mcParticles);
-            System.exit(1);
-        }
-        if (Math.abs(pair.get(0).getPDGID()) != 11 || Math.abs(pair.get(1).getPDGID()) != 11) {
-            System.out.printf("%s: ERROR decay products are not e+e-? \n", this.getClass().getSimpleName());
-            this.printMCParticles(mcParticles);
-            System.exit(1);
-        }
-        if (pair.get(0).getPDGID() * pair.get(1).getPDGID() > 0) {
-            System.out.printf("%s: ERROR decay products have the same sign? \n", this.getClass().getSimpleName());
-            this.printMCParticles(mcParticles);
-            System.exit(1);
-        }
-        return pair;
-
-    }
 
     private void printMCParticles(List<MCParticle> mcParticles) {
         System.out.printf("%s: printMCParticles \n", this.getClass().getSimpleName());

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutputDriver.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutputDriver.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLOutputDriver.java	Tue Dec  1 15:55:47 2015
@@ -79,7 +79,6 @@
         gbl = new GBLOutput(gblFileName, bfield); // if filename is empty no text file is written
         gbl.setDebug(_debug);
         gbl.buildModel(detector);
-        gbl.setAPrimeEventFlag(false);
         gbl.setXPlaneFlag(false);
         gbl.setAddBeamspot(addBeamspot);
         gbl.setBeamspotScatAngle(beamspotScatAngle);

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLRefitterDriver.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLRefitterDriver.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GBLRefitterDriver.java	Tue Dec  1 15:55:47 2015
@@ -6,6 +6,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.apache.commons.math3.util.Pair;
 import org.hps.recon.tracking.MaterialSupervisor;
 import org.hps.recon.tracking.MultipleScattering;
 import org.hps.recon.tracking.TrackUtils;
@@ -68,9 +69,9 @@
 
         Map<Track, Track> inputToRefitted = new HashMap<Track, Track>();
         for (Track track : tracks) {
-            Track newTrack = GblUtils.refitTrack(TrackUtils.getHTF(track), TrackUtils.getStripHits(track, hitToStrips, hitToRotated), track.getTrackerHits(), 5, _scattering, bfield);
-            refittedTracks.add(newTrack);
-            inputToRefitted.put(track, newTrack);
+            Pair<Track, GBLKinkData> newTrack = MakeGblTracks.refitTrack(TrackUtils.getHTF(track), TrackUtils.getStripHits(track, hitToStrips, hitToRotated), track.getTrackerHits(), 5, _scattering, bfield);
+            refittedTracks.add(newTrack.getFirst());
+            inputToRefitted.put(track, newTrack.getFirst());
         }
 
         if (mergeTracks) {
@@ -105,8 +106,8 @@
                         }
                     }
 
-                    Track mergedTrack = GblUtils.refitTrack(TrackUtils.getHTF(track), TrackUtils.getStripHits(track, hitToStrips, hitToRotated), allHth, 5, _scattering, bfield);
-                    mergedTracks.add(mergedTrack);
+                    Pair<Track, GBLKinkData> mergedTrack = MakeGblTracks.refitTrack(TrackUtils.getHTF(track), TrackUtils.getStripHits(track, hitToStrips, hitToRotated), allHth, 5, _scattering, bfield);
+                    mergedTracks.add(mergedTrack.getFirst());
 //                    System.out.format("%f %f %f\n", fit.get_chi2(), inputToRefitted.get(track).getChi2(), inputToRefitted.get(otherTrack).getChi2());
 //                mergedTrackToTrackList.put(mergedTrack, new ArrayList<Track>());
                 }

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GblUtils.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GblUtils.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/GblUtils.java	Tue Dec  1 15:55:47 2015
@@ -1,34 +1,11 @@
 package org.hps.recon.tracking.gbl;
 
 import hep.physics.matrix.BasicMatrix;
-import hep.physics.vec.BasicHep3Vector;
-import hep.physics.vec.Hep3Matrix;
-import hep.physics.vec.Hep3Vector;
-import hep.physics.vec.VecOp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import org.hps.recon.tracking.CoordinateTransformations;
 import org.hps.recon.tracking.MaterialSupervisor;
 import org.hps.recon.tracking.MultipleScattering;
-import org.hps.recon.tracking.TrackUtils;
-import org.lcsim.constants.Constants;
 import org.lcsim.detector.IDetectorElement;
-import org.lcsim.detector.ITransform3D;
-import org.lcsim.detector.tracker.silicon.ChargeCarrier;
-import org.lcsim.detector.tracker.silicon.HpsSiSensor;
-import org.lcsim.detector.tracker.silicon.SiSensor;
-import org.lcsim.detector.tracker.silicon.SiSensorElectrodes;
-import org.lcsim.event.RawTrackerHit;
-import org.lcsim.event.Track;
-import org.lcsim.event.TrackerHit;
 import org.lcsim.fit.helicaltrack.HelicalTrackFit;
-import org.lcsim.fit.helicaltrack.HelicalTrackStrip;
 import org.lcsim.fit.helicaltrack.HelixUtils;
-import org.lcsim.recon.tracking.digitization.sisim.SiTrackerHitStrip1D;
-import org.lcsim.recon.tracking.digitization.sisim.TrackerHitType;
 import org.lcsim.recon.tracking.seedtracker.ScatterAngle;
 
 /**
@@ -128,216 +105,4 @@
             throw new UnsupportedOperationException("Should not happen. This problem is only solved with the MaterialSupervisor.");
         }
     }
-
-    /**
-     * Do a GBL fit to an arbitrary set of strip hits, with a starting value of
-     * the helix parameters.
-     *
-     * @param helix Initial helix parameters. Only track parameters are used
-     * (not covariance)
-     * @param stripHits Strip hits to be used for the GBL fit. Does not need to
-     * be in sorted order.
-     * @param hth Stereo hits for the track's hit list (these are not used in
-     * the GBL fit). Does not need to be in sorted order.
-     * @param nIterations Number of times to iterate the GBL fit.
-     * @param scattering Multiple scattering manager.
-     * @param bfield B-field
-     * @return The refitted track.
-     */
-    public static Track refitTrack(HelicalTrackFit helix, Collection<TrackerHit> stripHits, Collection<TrackerHit> hth, int nIterations, MultipleScattering scattering, double bfield) {
-        List<TrackerHit> allHthList = sortHits(hth);
-        List<TrackerHit> sortedStripHits = sortHits(stripHits);
-        FittedGblTrajectory fit = GblUtils.doGBLFit(helix, sortedStripHits, scattering, bfield, 0);
-        for (int i = 0; i < nIterations; i++) {
-            Track newTrack = MakeGblTracks.makeCorrectedTrack(fit, helix, allHthList, 0, bfield);
-            helix = TrackUtils.getHTF(newTrack);
-            fit = GblUtils.doGBLFit(helix, sortedStripHits, scattering, bfield, 0);
-        }
-        Track mergedTrack = MakeGblTracks.makeCorrectedTrack(fit, helix, allHthList, 0, bfield);
-        return mergedTrack;
-    }
-
-    public static FittedGblTrajectory doGBLFit(HelicalTrackFit htf, List<TrackerHit> stripHits, MultipleScattering _scattering, double bfield, int debug) {
-        List<GBLStripClusterData> stripData = makeStripData(htf, stripHits, _scattering, bfield, debug);
-        double bfac = Constants.fieldConversion * bfield;
-
-        FittedGblTrajectory fit = HpsGblRefitter.fit(stripData, bfac, debug > 0);
-        return fit;
-    }
-
-    public static List<GBLStripClusterData> makeStripData(HelicalTrackFit htf, List<TrackerHit> stripHits, MultipleScattering _scattering, double _B, int _debug) {
-        List<GBLStripClusterData> stripClusterDataList = new ArrayList<GBLStripClusterData>();
-
-        // Find scatter points along the path
-        MultipleScattering.ScatterPoints scatters = _scattering.FindHPSScatterPoints(htf);
-
-        if (_debug > 0) {
-            System.out.printf("perPar covariance matrix\n%s\n", htf.covariance().toString());
-        }
-
-        for (TrackerHit stripHit : stripHits) {
-            HelicalTrackStripGbl strip;
-            if (stripHit instanceof SiTrackerHitStrip1D) {
-                strip = new HelicalTrackStripGbl(makeDigiStrip((SiTrackerHitStrip1D) stripHit), true);
-            } else {
-                SiTrackerHitStrip1D newHit = new SiTrackerHitStrip1D(stripHit);
-                strip = new HelicalTrackStripGbl(makeDigiStrip(newHit), true);
-            }
-
-            // find Millepede layer definition from DetectorElement
-            HpsSiSensor sensor = (HpsSiSensor) ((RawTrackerHit) stripHit.getRawHits().get(0)).getDetectorElement();
-
-            int millepedeId = sensor.getMillepedeId();
-
-            if (_debug > 0) {
-                System.out.printf("layer %d millepede %d (DE=\"%s\", origin %s) \n", strip.layer(), millepedeId, sensor.getName(), strip.origin().toString());
-            }
-
-            //Center of the sensor
-            Hep3Vector origin = strip.origin();
-
-            //Find intercept point with sensor in tracking frame
-            Hep3Vector trkpos = TrackUtils.getHelixPlaneIntercept(htf, strip, Math.abs(_B));
-            if (trkpos == null) {
-                if (_debug > 0) {
-                    System.out.println("Can't find track intercept; use sensor origin");
-                }
-                trkpos = strip.origin();
-            }
-            if (_debug > 0) {
-                System.out.printf("trkpos at intercept [%.10f %.10f %.10f]\n", trkpos.x(), trkpos.y(), trkpos.z());
-            }
-
-            //GBLDATA
-            GBLStripClusterData stripData = new GBLStripClusterData(millepedeId);
-            //Add to output list
-            stripClusterDataList.add(stripData);
-
-            //path length to intercept
-            double s = HelixUtils.PathToXPlane(htf, trkpos.x(), 0, 0).get(0);
-            double s3D = s / Math.cos(Math.atan(htf.slope()));
-
-            //GBLDATA
-            stripData.setPath(s);
-            stripData.setPath3D(s3D);
-
-            //GBLDATA
-            stripData.setU(strip.u());
-            stripData.setV(strip.v());
-            stripData.setW(strip.w());
-
-            //Print track direction at intercept
-            Hep3Vector tDir = HelixUtils.Direction(htf, s);
-            double phi = htf.phi0() - s / htf.R();
-            double lambda = Math.atan(htf.slope());
-
-            //GBLDATA
-            stripData.setTrackDir(tDir);
-            stripData.setTrackPhi(phi);
-            stripData.setTrackLambda(lambda);
-
-            //Print residual in measurement system
-            // start by find the distance vector between the center and the track position
-            Hep3Vector vdiffTrk = VecOp.sub(trkpos, origin);
-
-            // then find the rotation from tracking to measurement frame
-            Hep3Matrix trkToStripRot = getTrackToStripRotation(sensor);
-
-            // then rotate that vector into the measurement frame to get the predicted measurement position
-            Hep3Vector trkpos_meas = VecOp.mult(trkToStripRot, vdiffTrk);
-
-            //GBLDATA
-            stripData.setMeas(strip.umeas());
-            stripData.setTrackPos(trkpos_meas);
-            stripData.setMeasErr(strip.du());
-
-            if (_debug > 1) {
-                System.out.printf("rotation matrix to meas frame\n%s\n", VecOp.toString(trkToStripRot));
-                System.out.printf("tPosGlobal %s origin %s\n", trkpos.toString(), origin.toString());
-                System.out.printf("tDiff %s\n", vdiffTrk.toString());
-                System.out.printf("tPosMeas %s\n", trkpos_meas.toString());
-            }
-
-            if (_debug > 0) {
-                System.out.printf("layer %d millePedeId %d uRes %.10f\n", strip.layer(), millepedeId, stripData.getMeas() - stripData.getTrackPos().x());
-            }
-
-            // find scattering angle
-            MultipleScattering.ScatterPoint scatter = scatters.getScatterPoint(((RawTrackerHit) strip.getStrip().rawhits().get(0)).getDetectorElement());
-            double scatAngle;
-
-            if (scatter != null) {
-                scatAngle = scatter.getScatterAngle().Angle();
-            } else {
-                if (_debug > 0) {
-                    System.out.printf("WARNING cannot find scatter for detector %s with strip cluster at %s\n", ((RawTrackerHit) strip.getStrip().rawhits().get(0)).getDetectorElement().getName(), strip.origin().toString());
-                }
-                scatAngle = GblUtils.estimateScatter(sensor, htf, _scattering, _B);
-            }
-
-            //GBLDATA
-            stripData.setScatterAngle(scatAngle);
-        }
-        return stripClusterDataList;
-    }
-
-    private static Hep3Matrix getTrackToStripRotation(SiSensor sensor) {
-        // This function transforms the hit to the sensor coordinates
-
-        // Transform from JLab frame to sensor frame (done through the RawTrackerHit)
-        SiSensorElectrodes electrodes = sensor.getReadoutElectrodes(ChargeCarrier.HOLE);
-        ITransform3D detToStrip = electrodes.getGlobalToLocal();
-        // Get rotation matrix
-        Hep3Matrix detToStripMatrix = detToStrip.getRotation().getRotationMatrix();
-        // Transformation between the JLAB and tracking coordinate systems
-        Hep3Matrix detToTrackMatrix = CoordinateTransformations.getMatrix();
-
-        return VecOp.mult(detToStripMatrix, VecOp.inverse(detToTrackMatrix));
-    }
-
-    private static HelicalTrackStrip makeDigiStrip(SiTrackerHitStrip1D h) {
-        SiTrackerHitStrip1D local = h.getTransformedHit(TrackerHitType.CoordinateSystem.SENSOR);
-        SiTrackerHitStrip1D global = h.getTransformedHit(TrackerHitType.CoordinateSystem.GLOBAL);
-
-        ITransform3D trans = local.getLocalToGlobal();
-        Hep3Vector org = trans.transformed(new BasicHep3Vector(0., 0., 0.));
-        Hep3Vector u = global.getMeasuredCoordinate();
-        Hep3Vector v = global.getUnmeasuredCoordinate();
-
-        //rotate to tracking frame
-        Hep3Vector neworigin = CoordinateTransformations.transformVectorToTracking(org);
-        Hep3Vector newu = CoordinateTransformations.transformVectorToTracking(u);
-        Hep3Vector newv = CoordinateTransformations.transformVectorToTracking(v);
-
-        double umeas = local.getPosition()[0];
-        double vmin = VecOp.dot(local.getUnmeasuredCoordinate(), local.getHitSegment().getStartPoint());
-        double vmax = VecOp.dot(local.getUnmeasuredCoordinate(), local.getHitSegment().getEndPoint());
-        double du = Math.sqrt(local.getCovarianceAsMatrix().diagonal(0));
-
-        //don't fill fields we don't use
-//        IDetectorElement de = h.getSensor();
-//        String det = getName(de);
-//        int lyr = getLayer(de);
-//        BarrelEndcapFlag be = getBarrelEndcapFlag(de);
-        double dEdx = h.getdEdx();
-        double time = h.getTime();
-        List<RawTrackerHit> rawhits = h.getRawHits();
-        HelicalTrackStrip strip = new HelicalTrackStrip(neworigin, newu, newv, umeas, du, vmin, vmax, dEdx, time, rawhits, null, -1, null);
-
-        return strip;
-    }
-
-    private static List<TrackerHit> sortHits(Collection<TrackerHit> hits) {
-        List<TrackerHit> hitList = new ArrayList<TrackerHit>(hits);
-        Collections.sort(hitList, new LayerComparator());
-        return hitList;
-    }
-
-    private static class LayerComparator implements Comparator<TrackerHit> {
-
-        @Override
-        public int compare(TrackerHit o1, TrackerHit o2) {
-            return Integer.compare(TrackUtils.getLayer(o1), TrackUtils.getLayer(o2));
-        }
-    }
 }

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/HpsGblRefitter.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/HpsGblRefitter.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/HpsGblRefitter.java	Tue Dec  1 15:55:47 2015
@@ -14,8 +14,10 @@
 import java.util.logging.Formatter;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import org.apache.commons.math3.util.Pair;
 
 import org.hps.recon.tracking.TrackUtils;
+import static org.hps.recon.tracking.gbl.MakeGblTracks.makeCorrectedTrack;
 import org.hps.recon.tracking.gbl.matrix.Matrix;
 import org.hps.recon.tracking.gbl.matrix.SymMatrix;
 import org.hps.recon.tracking.gbl.matrix.Vector;
@@ -25,8 +27,12 @@
 import org.lcsim.event.GenericObject;
 import org.lcsim.event.LCRelation;
 import org.lcsim.event.Track;
+import org.lcsim.event.base.BaseLCRelation;
 import org.lcsim.geometry.Detector;
 import org.lcsim.geometry.compact.converter.MilleParameter;
+import org.lcsim.lcio.LCIOConstants;
+import org.lcsim.recon.tracking.seedtracker.SeedCandidate;
+import org.lcsim.recon.tracking.seedtracker.SeedTrack;
 import org.lcsim.util.Driver;
 
 /**
@@ -40,21 +46,20 @@
 public class HpsGblRefitter extends Driver {
 
     static Formatter f = new BasicLogFormatter();
-    private static Logger LOGGER = Logger.getLogger(HpsGblRefitter.class.getPackage().getName());
+    private final static Logger LOGGER = Logger.getLogger(HpsGblRefitter.class.getPackage().getName());
     private boolean _debug = false;
     private final String trackCollectionName = "MatchedTracks";
     private final String track2GblTrackRelationName = "TrackToGBLTrack";
     private final String gblTrack2StripRelationName = "GBLTrackToStripData";
+    private final String outputTrackCollectionName = "GBLTracks";
 
     private MilleBinary mille;
     private String milleBinaryFileName = MilleBinary.DEFAULT_OUTPUT_FILE_NAME;
     private boolean writeMilleBinary = false;
 
-    private final MakeGblTracks _makeTracks;
-
     public void setDebug(boolean debug) {
         _debug = debug;
-        _makeTracks.setDebug(debug);
+        MakeGblTracks.setDebug(debug);
     }
 
     public void setMilleBinaryFileName(String filename) {
@@ -66,8 +71,7 @@
     }
 
     public HpsGblRefitter() {
-        _makeTracks = new MakeGblTracks();
-        _makeTracks.setDebug(_debug);
+        MakeGblTracks.setDebug(_debug);
         LOGGER.setLevel(Level.WARNING);
         //System.out.println("level " + LOGGER.getLevel().toString());
     }
@@ -179,7 +183,35 @@
         LOGGER.info(stripsGblMap.size() + " tracks in stripsGblMap");
         LOGGER.info(trackFits.size() + " fitted GBL tracks before adding to event");
 
-        _makeTracks.Process(event, trackFits, bfield);
+        List<Track> newTracks = new ArrayList<Track>();
+
+        List<GBLKinkData> kinkDataCollection = new ArrayList<GBLKinkData>();
+
+        List<LCRelation> kinkDataRelations = new ArrayList<LCRelation>();
+
+        LOGGER.info("adding " + trackFits.size() + " of fitted GBL tracks to the event");
+
+        for (FittedGblTrajectory fittedTraj : trackFits) {
+
+            SeedTrack seedTrack = (SeedTrack) fittedTraj.get_seed();
+            SeedCandidate trackseed = seedTrack.getSeedCandidate();
+
+            //  Create a new Track
+            Pair<Track, GBLKinkData> trk = makeCorrectedTrack(fittedTraj, trackseed.getHelix(), seedTrack.getTrackerHits(), seedTrack.getType(), bfield);
+
+            //  Add the track to the list of tracks
+            newTracks.add(trk.getFirst());
+            kinkDataCollection.add(trk.getSecond());
+            kinkDataRelations.add(new BaseLCRelation(trk.getSecond(), trk.getFirst()));
+        }
+
+        LOGGER.info("adding " + Integer.toString(newTracks.size()) + " Gbl tracks to event with " + event.get(Track.class, "MatchedTracks").size() + " matched tracks");
+
+        // Put the tracks back into the event and exit
+        int flag = 1 << LCIOConstants.TRBIT_HITS;
+        event.put(outputTrackCollectionName, newTracks, Track.class, flag);
+        event.put(GBLKinkData.DATA_COLLECTION, kinkDataCollection, GBLKinkData.class, 0);
+        event.put(GBLKinkData.DATA_RELATION_COLLECTION, kinkDataRelations, LCRelation.class, 0);
 
         if (_debug) {
             System.out.printf("%s: Done.\n", getClass().getSimpleName());

Modified: java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/MakeGblTracks.java
 =============================================================================
--- java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/MakeGblTracks.java	(original)
+++ java/branches/jeremy-dev/tracking/src/main/java/org/hps/recon/tracking/gbl/MakeGblTracks.java	Tue Dec  1 15:55:47 2015
@@ -1,53 +1,58 @@
 package org.hps.recon.tracking.gbl;
 
-import static org.hps.recon.tracking.gbl.GBLOutput.getPerToClPrj;
 import hep.physics.matrix.SymmetricMatrix;
 import hep.physics.vec.BasicHep3Vector;
 import hep.physics.vec.Hep3Matrix;
 import hep.physics.vec.Hep3Vector;
 import hep.physics.vec.VecOp;
-
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
 import org.apache.commons.math3.util.Pair;
+import org.hps.recon.tracking.CoordinateTransformations;
+import org.hps.recon.tracking.MultipleScattering;
 import org.hps.recon.tracking.TrackType;
+import org.hps.recon.tracking.TrackUtils;
+import static org.hps.recon.tracking.gbl.GBLOutput.getPerToClPrj;
 import org.hps.recon.tracking.gbl.matrix.Matrix;
 import org.hps.recon.tracking.gbl.matrix.SymMatrix;
 import org.hps.recon.tracking.gbl.matrix.Vector;
 import org.lcsim.constants.Constants;
-import org.lcsim.event.EventHeader;
+import org.lcsim.detector.ITransform3D;
+import org.lcsim.detector.tracker.silicon.ChargeCarrier;
+import org.lcsim.detector.tracker.silicon.HpsSiSensor;
+import org.lcsim.detector.tracker.silicon.SiSensor;
+import org.lcsim.detector.tracker.silicon.SiSensorElectrodes;
+import org.lcsim.event.RawTrackerHit;
 import org.lcsim.event.Track;
 import org.lcsim.event.TrackState;
 import org.lcsim.event.TrackerHit;
 import org.lcsim.event.base.BaseTrack;
 import org.lcsim.event.base.BaseTrackState;
 import org.lcsim.fit.helicaltrack.HelicalTrackFit;
-import org.lcsim.lcio.LCIOConstants;
-import org.lcsim.recon.tracking.seedtracker.SeedCandidate;
-import org.lcsim.recon.tracking.seedtracker.SeedTrack;
+import org.lcsim.fit.helicaltrack.HelicalTrackStrip;
+import org.lcsim.fit.helicaltrack.HelixUtils;
+import org.lcsim.recon.tracking.digitization.sisim.SiTrackerHitStrip1D;
+import org.lcsim.recon.tracking.digitization.sisim.TrackerHitType;
 
 /**
- * A class that creates track objects from fitted GBL trajectories and adds them
- * into the event.
+ * Utilities that create track objects from fitted GBL trajectories.
  *
  * @author Per Hansson Adrian <[log in to unmask]>
  *
  */
 public class MakeGblTracks {
 
-    private String _TrkCollectionName = "GBLTracks";
-    private static Logger LOGGER = Logger.getLogger(MakeGblTracks.class.getPackage().getName());
-
-    /**
-     * Creates a new instance of MakeTracks.
-     */
-    public MakeGblTracks() {
-    }
-
-    public void setDebug(boolean debug) {
+    private final static Logger LOGGER = Logger.getLogger(MakeGblTracks.class.getPackage().getName());
+
+    private MakeGblTracks() {
+    }
+
+    public static void setDebug(boolean debug) {
         if (debug) {
             LOGGER.setLevel(Level.INFO);
         } else {
@@ -55,40 +60,7 @@
         }
     }
 
-    /**
-     * Process a Gbl track and store it into the event
-     *
-     * @param event event header
-     * @param track Gbl trajectory
-     * @param seed SeedTrack
-     * @param bfield magnetic field (used to turn curvature into momentum)
-     */
-    public void Process(EventHeader event, List<FittedGblTrajectory> gblTrajectories, double bfield) {
-
-        List<Track> tracks = new ArrayList<Track>();
-
-        LOGGER.info("adding " + gblTrajectories.size() + " of fitted GBL tracks to the event");
-
-        for (FittedGblTrajectory fittedTraj : gblTrajectories) {
-
-            SeedTrack seedTrack = (SeedTrack) fittedTraj.get_seed();
-            SeedCandidate trackseed = seedTrack.getSeedCandidate();
-
-            //  Create a new Track
-            Track trk = makeCorrectedTrack(fittedTraj, trackseed.getHelix(), seedTrack.getTrackerHits(), seedTrack.getType(), bfield);
-
-            //  Add the track to the list of tracks
-            tracks.add(trk);
-        }
-
-        LOGGER.info("adding " + Integer.toString(tracks.size()) + " Gbl tracks to event with " + event.get(Track.class, "MatchedTracks").size() + " matched tracks");
-
-        // Put the tracks back into the event and exit
-        int flag = 1 << LCIOConstants.TRBIT_HITS;
-        event.put(_TrkCollectionName, tracks, Track.class, flag);
-    }
-
-    public static Track makeCorrectedTrack(FittedGblTrajectory fittedTraj, HelicalTrackFit helix, List<TrackerHit> trackHits, int trackType, double bfield) {
+    public static Pair<Track, GBLKinkData> makeCorrectedTrack(FittedGblTrajectory fittedTraj, HelicalTrackFit helix, List<TrackerHit> trackHits, int trackType, double bfield) {
         //  Initialize the reference point to the origin
         double[] ref = new double[]{0., 0., 0.};
 
@@ -112,6 +84,8 @@
         Pair<double[], SymmetricMatrix> correctedHelixParamsLast = getGblCorrectedHelixParameters(helix, fittedTraj.get_traj(), bfield, FittedGblTrajectory.GBLPOINT.LAST);
         TrackState stateLast = new BaseTrackState(correctedHelixParamsLast.getFirst(), ref, correctedHelixParamsLast.getSecond().asPackedArray(true), TrackState.AtLastHit, bfield);
         trk.getTrackStates().add(stateLast);
+
+        GBLKinkData kinkData = getKinks(fittedTraj.get_traj());
 
         // Set other info needed
         trk.setChisq(fittedTraj.get_chi2());
@@ -128,7 +102,7 @@
                 LOGGER.info(String.format("param %d: %.10f -> %.10f    helix-gbl= %f", i, helix.parameters()[i], trk.getTrackParameter(i), helix.parameters()[i] - trk.getTrackParameter(i)));
             }
         }
-        return trk;
+        return new Pair<Track, GBLKinkData>(trk, kinkData);
     }
 
     /**
@@ -285,8 +259,239 @@
         return new Pair<double[], SymmetricMatrix>(parameters_gbl, cov);
     }
 
-    public void setTrkCollectionName(String name) {
-        _TrkCollectionName = name;
-    }
-
+    public static GBLKinkData getKinks(GblTrajectory traj) {
+
+        // get corrections from GBL fit
+        Vector locPar = new Vector(5);
+        SymMatrix locCov = new SymMatrix(5);
+        float[] lambdaKinks = new float[traj.getNumPoints() - 1];
+        double[] phiKinks = new double[traj.getNumPoints() - 1];
+
+        double oldPhi = 0, oldLambda = 0;
+        for (int i = 0; i < traj.getNumPoints(); i++) {
+            traj.getResults(i + 1, locPar, locCov); // vertex point
+            double newPhi = locPar.get(FittedGblTrajectory.GBLPARIDX.XTPRIME.getValue());
+            double newLambda = locPar.get(FittedGblTrajectory.GBLPARIDX.YTPRIME.getValue());
+            if (i > 0) {
+                lambdaKinks[i - 1] = (float) (newLambda - oldLambda);
+                phiKinks[i - 1] = newPhi - oldPhi;
+            }
+            oldPhi = newPhi;
+            oldLambda = newLambda;
+        }
+
+        return new GBLKinkData(lambdaKinks, phiKinks);
+    }
+
+    /**
+     * Do a GBL fit to an arbitrary set of strip hits, with a starting value of
+     * the helix parameters.
+     *
+     * @param helix Initial helix parameters. Only track parameters are used
+     * (not covariance)
+     * @param stripHits Strip hits to be used for the GBL fit. Does not need to
+     * be in sorted order.
+     * @param hth Stereo hits for the track's hit list (these are not used in
+     * the GBL fit). Does not need to be in sorted order.
+     * @param nIterations Number of times to iterate the GBL fit.
+     * @param scattering Multiple scattering manager.
+     * @param bfield B-field
+     * @return The refitted track.
+     */
+    public static Pair<Track, GBLKinkData> refitTrack(HelicalTrackFit helix, Collection<TrackerHit> stripHits, Collection<TrackerHit> hth, int nIterations, MultipleScattering scattering, double bfield) {
+        List<TrackerHit> allHthList = sortHits(hth);
+        List<TrackerHit> sortedStripHits = sortHits(stripHits);
+        FittedGblTrajectory fit = MakeGblTracks.doGBLFit(helix, sortedStripHits, scattering, bfield, 0);
+        for (int i = 0; i < nIterations; i++) {
+            Pair<Track, GBLKinkData> newTrack = MakeGblTracks.makeCorrectedTrack(fit, helix, allHthList, 0, bfield);
+            helix = TrackUtils.getHTF(newTrack.getFirst());
+            fit = MakeGblTracks.doGBLFit(helix, sortedStripHits, scattering, bfield, 0);
+        }
+        Pair<Track, GBLKinkData> mergedTrack = MakeGblTracks.makeCorrectedTrack(fit, helix, allHthList, 0, bfield);
+        return mergedTrack;
+    }
+
+    public static FittedGblTrajectory doGBLFit(HelicalTrackFit htf, List<TrackerHit> stripHits, MultipleScattering _scattering, double bfield, int debug) {
+        List<GBLStripClusterData> stripData = makeStripData(htf, stripHits, _scattering, bfield, debug);
+        double bfac = Constants.fieldConversion * bfield;
+
+        FittedGblTrajectory fit = HpsGblRefitter.fit(stripData, bfac, debug > 0);
+        return fit;
+    }
+
+    public static List<GBLStripClusterData> makeStripData(HelicalTrackFit htf, List<TrackerHit> stripHits, MultipleScattering _scattering, double _B, int _debug) {
+        List<GBLStripClusterData> stripClusterDataList = new ArrayList<GBLStripClusterData>();
+
+        // Find scatter points along the path
+        MultipleScattering.ScatterPoints scatters = _scattering.FindHPSScatterPoints(htf);
+
+        if (_debug > 0) {
+            System.out.printf("perPar covariance matrix\n%s\n", htf.covariance().toString());
+        }
+
+        for (TrackerHit stripHit : stripHits) {
+            HelicalTrackStripGbl strip;
+            if (stripHit instanceof SiTrackerHitStrip1D) {
+                strip = new HelicalTrackStripGbl(makeDigiStrip((SiTrackerHitStrip1D) stripHit), true);
+            } else {
+                SiTrackerHitStrip1D newHit = new SiTrackerHitStrip1D(stripHit);
+                strip = new HelicalTrackStripGbl(makeDigiStrip(newHit), true);
+            }
+
+            // find Millepede layer definition from DetectorElement
+            HpsSiSensor sensor = (HpsSiSensor) ((RawTrackerHit) stripHit.getRawHits().get(0)).getDetectorElement();
+
+            int millepedeId = sensor.getMillepedeId();
+
+            if (_debug > 0) {
+                System.out.printf("layer %d millepede %d (DE=\"%s\", origin %s) \n", strip.layer(), millepedeId, sensor.getName(), strip.origin().toString());
+            }
+
+            //Center of the sensor
+            Hep3Vector origin = strip.origin();
+
+            //Find intercept point with sensor in tracking frame
+            Hep3Vector trkpos = TrackUtils.getHelixPlaneIntercept(htf, strip, Math.abs(_B));
+            if (trkpos == null) {
+                if (_debug > 0) {
+                    System.out.println("Can't find track intercept; use sensor origin");
+                }
+                trkpos = strip.origin();
+            }
+            if (_debug > 0) {
+                System.out.printf("trkpos at intercept [%.10f %.10f %.10f]\n", trkpos.x(), trkpos.y(), trkpos.z());
+            }
+
+            //GBLDATA
+            GBLStripClusterData stripData = new GBLStripClusterData(millepedeId);
+            //Add to output list
+            stripClusterDataList.add(stripData);
+
+            //path length to intercept
+            double s = HelixUtils.PathToXPlane(htf, trkpos.x(), 0, 0).get(0);
+            double s3D = s / Math.cos(Math.atan(htf.slope()));
+
+            //GBLDATA
+            stripData.setPath(s);
+            stripData.setPath3D(s3D);
+
+            //GBLDATA
+            stripData.setU(strip.u());
+            stripData.setV(strip.v());
+            stripData.setW(strip.w());
+
+            //Print track direction at intercept
+            Hep3Vector tDir = HelixUtils.Direction(htf, s);
+            double phi = htf.phi0() - s / htf.R();
+            double lambda = Math.atan(htf.slope());
+
+            //GBLDATA
+            stripData.setTrackDir(tDir);
+            stripData.setTrackPhi(phi);
+            stripData.setTrackLambda(lambda);
+
+            //Print residual in measurement system
+            // start by find the distance vector between the center and the track position
+            Hep3Vector vdiffTrk = VecOp.sub(trkpos, origin);
+
+            // then find the rotation from tracking to measurement frame
+            Hep3Matrix trkToStripRot = getTrackToStripRotation(sensor);
+
+            // then rotate that vector into the measurement frame to get the predicted measurement position
+            Hep3Vector trkpos_meas = VecOp.mult(trkToStripRot, vdiffTrk);
+
+            //GBLDATA
+            stripData.setMeas(strip.umeas());
+            stripData.setTrackPos(trkpos_meas);
+            stripData.setMeasErr(strip.du());
+
+            if (_debug > 1) {
+                System.out.printf("rotation matrix to meas frame\n%s\n", VecOp.toString(trkToStripRot));
+                System.out.printf("tPosGlobal %s origin %s\n", trkpos.toString(), origin.toString());
+                System.out.printf("tDiff %s\n", vdiffTrk.toString());
+                System.out.printf("tPosMeas %s\n", trkpos_meas.toString());
+            }
+
+            if (_debug > 0) {
+                System.out.printf("layer %d millePedeId %d uRes %.10f\n", strip.layer(), millepedeId, stripData.getMeas() - stripData.getTrackPos().x());
+            }
+
+            // find scattering angle
+            MultipleScattering.ScatterPoint scatter = scatters.getScatterPoint(((RawTrackerHit) strip.getStrip().rawhits().get(0)).getDetectorElement());
+            double scatAngle;
+
+            if (scatter != null) {
+                scatAngle = scatter.getScatterAngle().Angle();
+            } else {
+                if (_debug > 0) {
+                    System.out.printf("WARNING cannot find scatter for detector %s with strip cluster at %s\n", ((RawTrackerHit) strip.getStrip().rawhits().get(0)).getDetectorElement().getName(), strip.origin().toString());
+                }
+                scatAngle = GblUtils.estimateScatter(sensor, htf, _scattering, _B);
+            }
+
+            //GBLDATA
+            stripData.setScatterAngle(scatAngle);
+        }
+        return stripClusterDataList;
+    }
+
+    private static Hep3Matrix getTrackToStripRotation(SiSensor sensor) {
+        // This function transforms the hit to the sensor coordinates
+
+        // Transform from JLab frame to sensor frame (done through the RawTrackerHit)
+        SiSensorElectrodes electrodes = sensor.getReadoutElectrodes(ChargeCarrier.HOLE);
+        ITransform3D detToStrip = electrodes.getGlobalToLocal();
+        // Get rotation matrix
+        Hep3Matrix detToStripMatrix = detToStrip.getRotation().getRotationMatrix();
+        // Transformation between the JLAB and tracking coordinate systems
+        Hep3Matrix detToTrackMatrix = CoordinateTransformations.getMatrix();
+
+        return VecOp.mult(detToStripMatrix, VecOp.inverse(detToTrackMatrix));
+    }
+
+    private static HelicalTrackStrip makeDigiStrip(SiTrackerHitStrip1D h) {
+        SiTrackerHitStrip1D local = h.getTransformedHit(TrackerHitType.CoordinateSystem.SENSOR);
+        SiTrackerHitStrip1D global = h.getTransformedHit(TrackerHitType.CoordinateSystem.GLOBAL);
+
+        ITransform3D trans = local.getLocalToGlobal();
+        Hep3Vector org = trans.transformed(new BasicHep3Vector(0., 0., 0.));
+        Hep3Vector u = global.getMeasuredCoordinate();
+        Hep3Vector v = global.getUnmeasuredCoordinate();
+
+        //rotate to tracking frame
+        Hep3Vector neworigin = CoordinateTransformations.transformVectorToTracking(org);
+        Hep3Vector newu = CoordinateTransformations.transformVectorToTracking(u);
+        Hep3Vector newv = CoordinateTransformations.transformVectorToTracking(v);
+
+        double umeas = local.getPosition()[0];
+        double vmin = VecOp.dot(local.getUnmeasuredCoordinate(), local.getHitSegment().getStartPoint());
+        double vmax = VecOp.dot(local.getUnmeasuredCoordinate(), local.getHitSegment().getEndPoint());
+        double du = Math.sqrt(local.getCovarianceAsMatrix().diagonal(0));
+
+        //don't fill fields we don't use
+//        IDetectorElement de = h.getSensor();
+//        String det = getName(de);
+//        int lyr = getLayer(de);
+//        BarrelEndcapFlag be = getBarrelEndcapFlag(de);
+        double dEdx = h.getdEdx();
+        double time = h.getTime();
+        List<RawTrackerHit> rawhits = h.getRawHits();
+        HelicalTrackStrip strip = new HelicalTrackStrip(neworigin, newu, newv, umeas, du, vmin, vmax, dEdx, time, rawhits, null, -1, null);
+
+        return strip;
+    }
+
+    private static List<TrackerHit> sortHits(Collection<TrackerHit> hits) {
+        List<TrackerHit> hitList = new ArrayList<TrackerHit>(hits);
+        Collections.sort(hitList, new LayerComparator());
+        return hitList;
+    }
+
+    private static class LayerComparator implements Comparator<TrackerHit> {
+
+        @Override
+        public int compare(TrackerHit o1, TrackerHit o2) {
+            return Integer.compare(TrackUtils.getLayer(o1), TrackUtils.getLayer(o2));
+        }
+    }
 }

Copied: java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfFitFunction.java (from r3964, java/trunk/users/src/main/java/org/hps/users/baltzell/RfFitFunction.java)
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/baltzell/RfFitFunction.java	(original)
+++ java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfFitFunction.java	Tue Dec  1 15:55:47 2015
@@ -4,10 +4,10 @@
 
 /*
  * Function for fitting the leading edge of the RF waveform.
+ * Straight line fit
  */
 public class RfFitFunction extends AbstractIFunction {
-	protected double pedestal=0;
-	protected double time=0;
+	protected double intercept=0;
 	protected double slope=0;
 	public RfFitFunction() {
 		this("");
@@ -15,22 +15,21 @@
 	public RfFitFunction(String title) {
 		super();
 		this.variableNames=new String[]{"time"};
-		this.parameterNames=new String[]{"pedestal","time","slope"};
+		this.parameterNames=new String[]{"intercept","slope"};
+
 		init(title);
 	}
 	public double value(double [] v) {
-		return  pedestal + (v[0]-time)*slope;
+		return  intercept + (v[0])*slope;
 	}
 	public void setParameters(double[] pars) throws IllegalArgumentException {
 		super.setParameters(pars);
-		pedestal=pars[0];
-		time=pars[1];
-		slope=pars[2];
+		intercept=pars[0];
+		slope=pars[1];
 	}
 	public void setParameter(String key,double value) throws IllegalArgumentException{
 		super.setParameter(key,value);
-		if      (key.equals("pedestal")) pedestal=value;
-		else if (key.equals("time"))     time=value;
+		if      (key.equals("intercept")) intercept=value;
 		else if (key.equals("slope"))    slope=value;
 	}
 }

Copied: java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfFitterDriver.java (from r3964, java/trunk/users/src/main/java/org/hps/users/baltzell/RfFitterDriver.java)
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/baltzell/RfFitterDriver.java	(original)
+++ java/branches/jeremy-dev/users/src/main/java/org/hps/users/baltzell/RfFitterDriver.java	Tue Dec  1 15:55:47 2015
@@ -29,6 +29,7 @@
 	static final int SLOT=13;
 	static final int CHANNELS[]={0,1};
 	static final double NSPERSAMPLE=4;
+		
 
 	// boilerplate:
     AIDA aida = AIDA.defaultInstance();
@@ -61,8 +62,12 @@
 					
 					// we found a RF readout, fit it:
 					foundRf=true;
-					IFitResult fit=fitPulse(hit);
-  				    times[ii]=NSPERSAMPLE*fit.fittedParameter("time");
+					times[ii] = fitPulse(hit);
+					if (ii==1){
+						
+						System.out.println(times[1]-times[0]);
+					}
+					  				    
 					break;
 				}
 			}
@@ -79,30 +84,85 @@
 	/*
 	 * Perform the fit to the RF pulse:
 	 */
-	public IFitResult fitPulse(FADCGenericHit hit) {
-
+	public double fitPulse(FADCGenericHit hit) {
 		fitData.clear();
 		final int adcSamples[]=hit.getData();
+		//stores the number of peaks
+		int iz=0;
+		int peakBin[]={-999,-999};
+		final int threshold = 300;	
+		double fitThresh[]={-999,-999};
+		double pedVal[]={-999,-999};
 		
-		// TODO: only add those ADC values which are to be fitted:
-		for (int ii=0; ii<adcSamples.length; ii++) {
-			final int jj=fitData.size();
-			fitData.addPoint();
-			fitData.point(jj).coordinate(0).setValue(ii);
-			fitData.point(jj).coordinate(1).setValue(adcSamples[ii]);
-			fitData.point(jj).coordinate(1).setErrorMinus(NOISE);
-			fitData.point(jj).coordinate(1).setErrorPlus(NOISE);
+		// Look for bins containing the peaks (2-3 peaks)
+		for (int ii=4; ii<adcSamples.length; ii++) {
+			// After 2 peaks, stop looking for more
+			if (iz==2){break;}
+			if ((adcSamples[ii+1]>0) && (adcSamples[ii-1]>0) && (adcSamples[ii]>threshold) && ii>8){
+				if ((adcSamples[ii]>adcSamples[ii+1]) && (adcSamples[ii]>=adcSamples[ii-1]) ){
+					
+					peakBin[iz]=ii;
+					iz++;
+				}
+			}
 		}
 		
-		// TODO: properly initialize fit parameters:
-		fitFunction.setParameter("time",0.0);
-		fitFunction.setParameter("pedestal",0.0);
-		fitFunction.setParameter("slope",100.0);
+		
+		int jj=0;
+		// Choose peak closest to center of window (second peak, ik=1)
+		final int ik=1;
+		pedVal[ik] = (adcSamples[peakBin[ik]-6]+adcSamples[peakBin[ik]-7]+adcSamples[peakBin[ik]-8]+adcSamples[peakBin[ik]-9])/4.0;
+		fitThresh[ik]= (adcSamples[peakBin[ik]]+pedVal[ik])/3.0;
 	
+		// Initial values: we find/fit 3 points:
+		double itime[] = {-999,-999,-999};
+		double ifadc[] = {-999,-999,-999};
+		
+		// Find the points of the peak bin to peak bin-5 
+		for (int ll=0; ll<5; ll++){	
+			if ((adcSamples[peakBin[ik]-5+ll]) > fitThresh[ik]){
+				// One point is below fit threshold and two points are above	
+				if(jj==0 && (adcSamples[peakBin[ik]-6+ll] > pedVal[ik])){
+					final int zz=fitData.size();	
+					fitData.addPoint();
+					itime[zz] = peakBin[ik]-6+ll;
+					ifadc[zz] = adcSamples[peakBin[ik]-6+ll];
+					fitData.point(zz).coordinate(0).setValue(peakBin[ik]-6+ll);
+					fitData.point(zz).coordinate(1).setValue(adcSamples[peakBin[ik]-6+ll]);
+					fitData.point(zz).coordinate(1).setErrorMinus(NOISE);
+					fitData.point(zz).coordinate(1).setErrorPlus(NOISE);		
+					jj++;	
+				}
+				final int zz=fitData.size();	
+				fitData.addPoint();
+				itime[zz] = peakBin[ik]-5+ll;
+				ifadc[zz] = adcSamples[peakBin[ik]-5+ll];
+				fitData.point(zz).coordinate(0).setValue(peakBin[ik]-5+ll);
+				fitData.point(zz).coordinate(1).setValue(adcSamples[peakBin[ik]-5+ll]);
+				fitData.point(zz).coordinate(1).setErrorMinus(NOISE);
+				fitData.point(zz).coordinate(1).setErrorPlus(NOISE);
+					
+				jj++;
+				if (jj==3) {break;}					
+			}
+		}
+		
+		double islope = ((double)(ifadc[2]-ifadc[0]))/(itime[2]-itime[0]);
+		double icept = ifadc[1] - islope*itime[1];
+		// Initialize fit parameters:
+		fitFunction.setParameter("intercept",icept);
+		fitFunction.setParameter("slope",islope);
+
 		// this used to be turned on somewhere else on every event, dunno if it still is:
 		//Logger.getLogger("org.freehep.math.minuit").setLevel(Level.OFF);
+	
+		IFitResult fitResults = fitter.fit(fitData,fitFunction);
 		
-		return fitter.fit(fitData,fitFunction);
+		// Read the time value at this location on the fit:
+		double halfVal = (adcSamples[peakBin[1]]+pedVal[1])/2.0;	
+	
+		return NSPERSAMPLE*(halfVal-fitResults.fittedParameter("intercept"))/fitResults.fittedParameter("slope");
+			
 	}
-	
+		
 }

Modified: java/branches/jeremy-dev/users/src/main/java/org/hps/users/meeg/SvtChargeIntegrator.java
 =============================================================================
--- java/branches/jeremy-dev/users/src/main/java/org/hps/users/meeg/SvtChargeIntegrator.java	(original)
+++ java/branches/jeremy-dev/users/src/main/java/org/hps/users/meeg/SvtChargeIntegrator.java	Tue Dec  1 15:55:47 2015
@@ -137,7 +137,7 @@
 
                 if (runNum != currentRun) {
                     RunManager.getRunManager().setRun(runNum);
-                    if (!RunManager.getRunManager().runExists() || RunManager.getRunManager().getTriggerConfig().getTiTimeOffset() == null) {
+                    if (!RunManager.getRunManager().runExists() || RunManager.getRunManager().getRunSummary().getTiTimeOffset() == null) {
                         continue;
                     }
                     try {
@@ -150,7 +150,7 @@
                         continue;
                     }
 
-                    tiTimeOffset = RunManager.getRunManager().getTriggerConfig().getTiTimeOffset();
+                    tiTimeOffset = RunManager.getRunManager().getRunSummary().getTiTimeOffset();
 
                     for (final SvtAlignmentConstant constant : alignmentConstants) {
                         switch (constant.getParameter()) {