LISTSERV mailing list manager LISTSERV 16.5

Help for HPS-SVN Archives


HPS-SVN Archives

HPS-SVN Archives


HPS-SVN@LISTSERV.SLAC.STANFORD.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

HPS-SVN Home

HPS-SVN Home

HPS-SVN  February 2016

HPS-SVN February 2016

Subject:

r4196 - in /java/branches/jeremy-dev: conditions/src/main/java/org/hps/conditions/run/ crawler/src/main/java/org/hps/crawler/ crawler/src/main/python/ crawler/src/main/python/crawler/ distribution/ logging/src/main/resources/org/hps/logging/config/ record-util/src/main/java/org/hps/record/daqconfig/ record-util/src/main/java/org/hps/record/triggerbank/ run-database/src/main/java/org/hps/run/database/ run-database/src/test/java/org/hps/run/database/

From:

[log in to unmask]

Reply-To:

Notification of commits to the hps svn repository <[log in to unmask]>

Date:

Wed, 10 Feb 2016 22:26:53 -0000

Content-Type:

text/plain

Parts/Attachments:

Parts/Attachments

text/plain (2044 lines)

Author: [log in to unmask]
Date: Wed Feb 10 14:26:49 2016
New Revision: 4196

Log:
Update dev branch with changes for run db, datacat and file crawling.

Added:
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/MetadataWriter.java
    java/branches/jeremy-dev/crawler/src/main/python/
    java/branches/jeremy-dev/crawler/src/main/python/crawler/
    java/branches/jeremy-dev/crawler/src/main/python/crawler/create_dataset_from_metadata.py   (with props)
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetCalculator.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/AbstractRunBuilder.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatabaseUpdater.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatBuilder.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatUtilities.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/LivetimeBuilder.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SpreadsheetBuilder.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/TriggerConfigBuilder.java
    java/branches/jeremy-dev/run-database/src/test/java/org/hps/run/database/RunBuilderTest.java
Modified:
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatAddFile.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatHelper.java
    java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/EvioMetadataReader.java
    java/branches/jeremy-dev/distribution/pom.xml
    java/branches/jeremy-dev/logging/src/main/resources/org/hps/logging/config/test_logging.properties
    java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/TriggerConfigEvioProcessor.java
    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/TriggerConfigData.java
    java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDao.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/RunDatabaseBuilder.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

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	Wed Feb 10 14:26:49 2016
@@ -99,11 +99,14 @@
      * @param file the CSV file
      */
     public RunSpreadsheet(final File file) {
+        if (file == null) {
+            throw new IllegalArgumentException("The file argument is null.");
+        }
         this.file = file;
         try {
             this.fromCsv(this.file);
         } catch (final Exception e) {
-            throw new RuntimeException();
+            throw new RuntimeException("Failed to parse run spreadsheet.", e);
         }
     }
 

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatAddFile.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatAddFile.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatAddFile.java	Wed Feb 10 14:26:49 2016
@@ -13,21 +13,18 @@
 import org.srs.datacat.model.DatasetModel;
 
 /**
- * Command line file crawler for populating the data catalog.
+ * Command line tool for adding files to the data catalog.
  *
  * @author Jeremy McCormick, SLAC
  */
 public final class DatacatAddFile {
 
-    /**
-     * Setup the logger.
-     */
     private static final Logger LOGGER = Logger.getLogger(DatacatCrawler.class.getPackage().getName());
     
-    private List<File> paths;
+    private List<File> paths = new ArrayList<File>();
     
     /**
-     * Command line options for the crawler.
+     * Command line options.
      */
     private static final Options OPTIONS = new Options();
 
@@ -95,7 +92,6 @@
                         
             // List of paths.
             if (!cl.getArgList().isEmpty()) {
-                paths = new ArrayList<File>();
                 for (String arg : cl.getArgList()) {                    
                     paths.add(new File(arg));
                 }
@@ -129,7 +125,7 @@
     }
 
     /**
-     * Print the usage statement for this tool to the console and then exit the program.
+     * Print the usage statement and then exit.
      */
     private void printUsage() {
         final HelpFormatter help = new HelpFormatter();

Modified: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatHelper.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatHelper.java	(original)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/DatacatHelper.java	Wed Feb 10 14:26:49 2016
@@ -37,17 +37,17 @@
     /*
      * Static map of strings to file formats.
      */
-    private static final Map<String, FileFormat> formatMap = new HashMap<String, FileFormat>();
+    private static final Map<String, FileFormat> FORMATS = new HashMap<String, FileFormat>();
     static {
         for (final FileFormat format : FileFormat.values()) {
-            formatMap.put(format.extension(), format);
+            FORMATS.put(format.extension(), format);
         }
     }
     
     /* 
      * System metadata fields. 
      */
-    private static final Set<String> SYSTEM_METADATA = new HashSet<String>();
+    static final Set<String> SYSTEM_METADATA = new HashSet<String>();
     static {
         SYSTEM_METADATA.add("eventCount");
         SYSTEM_METADATA.add("size");
@@ -56,9 +56,13 @@
         SYSTEM_METADATA.add("checksum");
         SYSTEM_METADATA.add("scanStatus");
     }
-   
-    /**
-     * Create metadata for a file using its specific reader.
+    
+    static final boolean isSystemMetadata(String name) {
+        return SYSTEM_METADATA.contains(name);
+    }
+           
+    /**
+     * Create metadata for a file using its {@link FileMetadataReader}.
      *
      * @param file the file
      * @return the metadata for the file
@@ -82,6 +86,7 @@
         } catch (final IOException e) {
             throw new RuntimeException(e);
         }
+        metadata.put("scanStatus", "OK");
         return metadata;
     }
 
@@ -128,7 +133,7 @@
             name = stripEvioFileNumber(name);
         }
         final String extension = name.substring(name.lastIndexOf(".") + 1);
-        return formatMap.get(extension);
+        return FORMATS.get(extension);
     }
 
     /**

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	Wed Feb 10 14:26:49 2016
@@ -11,11 +11,11 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.hps.record.evio.EventTagConstant;
 import org.hps.record.evio.EvioEventUtilities;
 import org.hps.record.evio.EvioFileUtilities;
 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;
@@ -24,13 +24,10 @@
 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.
+ * Creates detailed metadata for the datacat from an EVIO input file.
  * 
  * @author Jeremy McCormick, SLAC
  */
-// TODO: add physics events count
-// TODO: remove trigger rate and TI time offset 
 final class EvioMetadataReader implements FileMetadataReader {
 
     /**
@@ -42,11 +39,6 @@
      * 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.
@@ -57,7 +49,7 @@
     @Override
     public Map<String, Object> getMetadata(final File file) throws IOException {
         
-        long events = 0;
+        long totalEvents = 0;
         int physicsEvents = 0;
         int badEvents = 0;
         int blinded = 0;
@@ -66,12 +58,12 @@
         Integer lastHeadTimestamp = null;
         Integer lastPhysicsEvent = null;
         Integer firstPhysicsEvent = null;
+        Integer prestartTimestamp = null;
+        Integer endTimestamp = null;
+        Integer goTimestamp = null;
         Double triggerRate = null;
-        long lastTI = 0;
-        long minTIDelta = 0;
-        long maxTIDelta = 0;
-        long firstTI = 0;
-        
+        
+        // Processor for calculating TI time offsets.
         TiTimeOffsetEvioProcessor tiProcessor = new TiTimeOffsetEvioProcessor();
 
         // Create map for counting trigger types.
@@ -83,7 +75,7 @@
         // Get the file number from the name.
         final int fileNumber = EvioFileUtilities.getSequenceFromName(file);
 
-        // Files with a sequence number that is not divisible by 10 are blinded (Eng Run 2015 scheme).
+        // File numbers indivisible by 10 are blinded (Eng Run 2015 scheme).
         if (!(fileNumber % 10 == 0)) {
             blinded = 1;
         }
@@ -106,22 +98,22 @@
             EvioEvent evioEvent = null;
 
             // Event read loop.
-            fileLoop: while (true) {
+            eventLoop: while (true) {
                 try {
                     // Parse next event.
                     evioEvent = evioReader.parseNextEvent();
 
                     // End of file.
                     if (evioEvent == null) {
-                        LOGGER.fine("EOF after " + events + " events");
-                        break fileLoop;
+                        LOGGER.fine("EOF after " + totalEvents + " events.");
+                        break eventLoop;
                     }
                     
                     // Increment event count (doesn't count events that can't be parsed).
-                    ++events;
+                    ++totalEvents;
 
                     // Debug print event number and tag.
-                    LOGGER.finest("parsed event " + evioEvent.getEventNumber() + " with tag 0x"
+                    LOGGER.finest("Parsed event " + evioEvent.getEventNumber() + " with tag 0x"
                             + String.format("%08x", evioEvent.getHeader().getTag()));
 
                     // Get head bank.
@@ -139,7 +131,7 @@
                                 // First header timestamp.
                                 if (firstHeadTimestamp == null) {
                                     firstHeadTimestamp = thisTimestamp;
-                                    LOGGER.finer("first head timestamp " + firstHeadTimestamp + " from event "
+                                    LOGGER.finer("First head timestamp " + firstHeadTimestamp + " from event "
                                             + evioEvent.getEventNumber());
                                 }
 
@@ -151,31 +143,12 @@
                             if (run == null) {
                                 if (headBankData[1] != 0) {
                                     run = (long) headBankData[1];
-                                    LOGGER.finer("run " + run + " from event " + evioEvent.getEventNumber());
+                                    LOGGER.finer("Run number " + 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);
@@ -188,11 +161,24 @@
                             // Set the first physics event.
                             if (firstPhysicsEvent == null) {
                                 firstPhysicsEvent = eventIdData[0];
-                                LOGGER.finer("set first physics event " + firstPhysicsEvent);
+                                LOGGER.finer("Set first physics event " + firstPhysicsEvent);
                             }
                         }
                         
                         ++physicsEvents;
+                    } else if (EvioEventUtilities.isControlEvent(evioEvent)) {
+                        int[] controlData = EvioEventUtilities.getControlEventData(evioEvent);
+                        if (controlData[0] != 0) {
+                            if (EventTagConstant.PRESTART.isEventTag(evioEvent)) {
+                                prestartTimestamp = controlData[0];
+                            }                        
+                            if (EventTagConstant.GO.isEventTag(evioEvent)) {
+                                goTimestamp = controlData[0];
+                            }
+                            if (EventTagConstant.END.isEventTag(evioEvent)) {
+                                endTimestamp = controlData[0];
+                            }
+                        }
                     }
 
                     // Count trigger types for this event.
@@ -200,17 +186,16 @@
                     for (TriggerType mask : triggerTypes) {
                         int count = triggerCounts.get(mask) + 1;
                         triggerCounts.put(mask, count);
-                        LOGGER.finest("incremented " + mask.name() + " to " + count);
+                        LOGGER.finest("Incremented " + mask.name() + " to " + count);
                     }
                     
                     // Activate TI time offset processor.
                     tiProcessor.process(evioEvent);
                     
-                //} catch (IOException | NegativeArraySizeException | EvioException e) {
                 } catch (Exception e) {  
-                    // Trap event processing errors.
+                    // Trap all event processing errors.
                     badEvents++;
-                    LOGGER.warning("error processing EVIO event " + evioEvent.getEventNumber());
+                    LOGGER.warning("Error processing EVIO event " + evioEvent.getEventNumber());
                 }
             }
         } catch (final EvioException e) {
@@ -222,22 +207,23 @@
                 try {
                     evioReader.close();
                 } catch (IOException e) {
-                    LOGGER.log(Level.WARNING, "error closing EVIO reader", e);
+                    LOGGER.log(Level.WARNING, "Error closing EVIO reader", e);
                 }
             }
         }
 
-        LOGGER.info("done reading " + events + " events from " + file.getPath());
+        LOGGER.info("Done reading " + totalEvents + " events from " + file.getPath());
 
         // Rough trigger rate calculation.
         try {
-            if (firstHeadTimestamp != null && lastHeadTimestamp != null && events > 0) {
-                triggerRate = calculateTriggerRate(firstHeadTimestamp, lastHeadTimestamp, events);
+            if (firstHeadTimestamp != null && lastHeadTimestamp != null && totalEvents > 0 
+                    && (firstHeadTimestamp - lastHeadTimestamp != 0)) {
+                triggerRate = calculateTriggerRate(firstHeadTimestamp, lastHeadTimestamp, totalEvents);
             } else {
                 LOGGER.log(Level.WARNING, "Missing information for calculating trigger rate.");
             }
         } catch (Exception e) {
-            LOGGER.log(Level.WARNING, "Error calculating trigger rate.", e);
+            LOGGER.log(Level.WARNING, "Error calculating the trigger rate.", e);
         }
 
         // Create and fill the metadata map.
@@ -248,15 +234,15 @@
                 run = new Long(EvioFileUtilities.getRunFromName(file));
             }
         } catch (Exception e) {
-            throw new RuntimeException("Unable to determine run number from data or file name.", e);
-        }
-
-        // Set built-in system metadata.
+            throw new RuntimeException("Failed to get run number from event data or file name.", e);
+        }
+
+        // Set locationExtras metadata.
         metadataMap.put("runMin", run);
         metadataMap.put("runMax", run);
-        metadataMap.put("eventCount", events);
+        metadataMap.put("eventCount", totalEvents);
         metadataMap.put("size", size);
-        metadataMap.put("checksum", checksum);
+        metadataMap.put("checksum", checksum);     
         
         // File sequence number.
         metadataMap.put("FILE", fileNumber);
@@ -267,54 +253,52 @@
         // First and last timestamps which may come from control or physics events.
         if (firstHeadTimestamp != null) {
             metadataMap.put("FIRST_HEAD_TIMESTAMP", firstHeadTimestamp);
-        } else {
-            metadataMap.put("FIRST_HEAD_TIMESTAMP", 0L);
-        }
+        } 
         
         if (lastHeadTimestamp != null) {
             metadataMap.put("LAST_HEAD_TIMESTAMP", lastHeadTimestamp);
-        } else {
-            metadataMap.put("LAST_HEAD_TIMESTAMP", 0L);
-        }
+        } 
 
         // First and last physics event numbers.
         if (firstPhysicsEvent != null) {
             metadataMap.put("FIRST_PHYSICS_EVENT", firstPhysicsEvent);
-        } else {
-            metadataMap.put("FIRST_PHYSICS_EVENT", 0L);
-        }
+        } 
         
         if (lastPhysicsEvent != null) {
             metadataMap.put("LAST_PHYSICS_EVENT", lastPhysicsEvent);
-        } else {
-            metadataMap.put("LAST_PHYSICS_EVENT", 0L);
+        }
+        
+        // Timestamps which are only set if the corresponding control events were found in the file.
+        if (prestartTimestamp != null) {
+            metadataMap.put("PRESTART_TIMESTAMP", prestartTimestamp);
+        }
+        if (endTimestamp != null) {
+            metadataMap.put("END_TIMESTAMP", endTimestamp);
+        }
+        if (goTimestamp != null) {
+            metadataMap.put("GO_TIMESTAMP", goTimestamp);
         }
 
         // 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.
-        //metadataMap.put("TI_TIME_OFFSET", tiProcessor.getTiTimeOffset());
-
+        metadataMap.put("TI_TIME_MIN_OFFSET", new Long(tiProcessor.getMinOffset()).toString());
+        metadataMap.put("TI_TIME_MAX_OFFSET", new Long(tiProcessor.getMaxOffset()).toString());
+        metadataMap.put("TI_TIME_N_OUTLIERS", tiProcessor.getNumOutliers());
+        
         // Event counts.
         metadataMap.put("BAD_EVENTS", badEvents);
         
         // Physics event count.
-        metadataMap.put("PHYSICS_EVENTS",  physicsEvents);
-        
-        // Trigger rate in Hz to 2 decimal places.
-        /*
+        metadataMap.put("PHYSICS_EVENTS", physicsEvents);
+        
+        // Rough trigger rate.
         if (triggerRate != null && !Double.isInfinite(triggerRate) && !Double.isNaN(triggerRate)) {
             DecimalFormat df = new DecimalFormat("#.##");
             df.setRoundingMode(RoundingMode.CEILING);
-            LOGGER.info("setting trigger rate " + triggerRate);
+            LOGGER.info("Setting trigger rate to " + triggerRate + " Hz.");
             metadataMap.put("TRIGGER_RATE", Double.parseDouble(df.format(triggerRate)));
         } else {
-            metadataMap.put("TRIGGER_RATE", 0);
-        }
-        */
+            LOGGER.warning("Failed to calculate trigger rate.");
+        }        
 
         // Trigger type counts.
         for (Entry<TriggerType, Integer> entry : triggerCounts.entrySet()) {
@@ -327,7 +311,7 @@
         for (Entry<String, Object> entry : metadataMap.entrySet()) {
             sb.append("  " + entry.getKey() + " = " + entry.getValue() + '\n');
         }
-        LOGGER.info("file metadata ..." + '\n' + sb.toString());
+        LOGGER.info("File metadata ..." + '\n' + sb.toString());
 
         // Return the completed metadata map.
         return metadataMap;

Added: java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/MetadataWriter.java
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/MetadataWriter.java	(added)
+++ java/branches/jeremy-dev/crawler/src/main/java/org/hps/crawler/MetadataWriter.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,137 @@
+package org.hps.crawler;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+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;
+
+/**
+ * Creates metadata for a file and writes the results to a Python snippet that can be used as input to the SRS datacat.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public final class MetadataWriter {
+    
+    private static final Logger LOGGER = Logger.getLogger(MetadataWriter.class.getPackage().getName());        
+    private static final Options OPTIONS = new Options();
+    
+    private List<File> inputFiles;
+    private File outputDir = new File(".");    
+
+    static {
+        OPTIONS.addOption("h", "help", false, "print help and exit (overrides all other arguments)");
+        OPTIONS.addOption("d", "dir", true, "directory where metadata files should be written");
+    }
+            
+    public static void main(final String[] args) {
+        new MetadataWriter().parse(args).run();
+    }
+    
+    private MetadataWriter parse(final String[] args) {
+        
+        try { 
+            final CommandLine cl = new DefaultParser().parse(OPTIONS, args);
+
+            // Print help.
+            if (cl.hasOption("h") || args.length == 0) {
+                this.printUsage();
+            }
+                        
+            // List of input files.
+            if (!cl.getArgList().isEmpty()) {
+                inputFiles = new ArrayList<File>();
+                for (String arg : cl.getArgList()) {                    
+                    inputFiles.add(new File(arg));
+                }
+            } else {
+                printUsage();
+            }            
+            if (this.inputFiles.isEmpty()) {
+                throw new RuntimeException("Missing at least one input file to process.");
+            }
+            
+            // Output directory for metadata files.
+            if (cl.hasOption("d")) {
+                outputDir = new File(cl.getOptionValue("d"));
+                if (!outputDir.isDirectory()) {
+                    throw new IllegalArgumentException("The file " + outputDir.getPath() + " is not a directory.");
+                }
+            }
+         
+        } catch (final ParseException e) {
+            throw new RuntimeException("Error parsing command line options.", e);
+        }
+
+        LOGGER.info("Done parsing command line options.");
+
+        return this;
+    }
+
+    private void printUsage() {
+        final HelpFormatter help = new HelpFormatter();
+        help.printHelp(80, "MetadataWriter [options] file1 file2 [...]", "", OPTIONS, "");
+        System.exit(0);
+    }
+
+    private void run() {
+        for (File file : inputFiles) {
+            LOGGER.info("Creating metadata for " + file.getPath() + " ...");
+            Map<String, Object> metadata = DatacatHelper.createMetadata(file);
+            String metadataFileName = this.outputDir + File.separator + file.getName() + ".metadata";
+            writeString(toPyDict(metadata), new File(metadataFileName));            
+            LOGGER.info("Wrote metadata for " + file.getPath() + " to " + metadataFileName);
+        }
+    }
+               
+    private static String toPyDict(Map<String, Object> metadata) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("{");
+        for (String name : DatacatHelper.SYSTEM_METADATA) {
+            if (metadata.containsKey(name)) {
+                Object value = metadata.get(name);
+                if (value instanceof Number) {
+                    sb.append("\"" + name + "\" : " + metadata.get(name) + ", ");
+                } else {
+                    sb.append("\"" + name + "\" : \"" + metadata.get(name) + "\", ");
+                }
+            }            
+        }
+        sb.setLength(sb.length() - 2);
+        sb.append(", \"versionMetadata\" : {");
+        for (Map.Entry<String, Object> entry : metadata.entrySet()) {
+            if (!DatacatHelper.isSystemMetadata(entry.getKey())) {
+               Object value = entry.getValue();
+               String name = entry.getKey();
+               if (value instanceof Number) {
+                   sb.append("\"" + name + "\" : " + metadata.get(name) + ", ");
+               } else {
+                   sb.append("\"" + name + "\" : \"" + metadata.get(name) + "\", ");
+               }
+            }
+        }
+        sb.setLength(sb.length() - 2);
+        sb.append("}");
+        sb.append("}");
+        return sb.toString();
+    }
+    
+    private static void writeString(String dictString, File file) {
+        try {
+            FileWriter fileWriter = new FileWriter(file);
+            fileWriter.write(dictString);
+            fileWriter.flush();
+            fileWriter.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

Added: java/branches/jeremy-dev/crawler/src/main/python/crawler/create_dataset_from_metadata.py
 =============================================================================
--- java/branches/jeremy-dev/crawler/src/main/python/crawler/create_dataset_from_metadata.py	(added)
+++ java/branches/jeremy-dev/crawler/src/main/python/crawler/create_dataset_from_metadata.py	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+
+"""
+Insert files into datacat using previously written .metadata files or allow updating
+the metadata of an existing dataset if the "-u" option is specified.
+
+The dataset's resource directory and target folder in the datacat must be provided explicitly
+with command line arguments. 
+
+author: Jeremy McCormick, SLAC
+"""
+
+import os, sys, glob, argparse
+
+from datacat import *
+from datacat.error import DcException
+from datacat.model import Dataset
+
+# assumes datacat config is in current working dir
+client = client_from_config_file(path=os.getcwd() + '/default.cfg')
+
+def get_data_format(name):
+    if name.endswith('.aida'):
+        return 'AIDA'
+    elif name.endswith('.slcio'):
+        return 'LCIO'
+    elif name.endswith('.root'):
+        return 'ROOT' 
+    elif '.evio' in os.path.basename(name):
+        return 'EVIO'
+    raise Exception('Failed to get data format for %s' % name)
+
+def get_data_type(name, format):
+    if format == 'EVIO':
+        return 'RAW'
+    elif format == 'LCIO' and '_recon' in name:
+        return 'RECON'
+    elif format == 'ROOT' and '_dst' in name:
+        return 'DST'
+    elif format == 'ROOT' and '_dqm' in name:
+        return 'DQM'
+    elif format == 'AIDA' and '_dqm' in name:
+        return 'DQM'    
+    raise Exception('Failed to get data type for %s' % name)
+
+# define CL options
+parser = argparse.ArgumentParser(description='insert or update datasets from metadata files')
+parser.add_argument('--basedir', '-b', dest='basedir', nargs=1, help='base dir containing metadata files to read', required = True)
+parser.add_argument('--folder', '-f', dest='folder', nargs=1, help='target folder in the datacat',  required = True)
+parser.add_argument('--resource', '-r', dest='resource', nargs=1, help='actual directory of the files', required = True)
+parser.add_argument('--site', '-s', dest='site', nargs=1, help='datacat site (default JLAB)', default='JLAB') # TODO: default to dir of .metadata file
+parser.add_argument('--update', '-u', dest='update', help='allow updates to metadata of existing files', action='store_true')
+args = parser.parse_args()
+
+basedir = args.basedir[0]
+folder = args.folder[0]
+if args.resource[0] is not None:
+    resource = args.resource[0]
+else:
+    resource = basedir
+site = args.site[0]
+allow_update = args.update
+
+metadata_files = glob.glob(basedir + '/*.metadata')
+
+if len(metadata_files) == 0:
+    raise Exception("No metadata files found in %s dir." % basedir) 
+        
+for metadata_file in metadata_files:
+    
+    metadata = eval(open(metadata_file).read())
+    if not isinstance(metadata, dict):
+        raise Exception("Input metadata from %s is not a dict." % metadata_file)
+    
+    locationExtras = {}
+    for k, v in metadata.iteritems():
+        if k != 'versionMetadata':
+           locationExtras[k] = v
+        else:
+            versionMetadata = v
+
+    if versionMetadata is None:
+        versionmetadata = {}
+
+    # TODO: check for empty metadata here (should have some)
+
+    name = os.path.basename(metadata_file).replace('.metadata', '')
+    data_format = get_data_format(name)
+    data_type = get_data_type(name, data_format)
+    
+    print "adding dataset ..."
+    print "folder = %s" % folder
+    print "name = %s" % name
+    print "data_format = %s" % data_format
+    print "data_type = %s" % data_type
+    print "site = %s" % site
+    print "resource = %s" % (resource + '/' + name)
+    print "versionMetadata = " + repr(versionMetadata)
+    print "locationExtras = " + repr(locationExtras)
+    print
+    
+    dataset_exists = False    
+    try:
+        p = client.path("%s/%s" % (folder, name))
+        if isinstance(p, Dataset):
+           dataset_exists = True 
+    except DcException:
+        pass
+    
+    if not dataset_exists:
+        print "Creating new dataset for %s ..." % name
+        try:
+            client.mkds(folder,
+                        name,
+                        data_type,
+                        data_format, 
+                        site=site, 
+                        resource=resource + '/' + name,
+                        versionMetadata=versionMetadata,
+                        locationExtras=locationExtras)
+            print "%s was added successfully." % name
+        except DcException as e:
+            print 'Insert of %s failed!' % name
+            print repr(e)
+    else:
+        if allow_update:
+            print "Updating metadata on existing dataset %s ..." % name
+            try:
+                if metadata['checksum'] is not None:
+                    del metadata['checksum']
+                client.patchds(folder + '/' + name, metadata)
+            except DcException as e:
+                print "Update of %s failed!" % name
+                print repr(e)                
+        else:
+            raise Exception("Dataset already exists and updates are not allowed.")                

Modified: java/branches/jeremy-dev/distribution/pom.xml
 =============================================================================
--- java/branches/jeremy-dev/distribution/pom.xml	(original)
+++ java/branches/jeremy-dev/distribution/pom.xml	Wed Feb 10 14:26:49 2016
@@ -88,35 +88,31 @@
                                 </program>
                                 <program>
                                     <mainClass>org.hps.job.JobManager</mainClass>
-                                    <id>job</id>
+                                    <id>job-manager</id>
                                 </program>
                                 <program>
                                     <mainClass>org.hps.conditions.cli.CommandLineTool</mainClass>
-                                    <id>conddb</id>
-                                </program>
-                                <program>
-                                    <mainClass>org.hps.crawler.DatacatCrawler</mainClass>
-                                    <id>crawler</id>
+                                    <id>conditions-cli</id>
                                 </program>
                                 <program>
                                     <mainClass>org.hps.run.database.RunDatabaseCommandLine</mainClass>
-                                    <id>rundb</id>
+                                    <id>run-database-cli</id>
                                 </program>
                                 <program>
                                     <mainClass>org.hps.monitoring.application.Main</mainClass>
-                                    <id>monapp</id>
+                                    <id>monitoring-app</id>
                                 </program>
                                 <program>
                                     <mainClass>org.lcsim.geometry.compact.converter.Main</mainClass>
-                                    <id>detcnv</id>
+                                    <id>detector-converter</id>
                                 </program>
                                 <program>
                                     <mainClass>org.hps.record.evio.EvioFileProducer</mainClass>
-                                    <id>evio_file_producer</id>
+                                    <id>evio-file-producer</id>
                                 </program>
                                 <program>
                                     <mainClass>org.jlab.coda.et.apps.StartEt</mainClass>
-                                    <id>et_server</id>
+                                    <id>et-server</id>
                                     <commandLineArguments>
                                         <commandLineArgument>-f</commandLineArgument>
                                         <commandLineArgument>ETBuffer</commandLineArgument>
@@ -124,6 +120,18 @@
                                         <commandLineArgument>20000</commandLineArgument>
                                         <commandLineArgument>-v</commandLineArgument>
                                     </commandLineArguments>
+                                </program>
+                                <program>
+                                    <mainClass>org.hps.crawler.MetadataWriter</mainClass>
+                                    <id>dc-create-metadata</id>
+                                </program>
+                                <program>
+                                    <mainClass>org.hps.crawler.DatacatAddFile</mainClass>
+                                    <id>dc-add-file</id>
+                                </program>
+                                <program>
+                                    <mainClass>org.hps.crawler.DatacatCrawler</mainClass>
+                                    <id>dc-crawler</id>
                                 </program>
                             </programs>
                         </configuration>

Modified: java/branches/jeremy-dev/logging/src/main/resources/org/hps/logging/config/test_logging.properties
 =============================================================================
--- java/branches/jeremy-dev/logging/src/main/resources/org/hps/logging/config/test_logging.properties	(original)
+++ java/branches/jeremy-dev/logging/src/main/resources/org/hps/logging/config/test_logging.properties	Wed Feb 10 14:26:49 2016
@@ -66,7 +66,7 @@
 org.hps.recon.tracking.gbl.level = WARNING
 
 # run-database
-org.hps.run.database.level = WARNING
+org.hps.run.database.level = ALL
 
 # monitoring-application
 org.hps.monitoring.application.model.level = WARNING

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/TriggerConfigEvioProcessor.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/TriggerConfigEvioProcessor.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/daqconfig/TriggerConfigEvioProcessor.java	Wed Feb 10 14:26:49 2016
@@ -14,10 +14,11 @@
 import org.jlab.coda.jevio.EvioEvent;
 
 /**
- * Copied and modified from code in {@link org.hps.evio.TriggerConfigEvioReader} to extract DAQ config without
- * needing an output LCSim event.
+ * Extracts DAQ config strings from an EVIO event stream, saving a reference to the most recent
+ * {@link org.hps.record.triggerbank.TriggerConfigData} object.
  * <p>
- * Only the last valid DAQ config object is available once the job is finished.
+ * When event processing is completed, the <code>triggerConfig</code> variable should reference
+ * the last valid DAQ config and can be accessed using the {@link #getTriggerConfigData()} method.
  * 
  * @author Jeremy McCormick, SLAC
  */
@@ -25,8 +26,7 @@
 
     private Logger LOGGER = Logger.getLogger(TriggerConfigEvioProcessor.class.getPackage().getName());
             
-    private TriggerConfigData triggerConfig = null;    
-    private Integer run = null;
+    private TriggerConfigData triggerConfig = null;
     private int timestamp = 0;
 
     /**
@@ -35,32 +35,21 @@
     @Override
     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) {
+           
+            // Set current timestamp from head bank.
+            BaseStructure headBank = EvioEventUtilities.getHeadBank(evioEvent);
+            if (headBank != null) {
+                if (headBank.getIntData()[3] != 0) {
+                    timestamp = headBank.getIntData()[3];
+                    LOGGER.finest("Set timestamp " + timestamp + " from head bank.");
                 }
             }
-                        
-            // Can only start parsing DAQ banks once the run is set.
-            if (run != null) {
                 
-                // Set current timestamp from head bank.
-                BaseStructure headBank = EvioEventUtilities.getHeadBank(evioEvent);
-                if (headBank != null) {
-                    if (headBank.getIntData()[3] != 0) {
-                        timestamp = headBank.getIntData()[3];
-                        LOGGER.finest("set timestamp " + timestamp + " from head bank");
-                    }
-                }
-                
-                // Parse config data from the EVIO banks.
-                parseEvioData(evioEvent);                                                          
-            }
+            // Parse config data from the EVIO banks.
+            parseEvioData(evioEvent);                                                          
+            
         } catch (Exception e) {
-            LOGGER.log(Level.WARNING, "Error parsing DAQ config from EVIO.", e);
+            LOGGER.log(Level.SEVERE, "Error parsing DAQ config from EVIO.", e);
         }
     }
     
@@ -72,46 +61,68 @@
      */
     private void parseEvioData(EvioEvent evioEvent) {
         Map<Crate, String> stringData = null;
+        // Loop over top banks.
         for (BaseStructure bank : evioEvent.getChildrenList()) {
             if (bank.getChildCount() <= 0) {
                 continue;
             }
-            int crate = bank.getHeader().getTag();
+            int crateNumber = bank.getHeader().getTag();
+            // Loop over sub-banks.
             for (BaseStructure subBank : bank.getChildrenList()) {
+                // In trigger config bank?
                 if (EvioBankTag.TRIGGER_CONFIG.equals(subBank)) {
-                    if (subBank.getStringData() == null) {
-                        LOGGER.warning("Trigger config bank is missing string data.");
-                    } else {
+                    // Has a valid string array?
+                    if (subBank.getStringData() != null) {                    
                         try { 
+                            
+                            // Make sure string data map is initialized for this event.
                             if (stringData == null) {
                                 stringData = new HashMap<Crate, String>();
-                            }
-                            //LOGGER.fine("got raw trigger config string data ..." + '\n' + subBank.getStringData()[0]);
-                            stringData.put(TriggerConfigData.Crate.fromCrateNumber(crate), subBank.getStringData()[0]);
+                            }                                                       
+                            
+                            // Get the Crate enum from crate number (if this returns null then the crate is ignored).
+                            Crate crate = Crate.fromCrateNumber(crateNumber);
+                            
+                            // Is crate number valid?
+                            if (crate != null) {
+                                
+                                // Is there valid string data in the array?
+                                if (subBank.getStringData().length > 0) {
+                                    // Add string data to map.
+                                    stringData.put(crate, subBank.getStringData()[0]);
+                                    LOGGER.info("Added crate " + crate.getCrateNumber() + " data ..." + '\n' + subBank.getStringData()[0]);
+                                } /*else { 
+                                    LOGGER.warning("The string bank has no data.");
+                                }*/
+                            } 
                         } catch (Exception e) {
-                            LOGGER.log(Level.WARNING, "Failed to parse crate " + crate + " config.", e);
+                            LOGGER.log(Level.SEVERE, "Error parsing DAQ config from crate " + crateNumber, e);
+                            e.printStackTrace();
                         }
                     }
-                }
+                } /*else {
+                    LOGGER.warning("Trigger config bank is missing string data.");
+                }*/
             }
         }
         if (stringData != null) {
+            LOGGER.info("Found " + stringData.size() + " config data strings in event " + evioEvent.getEventNumber());
             TriggerConfigData currentConfig = new TriggerConfigData(stringData, timestamp);
             if (currentConfig.isValid()) {
                 triggerConfig = currentConfig;
-                LOGGER.warning("Found valid config in event num " + evioEvent.getEventNumber());
+                LOGGER.info("Found valid DAQ config data in event num " + evioEvent.getEventNumber());
             } else {
-                LOGGER.warning("Skipping invalid config from event num "  + evioEvent.getEventNumber());
+                LOGGER.warning("Skipping invalid DAQ config data in event num "  + evioEvent.getEventNumber());
             }
         }
     }
    
     /**
-     * Get a map of bank number to string data for the current config.
+     * Get the last valid set of config data that was found in the event stream.
      * 
-     * @return a map of bank to trigger config data
+     * @return a map of bank number to the corresponding trigger config string data
      */
     public TriggerConfigData getTriggerConfigData() {
         return this.triggerConfig;
     }
-}
+}

Added: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetCalculator.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetCalculator.java	(added)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TiTimeOffsetCalculator.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,56 @@
+package org.hps.record.triggerbank;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Calculate TI time offset given lists of min and max offsets and the number of outliers.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public class TiTimeOffsetCalculator {
+    
+    /* Constants from TiTimeOffsetEvioProcessor. */
+    private final static int MAX_OUTLIERS = 10;
+    private final static double MIN_RANGE = 0.99e9;
+    
+    private List<Long> minOffsets = new ArrayList<Long>();
+    private List<Long> maxOffsets = new ArrayList<Long>();
+    private int totalOutliers;
+    
+    public void addMinOffset(long minOffset) {
+        minOffsets.add(minOffset);
+    }
+    
+    public void addMaxOffset(long maxOffset) {
+        maxOffsets.add(maxOffset);
+    }
+    
+    public void addNumOutliers(int nOutliers) {
+        totalOutliers += nOutliers;
+    }
+    
+    public long calculateTimeOffset() {
+        
+        if (minOffsets.size() == 0) {
+            throw new RuntimeException("The min offsets list has no data.");
+        }
+        if (maxOffsets.size() == 0) {
+            throw new RuntimeException("The max offsets list has no data.");
+        }
+        
+        Collections.sort(minOffsets);
+        Collections.sort(maxOffsets);
+        
+        long minOffset = minOffsets.get(0);
+        long maxOffset = maxOffsets.get(maxOffsets.size() - 1);
+                
+        final long offsetRange = maxOffset - minOffset;
+        if (offsetRange > MIN_RANGE && totalOutliers < MAX_OUTLIERS) {
+            return minOffset;
+        } else {
+            return 0L;
+        }
+    }
+}

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	Wed Feb 10 14:26:49 2016
@@ -56,6 +56,18 @@
         }
     }
     
+    public long getMinOffset() {
+        return this.minOffset;
+    }
+    
+    public long getMaxOffset() {
+        return this.maxOffset;
+    }
+    
+    public int getNumOutliers() {
+        return this.nOutliers;
+    }
+    
     public long getTiTimeOffset() {
         final long offsetRange = maxOffset - minOffset;
         if (offsetRange > minRange && nOutliers < maxOutliers) {

Modified: java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TriggerConfigData.java
 =============================================================================
--- java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TriggerConfigData.java	(original)
+++ java/branches/jeremy-dev/record-util/src/main/java/org/hps/record/triggerbank/TriggerConfigData.java	Wed Feb 10 14:26:49 2016
@@ -26,20 +26,20 @@
             this.crate = crate;
         }
         
-        public int crate() {
+        public int getCrateNumber() {
             return crate;
         }
         
         public static Crate fromCrateNumber(int crateNumber) {
             for (Crate crate : Crate.values()) {
-                if (crate.crate() == crateNumber) {
+                if (crate.getCrateNumber() == crateNumber) {
                     return crate;
                 }
             }
             return null;
-        }
+        }              
     }
-              
+                  
     private int timestamp;
     private Map<Crate, String> data;
     
@@ -99,7 +99,7 @@
     public DAQConfig loadDAQConfig(int run) {
         EvioDAQParser parser = new EvioDAQParser();
         for (Entry<Crate, String> entry : data.entrySet()) {
-            parser.parse(entry.getKey().crate(), run, new String[] {entry.getValue()});
+            parser.parse(entry.getKey().getCrateNumber(), run, new String[] {entry.getValue()});
         }
         ConfigurationManager.updateConfiguration(parser);
         return ConfigurationManager.getInstance();

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/AbstractRunBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/AbstractRunBuilder.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/AbstractRunBuilder.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,35 @@
+package org.hps.run.database;
+
+/**
+ * Class for incrementally building records for the run database.
+ * <p>
+ * Classes that add information to the run summary or create objects
+ * for insertion into the run database should implement this.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public abstract class AbstractRunBuilder {
+    
+    private RunSummaryImpl runSummary;
+        
+    void setRunSummary(RunSummaryImpl runSummary) {
+        this.runSummary = runSummary;
+    }
+    
+    RunSummaryImpl getRunSummary() {
+        return runSummary;
+    }
+    
+    int getRun() {
+        if (this.runSummary == null) {
+            throw new IllegalStateException("The run summary object was never set.");
+        }
+        return this.runSummary.getRun();
+    }
+    
+    /**
+     * Abstract method that sub-classes should implement to update the run summary or 
+     * create objects for insertion into the database.
+     */
+    abstract void build();
+}

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatabaseUpdater.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatabaseUpdater.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatabaseUpdater.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,61 @@
+package org.hps.run.database;
+
+import java.sql.Connection;
+
+import org.hps.record.triggerbank.TriggerConfigData;
+
+// TODO: add EPICs and scaler update
+
+public class DatabaseUpdater {
+    
+    private Connection connection;
+    private TriggerConfigData triggerConfig;
+    private RunSummary runSummary;
+    private boolean updateExisting = false;
+                      
+    DatabaseUpdater(Connection connection) {
+        this.connection = connection;
+    }
+    
+    void setTriggerConfigData(TriggerConfigData triggerConfig) {
+        this.triggerConfig = triggerConfig;
+    }
+    
+    void setRunSummary(RunSummary runSummary) {
+        this.runSummary = runSummary;
+    }
+    
+    void setUpdateExisting(boolean updateExisting) {
+        this.updateExisting = updateExisting;
+    }
+
+    void update() {
+
+        int run = runSummary.getRun();
+        
+        final DaoProvider runFactory = new DaoProvider(connection);
+        final RunSummaryDao runSummaryDao = runFactory.getRunSummaryDao();
+        
+        RunManager runManager = new RunManager();
+        runManager.setRun(runSummary.getRun());
+        if (runManager.runExists()) {
+            if (updateExisting) {
+                runSummaryDao.updateRunSummary(runSummary);
+            } else {
+                throw new RuntimeException("Run already exists and updates are not allowed.");
+            }
+        } else {
+            runSummaryDao.insertRunSummary(runSummary);
+        }        
+        
+        final TriggerConfigDao configDao = runFactory.getTriggerConfigDao();
+        if (configDao.getTriggerConfig(run) != null) {
+            if (updateExisting) {
+                configDao.deleteTriggerConfig(run);
+            } else {
+                throw new RuntimeException("Run already exists and updates are not allowed.");
+            }
+        }
+        configDao.insertTriggerConfig(this.triggerConfig, run);
+    }
+}

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatBuilder.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatBuilder.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,191 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.hps.record.triggerbank.TiTimeOffsetCalculator;
+import org.srs.datacat.client.Client;
+import org.srs.datacat.client.exception.DcClientException;
+import org.srs.datacat.model.DatasetModel;
+import org.srs.datacat.model.DatasetResultSetModel;
+import org.srs.datacat.model.dataset.DatasetWithViewModel;
+import org.srs.datacat.shared.DatasetLocation;
+
+final class DatacatBuilder extends AbstractRunBuilder {
+    
+    private static final Logger LOGGER = Logger.getLogger(DatacatBuilder.class.getPackage().getName());
+    
+    private static final String[] METADATA_FIELDS = {
+        "TI_TIME_MIN_OFFSET", 
+        "TI_TIME_MAX_OFFSET", 
+        "TI_TIME_N_OUTLIERS", 
+        "END_TIMESTAMP",
+        "GO_TIMESTAMP",
+        "PRESTART_TIMESTAMP"
+    };
+    
+    private Client datacatClient;
+    private String site;
+    private String folder;    
+    private List<File> files;
+                
+    private static long calculateTiTimeOffset(DatasetResultSetModel results) {
+        TiTimeOffsetCalculator calc = new TiTimeOffsetCalculator();
+        for (DatasetModel ds : results) {
+            DatasetWithViewModel view = (DatasetWithViewModel) ds;
+            Map<String, Object> metadata = view.getMetadataMap();                        
+            if (metadata.containsKey("TI_TIME_MIN_OFFSET")) {
+                calc.addMinOffset(Long.parseLong((String) metadata.get("TI_TIME_MIN_OFFSET")));
+            }
+            if (metadata.containsKey("TI_TIME_MAX_OFFSET")) {
+                calc.addMaxOffset(Long.parseLong((String) metadata.get("TI_TIME_MAX_OFFSET")));
+            }
+            if (metadata.containsKey("TI_TIME_N_OUTLIERS")) {
+                calc.addNumOutliers((int) (long) metadata.get("TI_TIME_N_OUTLIERS"));
+            }
+        }
+        return calc.calculateTimeOffset();
+    }
+    
+    private static long getTotalEvents(DatasetResultSetModel results) {
+        long totalEvents = 0;
+        for (DatasetModel ds : results) {
+            DatasetWithViewModel view = (DatasetWithViewModel) ds;
+            DatasetLocation loc = (DatasetLocation) view.getViewInfo().getLocations().iterator().next();
+            totalEvents += loc.getEventCount();
+        }
+        return totalEvents;
+    }
+    
+    private static Integer getPrestartTimestamp(DatasetResultSetModel results) {
+        DatasetWithViewModel ds = (DatasetWithViewModel) results.getResults().get(0);
+        if (ds.getMetadataMap().containsKey("PRESTART_TIMESTAMP")) {
+            return (int) (long) ds.getMetadataMap().get("PRESTART_TIMESTAMP");
+        } else {
+            return null;
+        }
+    }
+    
+    private static Integer getEndTimestamp(DatasetResultSetModel results) {        
+        DatasetWithViewModel ds = (DatasetWithViewModel) results.getResults().get(results.getResults().size() - 1);
+        if (ds.getMetadataMap().containsKey("END_TIMESTAMP")) {
+            return (int) (long) ds.getMetadataMap().get("END_TIMESTAMP");
+        } else {
+            return null;
+        }
+    }
+    
+    
+    private static Integer getGoTimestamp(DatasetResultSetModel results) {
+        DatasetWithViewModel ds = (DatasetWithViewModel) results.getResults().get(0);
+        if (ds.getMetadataMap().containsKey("GO_TIMESTAMP")) {
+            return (int) (long) ds.getMetadataMap().get("GO_TIMESTAMP");
+        } else {
+            return null;
+        }
+    }
+    
+    private static double calculateTriggerRate(Integer startTimestamp, Integer endTimestamp, long nEvents) {
+        if (startTimestamp == null) {
+            throw new IllegalArgumentException("The start timestamp is null.");
+        }
+        if (endTimestamp == null) {
+            throw new IllegalArgumentException("The end timestamp is null.");
+        }
+        if (endTimestamp - startTimestamp == 0) {
+            throw new IllegalArgumentException("The start and end timestamp are the same.");
+        }
+        if (nEvents == 0) {
+            throw new IllegalArgumentException("The number of events is zero.");
+        }
+        double triggerRate = (double) nEvents / ((double) endTimestamp - (double) startTimestamp);
+        return triggerRate;
+    }
+        
+    void build() {
+        
+        if (getRunSummary() == null) {
+            throw new RuntimeException("The run summary was not set.");
+        }        
+        if (this.datacatClient == null) {
+            throw new RuntimeException("The datacat client was not set.");
+        }        
+        if (this.folder == null) {
+            throw new RuntimeException("The target folder was not set.");
+        }
+        if (this.site == null) {
+            throw new RuntimeException("The site was not set.");
+        }
+        
+        DatasetResultSetModel results = null;
+        try {
+            results = findDatasets();
+        } catch (DcClientException e) {
+            System.err.println("HTTP status: " + e.getStatusCode());
+            throw new RuntimeException(e);
+        }
+        
+        files = DatacatUtilities.toFileList(results);
+        
+        if (results.getResults().isEmpty()) {
+            throw new RuntimeException("No results found for datacat search.");
+        }
+        
+        long tiTimeOffset = calculateTiTimeOffset(results);
+        getRunSummary().setTiTimeOffset(tiTimeOffset);
+        
+        long totalEvents = getTotalEvents(results);
+        getRunSummary().setTotalEvents(totalEvents);
+        
+        int nFiles = results.getResults().size();
+        getRunSummary().setTotalFiles(nFiles);
+        
+        int prestartTimestamp = getPrestartTimestamp(results);
+        getRunSummary().setPrestartTimestamp(prestartTimestamp);
+        
+        int goTimestamp = getGoTimestamp(results);
+        getRunSummary().setGoTimestamp(goTimestamp);
+        
+        int endTimestamp = getEndTimestamp(results);
+        getRunSummary().setEndTimestamp(endTimestamp);
+        
+        double triggerRate = calculateTriggerRate(prestartTimestamp, endTimestamp, totalEvents);
+        getRunSummary().setTriggerRate(triggerRate);        
+    }
+                         
+    private DatasetResultSetModel findDatasets() {
+        
+        LOGGER.info("finding EVIO datasets for run " + getRun() + " in " + this.folder + " at " + this.site + " ...");
+        
+        DatasetResultSetModel results = datacatClient.searchForDatasets(
+                this.folder,
+                "current",
+                this.site,
+                "fileFormat eq 'EVIO' AND dataType eq 'RAW' AND runMin eq " + getRun(),
+                new String[] {"FILE"},
+                METADATA_FIELDS
+                );
+        
+        LOGGER.info("found " + results.getResults().size() + " EVIO datasets for run " + getRun());
+                               
+        return results;
+    }    
+    
+    void setSite(String site) {
+        this.site = site;
+    }
+    
+    void setDatacatClient(Client datacatClient) {
+        this.datacatClient = datacatClient;
+    }
+    
+    void setFolder(String folder) {
+        this.folder = folder;
+    }
+    
+    List<File> getFileList() {
+        return files;
+    }
+}

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatUtilities.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatUtilities.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/DatacatUtilities.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,29 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.srs.datacat.model.DatasetModel;
+import org.srs.datacat.model.DatasetResultSetModel;
+import org.srs.datacat.model.dataset.DatasetWithViewModel;
+
+final class DatacatUtilities {
+    
+    private DatacatUtilities() {
+        throw new RuntimeException("Do not instantiate this class.");
+    }
+    
+    static final List<File> toFileList(DatasetResultSetModel datasets) {
+        List<File> files = new ArrayList<File>();
+        for (DatasetModel dataset : datasets.getResults()) {
+            String resource = 
+                    ((DatasetWithViewModel) dataset).getViewInfo().getLocations().iterator().next().getResource();
+            if (resource.startsWith("/ss")) {
+                resource = "/cache" + resource;
+            }
+            files.add(new File(resource));
+        }
+        return files;
+    }
+}

Modified: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDao.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDao.java	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/EpicsDataDao.java	Wed Feb 10 14:26:49 2016
@@ -16,7 +16,7 @@
      *
      * @param run the run number
      */
-    public void deleteEpicsData(EpicsType epicsType, final int run);
+    public void deleteEpicsData(EpicsType epicsType, int run);
 
     /**
      * Get EPICS data by run.
@@ -34,5 +34,5 @@
      *
      * @param epicsDataList the list of EPICS data
      */
-    void insertEpicsData(List<EpicsData> epicsDataList);   
+    void insertEpicsData(List<EpicsData> epicsDataList, int run);
 }

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	Wed Feb 10 14:26:49 2016
@@ -192,12 +192,14 @@
     /**
      * Insert a list of EPICS data into the database.
      * <p>
-     * The run number comes from the header information.
+     * By default, the run number from the header will be used, but it will be overridden
+     * if it does not match the <code>run</code> argument.  (There are a few data files
+     * where the run in the EPICS header is occassionally wrong.)
      *
      * @param epicsDataList the list of EPICS data
      */
     @Override
-    public void insertEpicsData(final List<EpicsData> epicsDataList) {
+    public void insertEpicsData(final List<EpicsData> epicsDataList, int run) {
         if (epicsDataList.isEmpty()) {
             throw new IllegalArgumentException("The EPICS data list is empty.");
         }
@@ -211,9 +213,11 @@
                 if (epicsHeader == null) {
                     throw new IllegalArgumentException("The EPICS data is missing a header.");
                 }
-                insertHeaderStatement.setInt(1, epicsHeader.getRun());
+                insertHeaderStatement.setInt(1, run); /* Don't use run from bank as it is sometimes wrong! */
                 insertHeaderStatement.setInt(2, epicsHeader.getSequence());
                 insertHeaderStatement.setInt(3, epicsHeader.getTimestamp());
+                LOGGER.finer("creating EPICs record with run = " + run + " ; seq = " 
+                        + epicsHeader.getSequence() + "; ts = " + epicsHeader.getTimestamp());
                 final int rowsCreated = insertHeaderStatement.executeUpdate();
                 if (rowsCreated == 0) {
                     throw new SQLException("Creation of EPICS header record failed; no rows affected.");

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/LivetimeBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/LivetimeBuilder.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/LivetimeBuilder.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,58 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.hps.record.evio.EvioFileUtilities;
+import org.hps.record.scalers.ScalerData;
+import org.hps.record.scalers.ScalerUtilities;
+import org.hps.record.scalers.ScalersEvioProcessor;
+import org.hps.record.scalers.ScalerUtilities.LiveTimeIndex;
+import org.jlab.coda.jevio.EvioEvent;
+import org.jlab.coda.jevio.EvioException;
+import org.jlab.coda.jevio.EvioReader;
+
+public class LivetimeBuilder extends AbstractRunBuilder {
+    
+    private List<File> files;
+    private ScalerData scalerData;
+    
+    void setFiles(List<File> files) {
+        this.files = files;
+    }    
+    
+    void build() {
+        if (files == null) {
+            throw new RuntimeException("The list of files was never set.");
+        }        
+        int fileIndex = files.size() - 1;
+        ScalersEvioProcessor processor = new ScalersEvioProcessor();
+        processor.setResetEveryEvent(false);        
+        while (scalerData == null && fileIndex >= 0) {
+            File file = files.get(fileIndex);
+            try {
+                EvioReader reader = EvioFileUtilities.open(file, true);
+                EvioEvent evioEvent = reader.parseNextEvent();
+                while (evioEvent != null) {
+                    processor.process(evioEvent);
+                    evioEvent = reader.parseNextEvent();
+                }
+                if (processor.getCurrentScalerData() != null) {
+                    scalerData = processor.getCurrentScalerData();
+                    break;
+                }
+                fileIndex -= 1;
+            } catch (EvioException | IOException e) {
+                throw new RuntimeException(e);
+            }            
+        }
+                
+        if (scalerData != null) {
+            double[] livetimes = ScalerUtilities.getLiveTimes(scalerData);
+            getRunSummary().setLivetimeClock(livetimes[LiveTimeIndex.CLOCK.ordinal()]);
+            getRunSummary().setLivetimeFcupTdc(livetimes[LiveTimeIndex.FCUP_TDC.ordinal()]);
+            getRunSummary().setLivetimeFcupTrg(livetimes[LiveTimeIndex.FCUP_TRG.ordinal()]);
+        } 
+    }
+}

Modified: 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	(original)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/RunDatabaseBuilder.java	Wed Feb 10 14:26:49 2016
@@ -6,6 +6,7 @@
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -29,6 +30,7 @@
 import org.hps.record.scalers.ScalersEvioProcessor;
 import org.hps.record.triggerbank.AbstractIntData.IntBankDefinition;
 import org.hps.record.triggerbank.HeadBankData;
+import org.hps.record.triggerbank.TiTimeOffsetCalculator;
 import org.hps.record.triggerbank.TiTimeOffsetEvioProcessor;
 import org.hps.record.triggerbank.TriggerConfigData;
 import org.hps.record.triggerbank.TriggerConfigData.Crate;
@@ -40,6 +42,7 @@
 import org.srs.datacat.model.DatasetModel;
 import org.srs.datacat.model.DatasetResultSetModel;
 import org.srs.datacat.model.dataset.DatasetWithViewModel;
+import org.srs.datacat.shared.DatasetLocation;
 
 /**
  * Builds a complete {@link RunSummary} object from various data sources, including the data catalog and the run
@@ -143,7 +146,7 @@
      * Default folder for file search.
      */
     private String folder;
-        
+    
     /**
      * Reload state for the current run number for testing.
      */
@@ -208,15 +211,13 @@
     private void findEvioDatasets() {
         
         LOGGER.info("finding EVIO datasets for run " + getRun() + " in folder " + this.folder + " at site " + this.site);
-                
+        
         DatasetResultSetModel results = datacatClient.searchForDatasets(
                 this.folder,
                 "current",
                 this.site,
                 "fileFormat eq 'EVIO' AND dataType eq 'RAW' AND runMin eq " + getRun(),
                 null,
-                null,
-                null,
                 null
                 );
         
@@ -262,7 +263,7 @@
         // Insert the EPICS data.
         if (epicsData != null && !epicsData.isEmpty()) {
             LOGGER.info("inserting EPICS data");
-            runFactory.getEpicsDataDao().insertEpicsData(epicsData);
+            runFactory.getEpicsDataDao().insertEpicsData(epicsData, getRun());
         } else {
             LOGGER.warning("no EPICS data to insert");
         }
@@ -663,12 +664,21 @@
         EvioReader reader = null;
         Integer endTimestamp = null;
         try {
-            reader = EvioFileUtilities.open(lastEvioFile, true);
-            EvioEvent evioEvent = reader.parseNextEvent();
-            while (evioEvent != null) {
+            reader = EvioFileUtilities.open(lastEvioFile, true);            
+            while (true) {
+                if (reader.getNumEventsRemaining() == 0) {
+                    break;
+                }
+                EvioEvent evioEvent = null;
+                try {                                   
+                    evioEvent = reader.parseNextEvent();
+                } catch (Exception e) {
+                    LOGGER.severe("Error parsing EVIO event; skipping to next event.");
+                    continue;
+                }
                 if (EventTagConstant.END.matches(evioEvent)) {
                     endTimestamp = EvioEventUtilities.getControlEventData(evioEvent)[0];
-                    LOGGER.fine("found END timestamp " + endTimestamp);
+                    LOGGER.fine("found END timestamp " + endTimestamp + " in event " + evioEvent.getEventNumber());
                     break;
                 }
                 BaseStructure headBank = headBankDefinition.findBank(evioEvent);
@@ -677,10 +687,9 @@
                         endTimestamp = headBank.getIntData()[0];
                     }
                 }
-                evioEvent = reader.parseNextEvent();
-            }
-        } catch (IOException | EvioException e) {
-            throw new RuntimeException("Error reading first EVIO file.", e);
+            }
+        } catch (IOException | EvioException e2) {
+            throw new RuntimeException("Error getting END timestamp.", e2);
         } finally {
             if (reader != null) {
                 try {
@@ -690,7 +699,9 @@
                 }
             }
         }
-        runSummary.setEndTimestamp(endTimestamp);
+        if (endTimestamp != null) {
+            runSummary.setEndTimestamp(endTimestamp);
+        }
         LOGGER.fine("end timestamp was set to " + endTimestamp);
     }
 
@@ -816,16 +827,15 @@
             startTimestamp = runSummary.getGoTimestamp();
         } else if (runSummary.getPrestartTimestamp() != null) {
             startTimestamp = runSummary.getPrestartTimestamp();
-        } else {
-            LOGGER.warning("Could not get starting timestamp for trigger rate calculation.");
-        }
-        if (runSummary.getEndTimestamp() != null && startTimestamp != null) {
+        } 
+        Integer endTimestamp = runSummary.getEndTimestamp();
+        if (endTimestamp!= null && startTimestamp != null && runSummary.getTotalEvents() > 0) {
             double triggerRate = ((double) runSummary.getTotalEvents() /
                     ((double) runSummary.getEndTimestamp() - (double) runSummary.getGoTimestamp()));
             runSummary.setTriggerRate(triggerRate);
             LOGGER.info("trigger rate set to " + runSummary.getTriggerRate());
         } else {
-            LOGGER.warning("Skipped trigger rate calculation because a timestamp is missing.");
+            LOGGER.warning("Skipped trigger rate calculation due to missing data.");
         }
     }
 }

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	Wed Feb 10 14:26:49 2016
@@ -45,4 +45,11 @@
      * @return <code>true</code> if <code>run</code> exists in the database
      */
     boolean runSummaryExists(int run);
+    
+    /**
+     * Update a run summary that already exists.
+     * 
+     * @param runSummary the run summary to update
+     */
+    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	Wed Feb 10 14:26:49 2016
@@ -27,6 +27,12 @@
             + " go_timestamp, end_timestamp, trigger_rate, trigger_config_name, ti_time_offset," 
             + " livetime_clock, livetime_fcup_tdc, livetime_fcup_trg, target, notes, created, updated)"
             + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
+    
+    private static final String UPDATE = "UPDATE run_summaries SET nevents = ?, nfiles = ?, prestart_timestamp = ?,"
+            + " go_timestamp = ?, end_timestamp = ?, trigger_rate = ?, trigger_config_name = ?, ti_time_offset = ?," 
+            + " livetime_clock = ?, livetime_fcup_tdc = ?, livetime_fcup_trg = ?, target = ?, notes = ?, updated = NOW()"
+            + " WHERE run = ?";
+    
                      
     /**
      * Select record by run number.
@@ -196,6 +202,41 @@
             }
         }
     }
+    
+    @Override
+    public void updateRunSummary(RunSummary runSummary) {
+        PreparedStatement preparedStatement = null;
+        try {
+            preparedStatement = connection.prepareStatement(UPDATE);                                   
+            preparedStatement.setLong(1, runSummary.getTotalEvents());
+            preparedStatement.setInt(2, runSummary.getTotalFiles());
+            preparedStatement.setObject(3, runSummary.getPrestartTimestamp());
+            preparedStatement.setObject(4, runSummary.getGoTimestamp());
+            preparedStatement.setObject(5, runSummary.getEndTimestamp());
+            preparedStatement.setObject(6, runSummary.getTriggerRate());
+            preparedStatement.setObject(7, runSummary.getTriggerConfigName());
+            preparedStatement.setObject(8, runSummary.getTiTimeOffset());
+            preparedStatement.setObject(9, runSummary.getLivetimeClock());
+            preparedStatement.setObject(10, runSummary.getLivetimeFcupTdc());
+            preparedStatement.setObject(11, runSummary.getLivetimeFcupTrg());
+            preparedStatement.setObject(12, runSummary.getTarget());
+            preparedStatement.setObject(13, runSummary.getNotes());
+            preparedStatement.setInt(14, runSummary.getRun());
+            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();
+                }
+            }
+        }
+    }
+    
    
     /**
      * Return <code>true</code> if a run summary exists in the database for the run number.

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	Wed Feb 10 14:26:49 2016
@@ -75,7 +75,7 @@
     private Integer totalFiles;
    
     /**
-     * Get the name of the trigger config file.
+     * Name of the trigger config file.
      */
     private String triggerConfigName;
 

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SpreadsheetBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SpreadsheetBuilder.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/SpreadsheetBuilder.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,74 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.util.logging.Logger;
+
+import org.hps.conditions.run.RunSpreadsheet;
+import org.hps.conditions.run.RunSpreadsheet.RunData;
+
+/**
+ * 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 data, scaler data, trigger config and SVT config information from all of the EVIO files in a run.
+ * <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 SpreadsheetBuilder extends AbstractRunBuilder {
+    
+    private static final Logger LOGGER = Logger.getLogger(SpreadsheetBuilder.class.getPackage().getName());
+    
+    private File spreadsheetFile;
+    
+    void setSpreadsheetFile(File spreadsheetFile) {
+        this.spreadsheetFile = spreadsheetFile;
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    void build() {       
+        if (this.spreadsheetFile == null) {
+            throw new IllegalStateException("The spreadsheet file was never set.");
+        }
+        if (getRunSummary() == null) {
+            throw new IllegalStateException("The run summary was never set.");
+        }
+        LOGGER.fine("updating from spreadsheet file " + spreadsheetFile.getPath());
+        RunSpreadsheet runSpreadsheet = new RunSpreadsheet(spreadsheetFile);
+        RunData data = runSpreadsheet.getRunMap().get(getRunSummary().getRun());        
+        if (data != null) {
+            LOGGER.info("found run data ..." + '\n' + data.getRecord());
+            
+            // Trigger config name.
+            String triggerConfigName = data.getRecord().get("trigger_config");
+            if (triggerConfigName != null) {
+                getRunSummary().setTriggerConfigName(triggerConfigName);
+                LOGGER.info("set trigger config name <" + getRunSummary().getTriggerConfigName() + "> from spreadsheet");
+            }
+            
+            // Notes.
+            String notes = data.getRecord().get("notes");
+            if (notes != null) {
+                getRunSummary().setNotes(notes);
+                LOGGER.info("set notes <" + getRunSummary().getNotes() + "> from spreadsheet");
+            }
+            
+            // Target.
+            String target = data.getRecord().get("target");
+            if (target != null) {
+                getRunSummary().setTarget(target);
+                LOGGER.info("set target <" + getRunSummary().getTarget() + "> from spreadsheet");
+            }
+        } else {
+            LOGGER.warning("No record for this run was found in spreadsheet.");
+        }
+    }
+}

Added: java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/TriggerConfigBuilder.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/TriggerConfigBuilder.java	(added)
+++ java/branches/jeremy-dev/run-database/src/main/java/org/hps/run/database/TriggerConfigBuilder.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,50 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.hps.record.daqconfig.TriggerConfigEvioProcessor;
+import org.hps.record.evio.EvioFileUtilities;
+import org.hps.record.triggerbank.TriggerConfigData;
+import org.jlab.coda.jevio.EvioEvent;
+import org.jlab.coda.jevio.EvioException;
+import org.jlab.coda.jevio.EvioReader;
+
+public class TriggerConfigBuilder extends AbstractRunBuilder {
+    
+    private TriggerConfigData triggerConfig;
+    private List<File> files = null;
+    
+    void setFiles(List<File> files) {
+        this.files = files;
+    }
+    
+    void build() {        
+        int fileIndex = files.size() - 1;
+        TriggerConfigEvioProcessor processor = new TriggerConfigEvioProcessor();
+        while (triggerConfig == null && fileIndex >= 0) {
+            File file = files.get(fileIndex);
+            try {
+                EvioReader reader = EvioFileUtilities.open(file, true);
+                EvioEvent evioEvent = reader.parseNextEvent();
+                while (evioEvent != null) {
+                    processor.process(evioEvent);
+                    if (processor.getTriggerConfigData() != null 
+                            && processor.getTriggerConfigData().isValid()) {
+                        triggerConfig = processor.getTriggerConfigData();
+                        break;
+                    }
+                    evioEvent = reader.parseNextEvent();
+                }
+                fileIndex -= 1;
+            } catch (EvioException | IOException e) {
+                throw new RuntimeException(e);
+            }            
+        }
+    }
+    
+    TriggerConfigData getTriggerConfigData() {
+        return triggerConfig;
+    }
+}

Added: java/branches/jeremy-dev/run-database/src/test/java/org/hps/run/database/RunBuilderTest.java
 =============================================================================
--- java/branches/jeremy-dev/run-database/src/test/java/org/hps/run/database/RunBuilderTest.java	(added)
+++ java/branches/jeremy-dev/run-database/src/test/java/org/hps/run/database/RunBuilderTest.java	Wed Feb 10 14:26:49 2016
@@ -0,0 +1,63 @@
+package org.hps.run.database;
+
+import java.io.File;
+import java.sql.Connection;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.hps.conditions.database.ConnectionParameters;
+import org.srs.datacat.client.ClientBuilder;
+
+public class RunBuilderTest extends TestCase {
+    
+    private static final int RUN = 5403;
+    private static String DATACAT_URL = "http://localhost:8080/datacat-v0.5-SNAPSHOT/r";
+    private static String SPREADSHEET = "/work/hps/rundb/HPS_Runs_2015_Sheet1.csv";
+    private static String FOLDER = "/HPS/test";
+    private static String SITE = "SLAC";
+    
+    private static final ConnectionParameters CONNECTION_PARAMETERS = 
+            new ConnectionParameters("root", "derp", "hps_run_db", "localhost");
+        
+    public void testRunBuilder() throws Exception {
+        
+        RunSummaryImpl runSummary = new RunSummaryImpl(RUN);
+        
+        // datacat
+        DatacatBuilder datacatBuilder = new DatacatBuilder();
+        datacatBuilder.setDatacatClient(new ClientBuilder().setUrl(DATACAT_URL).build());
+        datacatBuilder.setFolder(FOLDER);
+        datacatBuilder.setSite(SITE);
+        datacatBuilder.setRunSummary(runSummary);
+        datacatBuilder.build();
+        
+        List<File> files = datacatBuilder.getFileList();
+        
+        // livetime measurements
+        LivetimeBuilder livetimeBuilder = new LivetimeBuilder();
+        livetimeBuilder.setRunSummary(runSummary);
+        livetimeBuilder.setFiles(files);
+        livetimeBuilder.build();
+        
+        // trigger config
+        TriggerConfigBuilder configBuilder = new TriggerConfigBuilder();
+        configBuilder.setFiles(files);
+        configBuilder.build();
+        
+        // run spreadsheet
+        SpreadsheetBuilder spreadsheetBuilder = new SpreadsheetBuilder();
+        spreadsheetBuilder.setSpreadsheetFile(new File(SPREADSHEET));
+        spreadsheetBuilder.setRunSummary(datacatBuilder.getRunSummary());
+        spreadsheetBuilder.build();
+        
+        // database updater
+        Connection connection = CONNECTION_PARAMETERS.createConnection();
+        DatabaseUpdater updater = new DatabaseUpdater(connection);
+        updater.setRunSummary(runSummary);
+        System.out.println("built run summary ...");
+        System.out.println(runSummary);
+        //updater.setTriggerConfigData(configBuilder.getTriggerConfigData());
+        //updater.update();
+    }
+}

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

November 2017
August 2017
July 2017
January 2017
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013

ATOM RSS1 RSS2



LISTSERV.SLAC.STANFORD.EDU

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager

Privacy Notice, Security Notice and Terms of Use