Author: [log in to unmask] Date: Thu Aug 20 12:50:12 2015 New Revision: 3380 Log: Major overhaul of record-util module for supporting run database. HPSJAVA-575, HPSJAVA-576 Added: java/trunk/record-util/src/main/java/org/hps/record/epics/Epics20sVariables.java java/trunk/record-util/src/main/java/org/hps/record/epics/Epics2sVariables.java java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java - copied, changed from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java java/trunk/record-util/src/main/java/org/hps/record/evio/EventCountProcessor.java - copied, changed from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EventCountProcessor.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileFilter.java - copied, changed from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileFilter.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaData.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaDataReader.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSequenceComparator.java - copied, changed from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java - copied, changed from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileUtilities.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoop.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryMap.java - copied, changed from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDao.java java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDaoImpl.java java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDao.java java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDaoImpl.java java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDao.java java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDaoImpl.java java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDao.java java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDaoImpl.java java/trunk/record-util/src/main/java/org/hps/record/run/package-info.java Removed: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsCsvExporter.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EventCountProcessor.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileFilter.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileList.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileUtilities.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLogUpdater.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryUpdater.java java/trunk/record-util/src/main/java/org/hps/record/run/AbstractRunDatabaseReader.java java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataReader.java java/trunk/record-util/src/main/java/org/hps/record/run/EvioFileListReader.java java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryReader.java java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataReader.java Modified: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsData.java java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsEvioProcessor.java java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsGenericObject.java java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsHeader.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/Crawler.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileVisitor.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/JCacheManager.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunFilter.java java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java java/trunk/record-util/src/main/java/org/hps/record/run/RunManager.java java/trunk/record-util/src/main/java/org/hps/record/run/RunSummary.java java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalerData.java java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalersEvioProcessor.java Added: java/trunk/record-util/src/main/java/org/hps/record/epics/Epics20sVariables.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/epics/Epics20sVariables.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/Epics20sVariables.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,54 @@ +package org.hps.record.epics; + +import java.util.HashMap; +import java.util.Map; + +/** + * Static list of variable names contained in 20s EPICS data block from Eng Run 2015 data along + * with a brief description (if known). + * + * @author Jeremy McCormick, SLAC + */ +public final class Epics20sVariables { + + /** + * Map with variable names and description. + */ + static final Map<String, String> VARIABLES = new HashMap<String, String>(); + + /** + * Variable definitions. + */ + static { + VARIABLES.put("beam_stop.RBV", "beam stop motor position"); + VARIABLES.put("hps:svt_bot:motor.RBV", "SVT bottom motor position"); + VARIABLES.put("hps:svt_top:motor.RBV", "SVT top motor position"); + VARIABLES.put("hps:target:motor.RBV", "target motor position"); + VARIABLES.put("hps_collimator.RBV", "collimator motor position"); + VARIABLES.put("scalerS10b", "DWN-B beamline counter"); + VARIABLES.put("scalerS11b", "DWN-R beamline counter"); + VARIABLES.put("scalerS8b", "DWN-T beamline counter"); + VARIABLES.put("scalerS9b", "DWN-L beamline counter"); + VARIABLES.put("scaler_cS3b", "UPS-L beamline counter"); + VARIABLES.put("scaler_cS4b", "UPS-R beamline counter"); + VARIABLES.put("scaler_cS5b", "TAG-L beamline counter"); + VARIABLES.put("scaler_cS6b", "TAG-T beamline counter"); + VARIABLES.put("scaler_cS7b", "TAG-T2 beamline counter"); + VARIABLES.put("SMRPOSB", ""); + } + + /** + * Get the variable map with the names and descriptions. + * + * @return the variable map + */ + public static Map<String, String> getVariables() { + return VARIABLES; + } + + /** + * Do not allow class instantiation. + */ + private Epics20sVariables() { + } +} Added: java/trunk/record-util/src/main/java/org/hps/record/epics/Epics2sVariables.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/epics/Epics2sVariables.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/Epics2sVariables.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,65 @@ +package org.hps.record.epics; + +import java.util.HashMap; +import java.util.Map; + +/** + * List of EPICS variables and their descriptions contained in the 2s data bank from Eng Run 2015 data. + * + * @author Jeremy McCormick, SLAC + */ +public final class Epics2sVariables { + + /** + * Map with variable names and description. + */ + static final Map<String, String> VARIABLES = new HashMap<String, String>(); + + /** + * Variable definitions. + */ + static { + VARIABLES.put("MBSY2C_energy", "Beam energy according to Hall B BSY dipole string"); + VARIABLES.put("PSPECIRBCK", "Pair Spectrometer Current Readback"); + VARIABLES.put("HPS:LS450_2:FIELD", "Frascati probe field"); + VARIABLES.put("HPS:LS450_1:FIELD", "Pair Spectrometer probe field"); + VARIABLES.put("MTIRBCK", "Frascati Current Readback"); + VARIABLES.put("VCG2C21 2C21", "Vacuum gauge pressure"); + VARIABLES.put("VCG2C21A", "2C21A Vacuum gauge pressure"); + VARIABLES.put("VCG2C24A", "2C24A Vacuum gauge pressure"); + VARIABLES.put("VCG2H00A", "2H00 Vacuum gauge pressure"); + VARIABLES.put("VCG2H01A", "2H01 Vacuum gauge pressure"); + VARIABLES.put("VCG2H02A", "2H02 Vacuum gauge pressure"); + VARIABLES.put("scaler_calc1", "Faraday cup current"); + VARIABLES.put("scalerS12b", "HPS-Left beam halo count"); + VARIABLES.put("scalerS13b", "HPS-Right beam halo count"); + VARIABLES.put("scalerS14b", "HPS-Top beam halo count"); + VARIABLES.put("scalerS15b", "HPS-SC beam halo count"); + VARIABLES.put("hallb_IPM2C21A_XPOS", "Beam position X at 2C21"); + VARIABLES.put("hallb_IPM2C21A_YPOS", "Beam position Y at 2C21"); + VARIABLES.put("hallb_IPM2C21A_CUR", "Current at 2C21"); + VARIABLES.put("hallb_IPM2C24A_XPOS", "Beam position X at 2C24"); + VARIABLES.put("hallb_IPM2C24A_YPOS", "Beam position Y at 2C24"); + VARIABLES.put("hallb_IPM2C24A_CUR", "Current at 2C24"); + VARIABLES.put("hallb_IPM2H00_XPOS", "Beam position X at 2H00"); + VARIABLES.put("hallb_IPM2H00_YPOS", "Beam position Y at 2H00"); + VARIABLES.put("hallb_IPM2H00_CUR", "Current at 2H00"); + VARIABLES.put("hallb_IPM2H02_YPOS", "Beam position X at 2H02"); + VARIABLES.put("hallb_IPM2H02_XPOS", "Beam position Y at 2H02"); + } + + /** + * Get the variable map. + * + * @return the variable map + */ + public static Map<String, String> getVariables() { + return VARIABLES; + } + + /** + * Do not allow class instantiation. + */ + private Epics2sVariables() { + } +} Modified: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsData.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsData.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsData.java Thu Aug 20 12:50:12 2015 @@ -42,66 +42,6 @@ private static final String EPICS_VARIABLE_NAMES = "EPICS_VARIABLE_NAMES"; /** - * This map contains the list of EPICS keys and their descriptions from the<br/> - * <a href="https://confluence.slac.stanford.edu/display/hpsg/EVIO+Data+Format">EVIO Data Format Confluence Page</a> - */ - private final static Map<String, String> VARIABLES = new HashMap<String, String>(); - - /** - * List of possible EPICS keys. - */ - static { - VARIABLES.put("MBSY2C_energy", "Beam energy according to Hall B BSY dipole string"); - VARIABLES.put("PSPECIRBCK", "Pair Spectrometer Current Readback"); - VARIABLES.put("HPS:LS450_2:FIELD", "Frascati probe field"); - VARIABLES.put("HPS:LS450_1:FIELD", "Pair Spectrometer probe field"); - VARIABLES.put("MTIRBCK", "Frascati Current Readback"); - VARIABLES.put("VCG2C21 2C21", "Vacuum gauge pressure"); - VARIABLES.put("VCG2C21A", "2C21A Vacuum gauge pressure"); - VARIABLES.put("VCG2C24A", "2C24A Vacuum gauge pressure"); - VARIABLES.put("VCG2H00A", "2H00 Vacuum gauge pressure"); - VARIABLES.put("VCG2H01A", "2H01 Vacuum gauge pressure"); - VARIABLES.put("VCG2H02A", "2H02 Vacuum gauge pressure"); - VARIABLES.put("scaler_calc1", "Faraday cup current"); - VARIABLES.put("scalerS12b", "HPS-Left beam halo count"); - VARIABLES.put("scalerS13b", "HPS-Right beam halo count"); - VARIABLES.put("scalerS14b", "HPS-Top beam halo count"); - VARIABLES.put("scalerS15b", "HPS-SC beam halo count"); - VARIABLES.put("hallb_IPM2C21A_XPOS", "Beam position X at 2C21"); - VARIABLES.put("hallb_IPM2C21A_YPOS", "Beam position Y at 2C21"); - VARIABLES.put("hallb_IPM2C21A_CUR", "Current at 2C21"); - VARIABLES.put("hallb_IPM2C24A_XPOS", "Beam position X at 2C24"); - VARIABLES.put("hallb_IPM2C24A_YPOS", "Beam position Y at 2C24"); - VARIABLES.put("hallb_IPM2C24A_CUR", "Current at 2C24"); - VARIABLES.put("hallb_IPM2H00_XPOS", "Beam position X at 2H00"); - VARIABLES.put("hallb_IPM2H00_YPOS", "Beam position Y at 2H00"); - VARIABLES.put("hallb_IPM2H00_CUR", "Current at 2H00"); - VARIABLES.put("hallb_IPM2H02_YPOS", "Beam position X at 2H02"); - VARIABLES.put("hallb_IPM2H02_XPOS", "Beam position Y at 2H02"); - } - - /** - * Get the description of a named EPICS variable. - * - * @param name the name of the variable - */ - public static String getVariableDescription(final String name) { - return VARIABLES.get(name); - } - - /** - * Get the static list of all available EPICs variable names. - * <p> - * This could be different than the variable names which were actually written into the collection header. For this, - * instead use the method {@link #getKeys()} method. - * - * @return the set of default EPICS variable names - */ - public static Set<String> getVariableNames() { - return VARIABLES.keySet(); - }; - - /** * <p> * Read data into this object from an LCIO event using the default collection name. * <p> @@ -109,7 +49,7 @@ * {@link org.lcsim.util.Driver#process(EventHeader)} method. * * @param event the LCIO event - * @return the EPICS data from the event + * @return the EPICS data from the event or null if none exists */ public static EpicsData read(final EventHeader event) { if (event.hasCollection(GenericObject.class, EpicsData.DEFAULT_COLLECTION_NAME)) { @@ -210,9 +150,6 @@ /** * Get the list of EPICS variables used by this object. - * <p> - * This could potentially be different than the list of default names from {@link #getVariableNames()} as not all - * variables are included in every EPICS event. * * @return the list of used EPICS variable names */ @@ -243,14 +180,15 @@ * * @param epicsHeader the {@link EpicsHeader} object */ - void setEpicsHeader(final EpicsHeader epicsHeader) { + public void setEpicsHeader(final EpicsHeader epicsHeader) { this.epicsHeader = epicsHeader; } /** - * Set a double value by name. - * - * @return the value from the key + * Set a variable's value. + * + * @param name the name of the variable + * @param value the new value of the variable */ public void setValue(final String name, final double value) { this.dataMap.put(name, value); @@ -268,7 +206,7 @@ newObject.setKeys(new String[this.dataMap.size()]); newObject.setValues(new double[this.dataMap.size()]); - + int index = 0; for (final String key : this.dataMap.keySet()) { newObject.setKey(index, key); @@ -278,10 +216,8 @@ // Write header information into the object's int array. if (epicsHeader != null) { - final int[] headerData = new int[] { - epicsHeader.getRun(), - epicsHeader.getSequence(), - epicsHeader.getTimeStamp()}; + final int[] headerData = new int[] {epicsHeader.getRun(), epicsHeader.getSequence(), + epicsHeader.getTimestamp()}; newObject.setHeaderData(headerData); } return newObject; Modified: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsEvioProcessor.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsEvioProcessor.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsEvioProcessor.java Thu Aug 20 12:50:12 2015 @@ -50,7 +50,7 @@ // Is this an EPICS event? if (EventTagConstant.EPICS.isEventTag(evio)) { - LOGGER.info("processing EPICS event " + evio.getEventNumber()); + LOGGER.fine("processing EPICS event " + evio.getEventNumber()); // Find the bank with the EPICS data string. final BaseStructure epicsBank = EvioBankTag.EPICS_STRING.findBank(evio); @@ -73,7 +73,7 @@ } else { // This is an error because the string data bank should always be present in EPICS events. - final RuntimeException x = new RuntimeException("No EPICS data bank found in EPICS event."); + final RuntimeException x = new RuntimeException("No data bank found in EPICS event."); LOGGER.log(Level.SEVERE, x.getMessage(), x); throw x; } Modified: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsGenericObject.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsGenericObject.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsGenericObject.java Thu Aug 20 12:50:12 2015 @@ -36,11 +36,23 @@ return this.values[index]; } + /** + * Dummy implementation. + * + * @param index the array index + * @return always returns 0 + */ @Override public float getFloatVal(final int index) { return 0; } + /** + * Get an int value which is used to store the EPICS header information. + * + * @param index the array index + * @return the int value at <code>index</code> + */ @Override public int getIntVal(final int index) { return headerData[index]; @@ -75,11 +87,21 @@ return this.values.length; } + /** + * Dummy implementation. + * + * @return always returns 0 + */ @Override public int getNFloat() { return 0; } + /** + * Get the number of int values which is the length of the data header. + * + * @return the number of int values + */ @Override public int getNInt() { return this.headerData.length; Modified: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsHeader.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsHeader.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsHeader.java Thu Aug 20 12:50:12 2015 @@ -1,7 +1,7 @@ package org.hps.record.epics; /** - * Representation of EPICs header data (run, sequence, time stamp). + * Representation of EPICs header data from the EVIO bank (run, sequence, time stamp). * * @author Jeremy McCormick, SLAC */ @@ -31,14 +31,15 @@ private final int sequence; /** - * The time stamp in seconds (Unix). + * The unix time in seconds. */ private final int timestamp; /** * Class constructor. * <p> - * The data array should be length 3 and usually will come from the int data of a <code>GenericObject</code>. + * The data array should be length 3 and usually will come from the int data of a <code>GenericObject</code>. In + * order, it should contain the run, sequence and timestamp values. * * @param data the header data with length 3 */ @@ -74,7 +75,7 @@ * * @return the time stamp */ - public int getTimeStamp() { + public int getTimestamp() { return timestamp; } } Copied: java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java (from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java) ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsRunProcessor.java Thu Aug 20 12:50:12 2015 @@ -1,25 +1,19 @@ -package org.hps.record.evio.crawler; +package org.hps.record.epics; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -import org.hps.record.epics.EpicsData; -import org.hps.record.epics.EpicsEvioProcessor; import org.hps.record.evio.EvioEventProcessor; import org.jlab.coda.jevio.EvioEvent; /** - * Create a summary log of EPICS information found in EVIO events. + * Creates a list of EPICS data found in EVIO events across an entire job. * * @author Jeremy McCormick, SLAC */ -final class EpicsLog extends EvioEventProcessor { - - /** - * A count of how many times a given EPICS variable is found in the input for computing the mean value across the - * entire run. - */ - private final Map<String, Integer> counts = new HashMap<String, Integer>(); +public final class EpicsRunProcessor extends EvioEventProcessor { /** * The current EPICS data block from the EVIO events (last one that was found). @@ -27,9 +21,11 @@ private EpicsData currentEpicsData; /** - * The summary information for the variables computed from the mean values across the whole run. + * Collection of the EPICS data accumulated during the job. + * <p> + * A set is used here to avoid adding duplicate objects. */ - private final EpicsData logData = new EpicsData(); + private Set<EpicsData> epicsDataSet; /** * The processor for extracting the EPICS information from EVIO events. @@ -39,31 +35,16 @@ /** * Create an EPICs log. */ - EpicsLog() { + public EpicsRunProcessor() { } /** - * End of job hook which computes the mean values for all EPICS variables found in the run. + * Get the EPICS data from the job. + * + * @return the EPICS data from the job */ - @Override - public void endJob() { - System.out.println(this.logData); - - // Compute means for all EPICS variables. - for (final String name : this.logData.getKeys()) { - final double total = this.logData.getValue(name); - final double mean = total / this.counts.get(name); - this.logData.setValue(name, mean); - } - } - - /** - * Get the {@link org.hps.record.epics.EpicsData} which contains mean values for the run. - * - * @return the {@link org.hps.record.epics.EpicsData} for the run - */ - EpicsData getEpicsData() { - return this.logData; + public List<EpicsData> getEpicsData() { + return new ArrayList<EpicsData>(this.epicsDataSet); } /** @@ -71,33 +52,22 @@ */ @Override public void process(final EvioEvent evioEvent) { + + // Call the processor that will load EPICS data if it exists in the event. this.processor.process(evioEvent); this.currentEpicsData = this.processor.getEpicsData(); - this.update(); + + // Add EPICS data to the collection. + if (this.currentEpicsData != null) { + this.epicsDataSet.add(this.currentEpicsData); + } } /** - * Update state from the current EPICS data. - * <p> - * If the current data is <code>null</code>, this method does nothing. + * Start of job hook (reset the list of EPICS data). */ - private void update() { - if (this.currentEpicsData != null) { - for (final String name : this.currentEpicsData.getKeys()) { - if (!this.logData.getKeys().contains(name)) { - this.logData.setValue(name, 0.); - } - if (!this.counts.keySet().contains(name)) { - this.counts.put(name, 0); - } - int count = this.counts.get(name); - count += 1; - this.counts.put(name, count); - final double value = this.logData.getValue(name) + this.currentEpicsData.getValue(name); - this.logData.setValue(name, value); - // System.out.println(name + " => added " + this.currentData.getValue(name) + "; total = " + value + - // "; mean = " + value / count); - } - } + @Override + public void startJob() { + epicsDataSet = new HashSet<EpicsData>(); } } Copied: java/trunk/record-util/src/main/java/org/hps/record/evio/EventCountProcessor.java (from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EventCountProcessor.java) ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EventCountProcessor.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EventCountProcessor.java Thu Aug 20 12:50:12 2015 @@ -1,12 +1,8 @@ -package org.hps.record.evio.crawler; +package org.hps.record.evio; import java.util.HashMap; import java.util.Map; -import org.hps.record.evio.EventTagBitMask; -import org.hps.record.evio.EventTagConstant; -import org.hps.record.evio.EvioEventProcessor; -import org.hps.record.evio.EvioEventUtilities; import org.jlab.coda.jevio.EvioEvent; /** Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioEventConstants.java Thu Aug 20 12:50:12 2015 @@ -16,29 +16,16 @@ * EPICS bank tag. */ public static final int EPICS_BANK_TAG = 57620; - - - /** - * Tag of bank containing the EPICS data bank. - */ - public static final int EPICS_PARENT_BANK_TAG = 129; - - /** - * EPICS 20 second event tag. - */ - // FIXME: This is unused and not handled in event processing. - public static final int EPICS_BANK_TAG_20s = -1; - - /** - * EPICS 2 second event tag. - */ - // FIXME: This is unused and not handled in event processing. - public static final int EPICS_BANK_TAG_2s = -1; /** * EPICS event tag. */ public static final int EPICS_EVENT_TAG = 31; + + /** + * Tag of bank containing the EPICS data bank. + */ + public static final int EPICS_PARENT_BANK_TAG = 129; /** * Event ID bank tag. @@ -58,7 +45,6 @@ /** * Pause event tag. */ - // FIXME: Not generally handled or used in event processing. public static final int PAUSE_EVENT_TAG = 19; /** Copied: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileFilter.java (from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileFilter.java) ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileFilter.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileFilter.java Thu Aug 20 12:50:12 2015 @@ -1,27 +1,29 @@ -package org.hps.record.evio.crawler; +package org.hps.record.evio; import java.io.File; import java.io.FileFilter; /** - * This is a simple file filter that will accept EVIO files with a certain convention to their naming which looks like - * <i>FILENAME.evio.SEQUENCE</i>. This matches the convention used by the CODA DAQ software. + * This is a simple file filter that will accept EVIO files with a certain pattern to their file names:<br/> + * <i>FILENAME.evio.SEQUENCE</i>. + * <p> + * This matches the convention used by the CODA DAQ software. * * @author Jeremy McCormick, SLAC */ -final class EvioFileFilter implements FileFilter { +public final class EvioFileFilter implements FileFilter { /** - * Return <code>true</code> if file is an EVIO file with correct file name convention. + * Return <code>true</code> if file is an EVIO file with the correct file naming convention. * - * @return <code>true</code> if file is an EVIO file with correct file name convention + * @return <code>true</code> if file is an EVIO file with correct file naming convention */ @Override public boolean accept(final File pathname) { final boolean isEvio = pathname.getName().contains(".evio"); boolean hasSeqNum = false; try { - EvioFileUtilities.getSequence(pathname); + EvioFileUtilities.getSequenceFromName(pathname); hasSeqNum = true; } catch (final Exception e) { } Added: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaData.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaData.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaData.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,315 @@ +package org.hps.record.evio; + +import java.io.File; +import java.util.Date; + +/** + * Meta data that can be extracted from EVIO files. + * + * @author Jeremy McCormick, SLAC + */ +public final class EvioFileMetaData { + + /** + * The number of bad events in the file that are unreadable. + */ + private int badEventCount; + + /** + * The total number of bytes in the file. + */ + private long byteCount; + + /** + * The end date of the file which will be taken from the <i>END</i> event or the last physics event. + */ + private Date endDate; + + /** + * The last event number in the file. + */ + private int endEvent; + + /** + * The number of events in the file. + */ + private int eventCount; + + /** + * The EVIO file which is set at class construction time and cannot be changed. + */ + private final File evioFile; + + /** + * <code>true</code> if there is an <i>END</i> event in this file. + */ + private boolean hasEnd; + + /** + * <code>true</code> if there is a <i>PRESTART</i> event in this file. + */ + private boolean hasPrestart; + + /** + * The run number. + */ + private int run; + + /** + * The file sequence number. + */ + private int sequence; + + /** + * The start date which comes from the <i>PRESTART</i> event or the first physics event. + */ + private Date startDate; + + /** + * The first event number in the file. + */ + private int startEvent; + + /** + * Create a meta data object. + * + * @param evioFile the EVIO file to which the meta data applies + */ + public EvioFileMetaData(final File evioFile) { + if (evioFile == null) { + throw new IllegalArgumentException("The EVIO file argument is null."); + } + if (!evioFile.exists()) { + throw new IllegalArgumentException("The file " + evioFile.getPath() + + " does not exist or it is inaccessible."); + } + this.evioFile = evioFile; + } + + /** + * Get the bad event count. + * + * @return the bad event count + */ + public int getBadEventCount() { + return badEventCount; + } + + /** + * Get the byte count. + * + * @return the byte count + */ + public long getByteCount() { + return byteCount; + } + + /** + * Get the end date. + * + * @return the end date + */ + public Date getEndDate() { + return endDate; + } + + /** + * Get the end event number. + * + * @return the end event number + */ + public int getEndEvent() { + return endEvent; + } + + /** + * Get the number of events in the file (all types). + * + * @return the number of events in the file + */ + public int getEventCount() { + return eventCount; + } + + /** + * Get the EVIO file. + * + * @return the EVIO file + */ + public File getEvioFile() { + return evioFile; + } + + /** + * Get the run number. + * + * @return the run number + */ + public Integer getRun() { + return run; + } + + /** + * Get the file sequence number, numbered from 0. + * + * @return the file sequence number + */ + public int getSequence() { + return sequence; + } + + /** + * Get the start date. + * + * @return the start date + */ + public Date getStartDate() { + return startDate; + } + + /** + * Get the first event number in the file. + * + * @return the first event number in the file + */ + public int getStartEvent() { + return startEvent; + } + + /** + * Return <code>true</code> if the file has an EVIO <i>END</i> event. + * + * @return <code>true</code> if the file has an EVIO <i>END</i> event + */ + public boolean hasEnd() { + return hasEnd; + } + + /** + * Return <code>true</code> if the file has an EVIO <i>PRESTART</i> event. + * + * @return <code>true</code> if the file has an EVIO <i>PRESTART</i> event + */ + public boolean hasPrestart() { + return hasPrestart; + } + + /** + * Set the bad event count. + * + * @param badEventCount the bad event count + */ + void setBadEventCount(final int badEventCount) { + if (badEventCount < 0) { + throw new IllegalArgumentException("badEventCount"); + } + this.badEventCount = badEventCount; + } + + /** + * Set the byte count. + * + * @param byteCount the byte count + */ + void setByteCount(final long byteCount) { + if (byteCount < 0) { + throw new IllegalArgumentException("byteCount"); + } + this.byteCount = byteCount; + } + + /** + * Set the end date. + * + * @param endDate the end date + */ + void setEndDate(final Date endDate) { + this.endDate = endDate; + } + + /** + * Set the end event number. + * + * @param endEvent the end event number + */ + void setEndEvent(final int endEvent) { + this.endEvent = endEvent; + } + + /** + * Set the event count. + * + * @param eventCount the event count + */ + void setEventCount(final int eventCount) { + this.eventCount = eventCount; + } + + /** + * Set whether the file has an EVIO <i>END</i> event. + * + * @param hasEnd <code>true</code> if file has an EVIO <i>END</i> event + */ + void setHasEnd(final boolean hasEnd) { + this.hasEnd = hasEnd; + } + + /** + * Set whether the file has an EVIO <i>PRESTART</i> event. + * + * @param hasPrestart <code>true</code> if file has an EVIO <i>PRESTART</i> event + */ + void setHasPrestart(final boolean hasPrestart) { + this.hasPrestart = hasPrestart; + } + + /** + * Set the run number. + * + * @param run the run number + */ + void setRun(final int run) { + this.run = run; + } + + /** + * Set the sequence number + * + * @param sequence the sequence number + */ + void setSequence(final int sequence) { + this.sequence = sequence; + } + + /** + * Set the start date. + * + * @param startDate the start date + */ + void setStartDate(final Date startDate) { + this.startDate = startDate; + } + + /** + * Set the start event number. + * + * @param startEvent the start event number + */ + void setStartEvent(final int startEvent) { + this.startEvent = startEvent; + } + + /** + * Convert this object to a human readable string. + * + * @return this object converted to a string + */ + @Override + public String toString() { + return "EvioFileMetaData { evioFile: " + this.evioFile + ", startDate: " + this.startDate + "endDate: " + + this.endDate + "badEventCount: " + this.badEventCount + ", byteCount: " + this.byteCount + + ", eventCount: " + this.eventCount + ", hasPrestart: " + this.hasPrestart + ", hasEnd: " + + this.hasEnd + ", run: " + this.run + ", fileNumber: " + sequence + ", startEvent: " + + this.startEvent + ", endEvent: " + endEvent + " }"; + } +} Added: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaDataReader.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaDataReader.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileMetaDataReader.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,151 @@ +package org.hps.record.evio; + +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; + +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.util.log.DefaultLogFormatter; +import org.lcsim.util.log.LogUtil; + +/** + * Read {@link EvioFileMetaData} from an EVIO file. + * + * @author Jeremy McCormick, SLAC + */ +public class EvioFileMetaDataReader { + + /** + * The class logger. + */ + private static Logger LOGGER = LogUtil.create(EvioFileMetaDataReader.class, new DefaultLogFormatter(), Level.ALL); + + /** + * Get meta data from the EVIO file. + * + * @param evioFile the EVIO file + * @return the file's meta data + */ + public EvioFileMetaData getMetaData(final File evioFile) { + + LOGGER.info("getting meta data for " + evioFile.getPath()); + + final EvioFileMetaData metaData = new EvioFileMetaData(evioFile); + + EvioEvent evioEvent = null; + Date startDate = null; + Date endDate = null; + int badEventCount = 0; + int eventCount = 0; + int byteCount = 0; + boolean hasPrestart = false; + boolean hasEnd = false; + int[] headBankData = null; + int[] eventIdData = null; + Integer run = null; + Integer endEvent = null; + Integer startEvent = null; + + final int fileNumber = EvioFileUtilities.getSequenceFromName(evioFile); + LOGGER.info("set file number to " + fileNumber); + + try { + final EvioReader evioReader = EvioFileUtilities.open(evioFile, false); + LOGGER.getHandlers()[0].flush(); + eventCount = evioReader.getEventCount(); + while (true) { + try { + evioEvent = evioReader.parseNextEvent(); + if (evioEvent == null) { + break; + } + } catch (final Exception e) { + badEventCount++; + LOGGER.log(Level.WARNING, "got bad EVIO event", e); + continue; + } + byteCount += evioEvent.getTotalBytes(); + if (EventTagConstant.PRESTART.isEventTag(evioEvent)) { + LOGGER.info("found PRESTART"); + hasPrestart = true; + final int[] controlEventData = EvioEventUtilities.getControlEventData(evioEvent); + final long timestamp = controlEventData[0] * 1000L; + startDate = new Date(timestamp); + LOGGER.info("set start date to " + startDate); + if (run == null) { + run = controlEventData[1]; + LOGGER.info("set run to " + run); + } + } else if (EventTagConstant.END.isEventTag(evioEvent)) { + LOGGER.info("found END"); + hasEnd = true; + final int[] controlEventData = EvioEventUtilities.getControlEventData(evioEvent); + final long timestamp = controlEventData[0] * 1000L; + endDate = new Date(timestamp); + LOGGER.info("set end date to " + endDate); + if (run == null) { + run = controlEventData[1]; + LOGGER.info("set run to " + run); + } + } else if (EventTagBitMask.PHYSICS.isEventTag(evioEvent)) { + final BaseStructure headBank = EvioEventUtilities.getHeadBank(evioEvent); + if (headBank != null) { + headBankData = headBank.getIntData(); + if (startDate == null) { + startDate = new Date(headBankData[3] * 1000); + LOGGER.info("set start date to " + endDate + " from physics event"); + } + if (run == null) { + run = headBankData[1]; + LOGGER.info("set run to " + run + " from physics event"); + } + } + eventIdData = EvioEventUtilities.getEventIdData(evioEvent); + if (eventIdData != null) { + if (startEvent == null) { + startEvent = eventIdData[0]; + LOGGER.info("set start event " + startEvent); + } + } + } + LOGGER.getHandlers()[0].flush(); + } + + if (endDate == null) { + if (headBankData != null) { + endDate = new Date(headBankData[3] * 1000); + LOGGER.info("set end date to " + endDate + " from last head bank"); + } + } + if (eventIdData != null) { + endEvent = eventIdData[0]; + LOGGER.info("set end event " + endEvent); + } + } catch (EvioException | IOException e) { + throw new RuntimeException(e); + } + + metaData.setStartDate(startDate); + metaData.setEndDate(endDate); + metaData.setBadEventCount(badEventCount); + metaData.setByteCount(byteCount); + metaData.setEventCount(eventCount); + metaData.setHasPrestart(hasPrestart); + metaData.setHasEnd(hasEnd); + metaData.setRun(run); + metaData.setSequence(fileNumber); + if (endEvent != null) { + metaData.setEndEvent(endEvent); + } + if (startEvent != null) { + metaData.setStartEvent(startEvent); + } + + return metaData; + } +} Copied: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSequenceComparator.java (from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java) ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSequenceComparator.java Thu Aug 20 12:50:12 2015 @@ -1,4 +1,4 @@ -package org.hps.record.evio.crawler; +package org.hps.record.evio; import java.io.File; import java.util.Comparator; @@ -8,7 +8,7 @@ * * @author Jeremy McCormick, SLAC */ -final class EvioFileSequenceComparator implements Comparator<File> { +public final class EvioFileSequenceComparator implements Comparator<File> { /** * Compare two EVIO files by their sequence numbers. @@ -17,8 +17,8 @@ */ @Override public int compare(final File o1, final File o2) { - final Integer sequenceNumber1 = EvioFileUtilities.getSequence(o1); - final Integer sequenceNumber2 = EvioFileUtilities.getSequence(o2); + final Integer sequenceNumber1 = EvioFileUtilities.getSequenceFromName(o1); + final Integer sequenceNumber2 = EvioFileUtilities.getSequenceFromName(o2); return sequenceNumber1.compareTo(sequenceNumber2); } } Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileSource.java Thu Aug 20 12:50:12 2015 @@ -12,12 +12,12 @@ import org.jlab.coda.jevio.EvioReader; /** - * A basic implementation of an <tt>AbstractRecordSource</tt> for supplying <tt>EvioEvent</tt> objects to a loop from - * EVIO files. + * 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. * - * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a> + * @author Jeremy McCormick, SLAC */ public final class EvioFileSource extends AbstractRecordSource { @@ -48,7 +48,7 @@ */ public EvioFileSource(final File file) { this.files.add(file); - openReader(); + this.openReader(); } /** @@ -58,7 +58,7 @@ */ public EvioFileSource(final List<File> files) { this.files.addAll(files); - openReader(); + this.openReader(); } /** @@ -89,6 +89,15 @@ @Override public Object getCurrentRecord() throws IOException { return this.currentEvent; + } + + /** + * Get the list of files. + * + * @return the list of files + */ + public List<File> getFiles() { + return this.files; } /** @@ -130,10 +139,10 @@ throw new IOException(e); } if (this.currentEvent == null) { - closeReader(); + this.closeReader(); this.fileIndex++; - if (!endOfFiles()) { - openReader(); + if (!this.endOfFiles()) { + this.openReader(); continue; } else { throw new NoSuchRecordException(); @@ -151,7 +160,7 @@ private void openReader() { try { System.out.println("Opening reader for file " + this.files.get(this.fileIndex) + " ..."); - this.reader = new EvioReader(this.files.get(this.fileIndex), false,true); + this.reader = new EvioReader(this.files.get(this.fileIndex), false, true); System.out.println("Done opening file."); } catch (EvioException | IOException e) { throw new RuntimeException(e); @@ -167,4 +176,5 @@ public boolean supportsNext() { return true; } + } Copied: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java (from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileUtilities.java) ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileUtilities.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioFileUtilities.java Thu Aug 20 12:50:12 2015 @@ -1,26 +1,24 @@ -package org.hps.record.evio.crawler; +package org.hps.record.evio; import java.io.File; import java.io.IOException; +import java.util.Collections; +import java.util.List; import java.util.logging.Logger; -import org.hps.record.evio.EvioEventConstants; -import org.hps.record.evio.EvioEventUtilities; -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.util.log.LogUtil; /** - * A miscellaneous collection of EVIO file utility methods used by the crawler package. + * A miscellaneous collection of EVIO file utility methods. * * @author Jeremy McCormick, SLAC */ -final class EvioFileUtilities { +public final class EvioFileUtilities { /** - * Setup logger. + * Setup class logger. */ private static final Logger LOGGER = LogUtil.create(EvioFileUtilities.class); @@ -36,7 +34,7 @@ * @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") */ - static File getCachedFile(final File file) { + public static File getCachedFile(final File file) { if (!isMssFile(file)) { throw new IllegalArgumentException("File " + file.getPath() + " is not on the JLab MSS."); } @@ -47,80 +45,13 @@ } /** - * Get the end date - * - * @param evioReader the <code>EvioReader</code> - * @return the run end date - */ - static Long getEndTimestamp(final EvioReader evioReader) { - - // Date endDate = null; - Long timestamp = null; - - try { - // Search for the last physics event in the last 5 events of the file. - System.out.println("going to event " + (evioReader.getEventCount() - 5) + " / " - + evioReader.getEventCount() + " to find end date"); - evioReader.gotoEventNumber(evioReader.getEventCount() - 5); - EvioEvent evioEvent = null; - EvioEvent lastPhysicsEvent = null; - - // Find last physics event. - while ((evioEvent = evioReader.parseNextEvent()) != null) { - if (EvioEventUtilities.isPhysicsEvent(evioEvent)) { - lastPhysicsEvent = evioEvent; - } - } - - // If there is no physics event found this is an error. - if (lastPhysicsEvent == null) { - throw new RuntimeException("No physics event found."); - } - - // Get the timestamp from the head bank of the physics event. - LOGGER.info("getting head bank date from " + lastPhysicsEvent.getEventNumber()); - final Long eventTimestamp = getHeadBankTimestamp(lastPhysicsEvent); - if (eventTimestamp != null) { - LOGGER.info("found end timestamp " + eventTimestamp); - timestamp = eventTimestamp; - } else { - throw new RuntimeException("No timestamp found in head bank."); - } - - } catch (EvioException | IOException e) { - throw new RuntimeException(e); - } - return timestamp; - } - - /** - * Get the date from the head bank. - * - * @param event the EVIO file - * @return the date from the head bank or null if not found - */ - static Long getHeadBankTimestamp(final EvioEvent event) { - // Date date = null; - Long timestamp = null; - final BaseStructure headBank = EvioEventUtilities.getHeadBank(event); - if (headBank != null) { - final int[] data = headBank.getIntData(); - final long time = data[3]; - if (time != 0L) { - timestamp = time * MILLISECONDS; - } - } - return timestamp; - } - - /** * Get the run number from the file name. * * @param file the EVIO file * @return the run number * @throws Exception if there is a problem parsing out the run number */ - static Integer getRun(final File file) { + public static Integer getRunFromName(final File file) { final String name = file.getName(); final int startIndex = name.lastIndexOf("_") + 1; final int endIndex = name.indexOf("."); @@ -134,65 +65,9 @@ * @return the file's sequence number * @throws Exception if there is an error parsing out the sequence number */ - static Integer getSequence(final File file) { + public static Integer getSequenceFromName(final File file) { final String name = file.getName(); return Integer.parseInt(name.substring(name.lastIndexOf(".") + 1)); - } - - /** - * Get the start date from the first physics event. - * - * @param evioReader the <code>EvioReader</code> - * @return the run start date - */ - static Long getStartTimestamp(final EvioReader evioReader) { - - Long timestamp = null; - - // Read events until there is a physics event and return its timestamp. - try { - EvioEvent event = null; - while ((event = evioReader.parseNextEvent()) != null) { - if (EvioEventUtilities.isPhysicsEvent(event)) { - if ((timestamp = getHeadBankTimestamp(event)) != null) { - break; - } - } - } - } catch (EvioException | IOException e) { - throw new RuntimeException(e); - } - return timestamp; - } - - /** - * Get the date from the control bank of an EVIO event. - * - * @param file the EVIO file - * @param eventTag the event tag on the bank - * @param gotoEvent an event to start the scanning - * @return the control bank date or null if not found - */ - static Long getTimestamp(final EvioReader evioReader, final int eventTag, final int gotoEvent) { - Long timestamp = null; - try { - EvioEvent evioEvent; - if (gotoEvent > 0) { - evioReader.gotoEventNumber(gotoEvent); - } else if (gotoEvent < 0) { - evioReader.gotoEventNumber(evioReader.getEventCount() + gotoEvent); - } - while ((evioEvent = evioReader.parseNextEvent()) != null) { - if (evioEvent.getHeader().getTag() == eventTag) { - final int[] data = EvioEventUtilities.getControlEventData(evioEvent); - timestamp = (long) (data[0] * MILLISECONDS); - break; - } - } - } catch (EvioException | IOException e) { - throw new RuntimeException(e); - } - return timestamp; } /** @@ -201,35 +76,8 @@ * @param file the file * @return <code>true</code> if the file is a cached file */ - static boolean isCachedFile(final File file) { + public static boolean isCachedFile(final File file) { return file.getPath().startsWith("/cache"); - } - - /** - * Return <code>true</code> if a valid CODA <i>END</i> event can be located in the <code>EvioReader</code>'s current - * file. - * - * @param reader the EVIO reader - * @return <code>true</code> if valid END event is located - * @throws Exception if there are IO problems using the reader - */ - static boolean isEndOkay(final EvioReader reader) throws Exception { - LOGGER.info("checking is END okay ..."); - - boolean endOkay = false; - - // Go to second to last event for searching. - reader.gotoEventNumber(reader.getEventCount() - 2); - - // Look for END event. - EvioEvent event = null; - while ((event = reader.parseNextEvent()) != null) { - if (event.getHeader().getTag() == EvioEventConstants.END_EVENT_TAG) { - endOkay = true; - break; - } - } - return endOkay; } /** @@ -238,7 +86,7 @@ * @param file the file * @return <code>true</code> if the file is on the MSS */ - static boolean isMssFile(final File file) { + public static boolean isMssFile(final File file) { return file.getPath().startsWith("/mss"); } @@ -250,7 +98,7 @@ * @throws IOException if there is an IO problem * @throws EvioException if there is an error reading the EVIO data */ - static EvioReader open(final File file) throws IOException, EvioException { + public static EvioReader open(final File file) throws IOException, EvioException { return open(file, false); } @@ -263,7 +111,7 @@ * @throws IOException if there is an IO problem * @throws EvioException if there is an error reading the EVIO data */ - static EvioReader open(final File file, final boolean sequential) throws IOException, EvioException { + public static EvioReader open(final File file, final boolean sequential) throws IOException, EvioException { File openFile = file; if (isMssFile(file)) { openFile = getCachedFile(file); @@ -284,12 +132,21 @@ * @throws IOException if there is an IO problem * @throws EvioException if there is an error reading the EVIO data */ - static EvioReader open(final String path) throws IOException, EvioException { + public static EvioReader open(final String path) throws IOException, EvioException { return open(new File(path), false); } /** - * Present class instantiation. + * Sort a list of EVIO files by their sequence numbers. + * + * @param evioFileList the list of files to sort + */ + public static void sortBySequence(final List<File> evioFileList) { + Collections.sort(evioFileList, new EvioFileSequenceComparator()); + } + + /** + * Prevent class instantiation. */ private EvioFileUtilities() { } Added: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoop.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoop.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoop.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,58 @@ +package org.hps.record.evio; + +import org.freehep.record.loop.DefaultRecordLoop; + +/** + * 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(); + + /** + * Create a new record loop. + */ + public EvioLoop() { + 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. + * + * @param evioFileSource the EVIO data source + */ + public void setEvioFileSource(final EvioFileSource evioFileSource) { + this.setRecordSource(evioFileSource); + } +} Added: java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/EvioLoopAdapter.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,96 @@ +package org.hps.record.evio; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.freehep.record.loop.AbstractLoopListener; +import org.freehep.record.loop.LoopEvent; +import org.freehep.record.loop.RecordEvent; +import org.freehep.record.loop.RecordListener; +import org.jlab.coda.jevio.EvioEvent; +import org.lcsim.util.log.DefaultLogFormatter; +import org.lcsim.util.log.LogUtil; + +/** + * A loop adapter for the {@link EvioLoop} which manages and activates a list of {@link EvioEventProcessor} objects. + * + * @author Jeremy McCormick, SLAC + */ +public final class EvioLoopAdapter extends AbstractLoopListener implements RecordListener { + + /** + * Setup class logger. + */ + private final Logger LOGGER = LogUtil.create(EvioLoopAdapter.class, new DefaultLogFormatter(), Level.ALL); + + /** + * 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("executing finish hook"); + 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("executing start hook"); + for (final EvioEventProcessor processor : processors) { + processor.startJob(); + } + } +} Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/Crawler.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/Crawler.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/Crawler.java Thu Aug 20 12:50:12 2015 @@ -7,6 +7,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Date; import java.util.EnumSet; import java.util.HashSet; @@ -20,6 +21,8 @@ import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.hps.conditions.database.ConnectionParameters; +import org.hps.record.run.RunSummary; +import org.hps.record.run.RunSummaryDaoImpl; import org.lcsim.util.log.DefaultLogFormatter; import org.lcsim.util.log.LogUtil; @@ -50,20 +53,20 @@ * Statically define the command options. */ static { - OPTIONS.addOption("a", "runs", true, "list of run numbers to accept (others will be excluded)"); + // TODO: add -f argument with file name to include; others would be excluded if they do not match OPTIONS.addOption("b", "min-date", true, "min date for a file (example \"2015-03-26 11:28:59\")"); OPTIONS.addOption("c", "cache", false, "automatically cache files from MSS to cache disk (JLAB only)"); OPTIONS.addOption("C", "connection-properties", true, "database connection properties file (required)"); OPTIONS.addOption("d", "directory", true, "root directory to start crawling (default is current dir)"); OPTIONS.addOption("E", "evio-processor", true, "class name of an EvioEventProcessor to execute"); OPTIONS.addOption("h", "help", false, "print help and exit (overrides all other arguments)"); - OPTIONS.addOption("m", "max-files", true, "max number of files to process per run (mostly for debugging)"); - OPTIONS.addOption("p", "print", true, "set event printing interval during EVIO processing"); - OPTIONS.addOption("r", "insert", false, "insert information into the run database (not done by default)"); + OPTIONS.addOption("i", "insert", false, "insert information into the run database (not done by default)"); + OPTIONS.addOption("L", "log-level", true, "set the log level (INFO, FINE, etc.)"); + OPTIONS.addOption("r", "run", true, "add a run number to accept (when used others will be excluded)"); OPTIONS.addOption("t", "timestamp-file", true, "existing or new timestamp file name"); OPTIONS.addOption("w", "max-cache-wait", true, "total time to allow for file caching (seconds)"); - OPTIONS.addOption("L", "log-level", true, "set the log level (INFO, FINE, etc.)"); - OPTIONS.addOption("u", "update", false, "allow overriding existing data in the run db (not allowed by default)"); + OPTIONS.addOption("u", "update", false, + "allow replacement of existing data in the run db (not allowed by default)"); OPTIONS.addOption("x", "max-depth", true, "max depth to crawl in the directory tree"); } @@ -179,9 +182,9 @@ } // List of one or more runs to accept in the job. - if (cl.hasOption("a")) { + if (cl.hasOption("r")) { final Set<Integer> acceptRuns = new HashSet<Integer>(); - for (final String runString : cl.getOptionValues("a")) { + for (final String runString : cl.getOptionValues("r")) { final Integer acceptRun = Integer.parseInt(runString); acceptRuns.add(acceptRun); LOGGER.config("added run filter " + acceptRun); @@ -189,7 +192,7 @@ config.setAcceptRuns(acceptRuns); } - // Enable run log updating (off by default). + // Enable updating of run database. if (cl.hasOption("r")) { config.setUpdateRunLog(true); LOGGER.config("inserting into run database is enabled"); @@ -208,21 +211,7 @@ LOGGER.config("max time for file caching set to " + config.waitTime()); } - // Max files to process per run; mostly just here for debugging purposes. - if (cl.hasOption("m")) { - final int maxFiles = Integer.parseInt(cl.getOptionValue("m")); - config.setMaxFiles(maxFiles); - LOGGER.config("max files set to " + maxFiles); - } - - // Event printing interval when doing EVIO event processing. - if (cl.hasOption("p")) { - final int eventPrintInterval = Integer.parseInt(cl.getOptionValue("p")); - config.setEventPrintInterval(eventPrintInterval); - LOGGER.config("event print interval set to " + eventPrintInterval); - } - - // Flag to allow replacement of existing records in the database; not allowed by default. + // Allow deletion and replacement of records in run database. if (cl.hasOption("u")) { config.setAllowUpdates(true); LOGGER.config("deletion and replacement of existing runs in the database is enabled"); @@ -267,7 +256,13 @@ throw new RuntimeException("Error parsing options.", e); } + // Configure the max wait time for file caching operations. + if (config.waitTime() != null && config.waitTime() > 0L) { + cacheManager.setWaitTime(config.waitTime()); + } + LOGGER.info("done parsing command line options"); + LOGGER.getHandlers()[0].flush(); return this; } @@ -293,31 +288,32 @@ LOGGER.info("running Crawler job"); // Create the file visitor for crawling the root directory with the given date filter. + LOGGER.info("creating file visitor"); + LOGGER.getHandlers()[0].flush(); final EvioFileVisitor visitor = new EvioFileVisitor(config.timestamp()); // Walk the file tree using the visitor. + LOGGER.info("walking the dir tree"); + LOGGER.getHandlers()[0].flush(); this.walk(visitor); // Get the list of run data created by the visitor. - final RunLog runs = visitor.getRunLog(); - - // Print the run numbers that were found. - runs.printRunNumbers(); - - // Sort the files on their sequence numbers. - runs.sortFiles(); + final RunSummaryMap runs = visitor.getRunMap(); // Process all the files, performing caching from the MSS if necessary. + LOGGER.info("processing all runs"); RunProcessor.processAllRuns(this.cacheManager, runs, config); - - // Print the summary information after the run processing is done. - runs.printRunSummaries(); + LOGGER.getHandlers()[0].flush(); // Execute the run database update. + LOGGER.info("updating run database"); this.updateRunDatabase(runs); + LOGGER.getHandlers()[0].flush(); // Update the timestamp output file. + LOGGER.info("updating the timestamp"); this.updateTimestamp(); + LOGGER.getHandlers()[0].flush(); LOGGER.info("Crawler job is done!"); } @@ -328,21 +324,26 @@ * @param runs the list of runs to update * @throws SQLException if there is a database query error */ - private void updateRunDatabase(final RunLog runs) throws SQLException { + private void updateRunDatabase(final RunSummaryMap runs) throws SQLException { // Insert the run information into the database. if (config.updateRunLog()) { + LOGGER.info("updating run database"); + // Open a DB connection. final Connection connection = config.connectionParameters().createConnection(); - // Create and configure RunLogUpdater which updates the run log for all runs found in the crawl job. - final RunLogUpdater runUpdater = new RunLogUpdater(connection, runs, config.allowUpdates()); - - // Update the DB. - runUpdater.insert(); + // Insert all run summaries into the database. + new RunSummaryDaoImpl(connection).insertFullRunSummaries(new ArrayList<RunSummary>(runs.getRunSummaries()), + config.allowUpdates()); // Close the DB connection. connection.close(); + + LOGGER.info("done updating run database"); + + } else { + LOGGER.info("run database will not be updated"); } } Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java Thu Aug 20 12:50:12 2015 @@ -13,7 +13,7 @@ import org.hps.record.evio.EvioEventProcessor; /** - * Full configuration information for the {@link Crawler class}. + * Full configuration information for the {@link Crawler} class. * <p> * Method chaining of setters is supported. * @@ -38,19 +38,9 @@ private boolean allowUpdates = false; /** - * The database connection parameters which must be provided by command line argument. + * The database connection parameters which must be provided by a command line argument. */ private ConnectionParameters connectionParameters; - - /** - * Default event print interval. - */ - private final int DEFAULT_EVENT_PRINT_INTERVAL = 1000; - - /** - * Interval for printing out event number while running EVIO processors. - */ - private int eventPrintInterval = DEFAULT_EVENT_PRINT_INTERVAL; /** * The maximum depth to crawl. @@ -151,15 +141,6 @@ } /** - * Get the event print interval. - * - * @return the event print interval - */ - int eventPrintInterval() { - return this.eventPrintInterval; - } - - /** * Get the max depth in the directory tree to crawl. * * @return the max depth @@ -231,17 +212,6 @@ } /** - * Set the interval for printing the EVIO event numbers during processing. - * - * @param eventPrintInterval the event print interval - * @return this object - */ - CrawlerConfig setEventPrintInterval(final int eventPrintInterval) { - this.eventPrintInterval = eventPrintInterval; - return this; - } - - /** * Set the max depth. * * @param maxDepth the max depth Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileVisitor.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileVisitor.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileVisitor.java Thu Aug 20 12:50:12 2015 @@ -12,11 +12,13 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.hps.record.evio.EvioFileFilter; +import org.hps.record.evio.EvioFileUtilities; import org.lcsim.util.log.DefaultLogFormatter; import org.lcsim.util.log.LogUtil; /** - * A file visitor that crawls directories for EVIO files and returns the information as a {@link RunLog}. + * A file visitor that crawls directories for EVIO files and returns the information as a {@link RunSummaryMap}. * * @author Jeremy McCormick, SLAC */ @@ -35,8 +37,8 @@ /** * The run log containing information about files from each run. */ - private final RunLog runs = new RunLog(); - + private final RunSummaryMap runs = new RunSummaryMap(); + /** * Create a new file visitor. * @@ -83,7 +85,7 @@ * * @return the run log */ - RunLog getRunLog() { + RunSummaryMap getRunMap() { return this.runs; } @@ -99,10 +101,10 @@ if (this.accept(file)) { // Get the run number from the file name. - final Integer run = EvioFileUtilities.getRun(file); + final Integer run = EvioFileUtilities.getRunFromName(file); // Get the sequence number from the file name. - final Integer seq = EvioFileUtilities.getSequence(file); + final Integer seq = EvioFileUtilities.getSequenceFromName(file); LOGGER.info("accepted file " + file.getPath() + " with run " + run + " and seq " + seq); Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/JCacheManager.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/JCacheManager.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/JCacheManager.java Thu Aug 20 12:50:12 2015 @@ -13,6 +13,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.hps.record.evio.EvioFileUtilities; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunFilter.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunFilter.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunFilter.java Thu Aug 20 12:50:12 2015 @@ -3,6 +3,8 @@ import java.io.File; import java.io.FileFilter; import java.util.Set; + +import org.hps.record.evio.EvioFileUtilities; /** * A filter which rejects files with run numbers not in a specified set. @@ -36,6 +38,6 @@ */ @Override public boolean accept(final File file) { - return this.acceptRuns.contains(EvioFileUtilities.getRun(file)); + return this.acceptRuns.contains(EvioFileUtilities.getRunFromName(file)); } } Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java Thu Aug 20 12:50:12 2015 @@ -1,19 +1,16 @@ package org.hps.record.evio.crawler; import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import org.hps.record.evio.EvioEventConstants; -import org.hps.record.evio.EvioEventProcessor; +import org.hps.record.epics.EpicsRunProcessor; +import org.hps.record.evio.EvioFileMetaData; +import org.hps.record.evio.EvioFileMetaDataReader; +import org.hps.record.evio.EvioFileSource; +import org.hps.record.evio.EvioLoop; import org.hps.record.run.RunSummary; import org.hps.record.scalers.ScalersEvioProcessor; -import org.jlab.coda.jevio.EvioEvent; -import org.jlab.coda.jevio.EvioException; -import org.jlab.coda.jevio.EvioReader; import org.lcsim.util.log.DefaultLogFormatter; import org.lcsim.util.log.LogUtil; @@ -40,35 +37,24 @@ * @param runs the run log containing the list of run summaries * @throws Exception if there is an error processing one of the runs */ - static void processAllRuns(final JCacheManager cacheManager, final RunLog runs, final CrawlerConfig config) + static void processAllRuns(final JCacheManager cacheManager, final RunSummaryMap runs, final CrawlerConfig config) throws Exception { - // Configure the max wait time for file caching operations. - if (config.waitTime() != null && config.waitTime() > 0L) { - cacheManager.setWaitTime(config.waitTime()); - } - // Process all of the runs that were found. - for (final int run : runs.getSortedRunNumbers()) { - - // Get the run summary. - final RunSummary runSummary = runs.getRunSummary(run); + for (final RunSummary runSummary : runs.getRunSummaries()) { // Clear the cache manager. if (config.useFileCache()) { + LOGGER.info("clearing file cache"); cacheManager.clear(); } // Create a processor to process all the EVIO events in the run. + LOGGER.info("creating run processor for " + runSummary.getRun()); final RunProcessor runProcessor = new RunProcessor(cacheManager, runSummary, config); - // Add extra processors. - for (final EvioEventProcessor processor : config.processors()) { - runProcessor.addProcessor(processor); - LOGGER.config("added extra EVIO processor " + processor.getClass().getName()); - } - // Process all of the run's files. + LOGGER.info("processing run " + runSummary.getRun()); runProcessor.processRun(); } } @@ -81,27 +67,17 @@ /** * Processor for extracting EPICS information. */ - private final EpicsLog epicsLog; - - /** - * Processor for extracting event type counts (sync, physics, trigger types, etc.). - */ - private final EventCountProcessor eventCountProcessor; - - /** - * The event printing interval when processing EVIO files. - */ - private int eventPrintInterval = 1000; - - /** - * Max total files to read (default is unlimited). - */ - private int maxFiles = -1; - - /** - * The list of EVIO processors to run on the files that are found. - */ - private final List<EvioEventProcessor> processors = new ArrayList<EvioEventProcessor>(); + private final EpicsRunProcessor epicsLog; + + /** + * The data source with the list of EVIO files to process. + */ + private final EvioFileSource evioFileSource; + + /** + * The EVIO event processing loop. + */ + private final EvioLoop evioLoop = new EvioLoop(); /** * The run summary information updated by running this processor. @@ -119,9 +95,9 @@ private boolean useFileCache; /** - * Create the processor for a single run. - * - * @param runSummary the run summary for the run + * Create a run processor. + * + * @param runSummary the run summary object for the run * @return the run processor */ RunProcessor(final JCacheManager cacheManager, final RunSummary runSummary, final CrawlerConfig config) { @@ -129,39 +105,22 @@ this.runSummary = runSummary; this.cacheManager = cacheManager; - // EPICS processor. - epicsLog = new EpicsLog(); - this.addProcessor(epicsLog); - - // Scaler data processor. + // Setup record loop. + runSummary.sortFiles(); + evioFileSource = new EvioFileSource(runSummary.getEvioFileList()); + evioLoop.setEvioFileSource(evioFileSource); + + // Add EPICS processor. + epicsLog = new EpicsRunProcessor(); + evioLoop.addEvioEventProcessor(epicsLog); + + // Add Scaler data processor. scalersProcessor = new ScalersEvioProcessor(); scalersProcessor.setResetEveryEvent(false); - this.addProcessor(scalersProcessor); - - // Event log processor. - eventCountProcessor = new EventCountProcessor(); - this.addProcessor(eventCountProcessor); - - // Max files. - if (config.maxFiles() != -1) { - this.setMaxFiles(config.maxFiles()); - } - - // Enable file caching. + evioLoop.addEvioEventProcessor(scalersProcessor); + + // Set whether file caching from MSS is enabled. this.useFileCache(config.useFileCache()); - - // Set event printing interval. - this.setEventPrintInterval(config.eventPrintInterval()); - } - - /** - * Add a processor of EVIO events. - * - * @param processor the EVIO event processor - */ - void addProcessor(final EvioEventProcessor processor) { - this.processors.add(processor); - LOGGER.config("added processor " + processor.getClass().getSimpleName()); } /** @@ -172,10 +131,10 @@ */ private void cacheFiles() { - LOGGER.info("caching files from run " + this.runSummary.getRun() + " ..."); + LOGGER.info("caching files from run " + this.runSummary.getRun()); // Cache all the files and wait for the operation to complete (it will take awhile!). - this.cacheManager.cache(this.getFiles()); + this.cacheManager.cache(this.runSummary.getEvioFileList()); final boolean cached = this.cacheManager.waitForCache(); // If the files weren't cached then die. @@ -187,245 +146,82 @@ } /** - * Find the end date in the EVIO events. - * - * @param evioReader the open <code>EvioReader</code> - */ - private void findEndDate(final EvioReader evioReader) { - - // Try to get end date from END event. - Long endTimestamp = EvioFileUtilities.getTimestamp(evioReader, EvioEventConstants.END_EVENT_TAG, -5); - - if (endTimestamp != null) { - // Flag end okay for the run. - this.runSummary.setEndOkay(true); - } else { - // Try to find the end date from the last physics event. - endTimestamp = EvioFileUtilities.getEndTimestamp(evioReader); - this.runSummary.setEndOkay(false); - } - - if (endTimestamp == null) { - // Not finding the end date is a fatal error. - throw new RuntimeException("Failed to find end date."); - } - - LOGGER.info("found end timestamp " + endTimestamp); - this.runSummary.setEndTimeUtc(endTimestamp); - } - - /** - * Find the start date in the EVIO events. - * - * @param evioReader the open <code>EvioReader</code> - */ - private void findStartDate(final EvioReader evioReader) { - - // First try to find the start date in the PRESTART event. - Long startTimestamp = EvioFileUtilities.getTimestamp(evioReader, EvioEventConstants.PRESTART_EVENT_TAG, 0); - - if (startTimestamp == null) { - // Search for start date in first physics event. - startTimestamp = EvioFileUtilities.getStartTimestamp(evioReader); - } - - if (startTimestamp == null) { - // Not finding the start date is a fatal error. - throw new RuntimeException("Failed to find start date."); - } - - LOGGER.fine("got run start " + startTimestamp); - this.runSummary.setStartTimeUtc(startTimestamp); - } - - /** - * Get the list of files to process, which will be limited by the {@link #maxFiles} value if it is set. - * - * @return the files to process - */ - private List<File> getFiles() { - // Get the list of files to process, taking into account the max files setting. - List<File> files = this.runSummary.getEvioFileList(); - if (this.maxFiles != -1) { - int toIndex = this.maxFiles; - if (toIndex > files.size()) { - toIndex = files.size(); - } - files = files.subList(0, toIndex); - } - return files; - } - - /** - * Get the list of EVIO processors. - * - * @return the list of EVIO processors - */ - List<EvioEventProcessor> getProcessors() { - return this.processors; - } - - /** - * Return <code>true</code> if the file is the first one in the list for the run. - * - * @param file the EVIO <code>File</code> - * @return <code>true</code> if the file is the first one in the list for the run - */ - private boolean isFirstFile(final File file) { - return file.equals(this.runSummary.getEvioFileList().first()); - } - - /** - * Return <code>true</code> if the file is the last one in the list for the run. - * - * @param file the EVIO <code>File</code> - * @return <code>true</code> if the file is the last one in the list for the run - */ - private boolean isLastFile(final File file) { - return file.equals(this.getFiles().get(this.getFiles().size() - 1)); - } - - /** - * Process events using the list of EVIO processors. - * - * @param evioReader the open <code>EvioReader</code> - * @throws IOException if there is a file IO error - * @throws EvioException if there is an EVIO error - * @throws Exception if there is some other error - */ - private void processEvents(final EvioReader evioReader) throws IOException, EvioException, Exception { - LOGGER.finer("running EVIO processors ..."); - evioReader.gotoEventNumber(0); - int nProcessed = 0; - if (!this.processors.isEmpty()) { - EvioEvent event = null; - while ((event = evioReader.parseNextEvent()) != null) { - for (final EvioEventProcessor processor : this.processors) { - processor.process(event); - } - ++nProcessed; - if (nProcessed % this.eventPrintInterval == 0) { - LOGGER.finer("processed " + nProcessed + " EVIO events"); - } - } - LOGGER.info("done running EVIO processors"); - } - } - - /** - * Process a single EVIO file from the run. - * - * @param file the EVIO file - * @throws EvioException if there is an EVIO error - * @throws IOException if there is some kind of IO error - * @throws Exception if there is a generic error thrown by event processing - */ - private void processFile(final File file) throws EvioException, IOException, Exception { - - LOGGER.fine("processing file " + file.getPath() + " ..."); - - EvioReader evioReader = null; - try { - - // Open file for reading (flag should be true for sequential or false for mem map). - evioReader = EvioFileUtilities.open(file, true); - - // If this is the first file then get the start date. - if (this.isFirstFile(file)) { - LOGGER.fine("getting run start from first file " + file.getPath() + " ..."); - this.findStartDate(evioReader); - } - - // Go back to the first event and process the events using the list of EVIO processors. - this.processEvents(evioReader); - - // Find end date from last file in the run. - if (this.isLastFile(file)) { - LOGGER.fine("getting run end from last file " + file.getPath() + " ..."); - this.findEndDate(evioReader); - } - - } finally { - // Close the EvioReader for the current file. - if (evioReader != null) { - evioReader.close(); - } - } - LOGGER.fine("done processing " + file.getPath()); - } - - /** - * Process the run by executing the registered {@link org.hps.record.evio.EvioEventProcessor}s extracting the start - * and end dates. + * Process the run by executing the registered {@link org.hps.record.evio.EvioEventProcessor}s and extracting the + * start and end dates. * <p> - * This method will also activate file caching, if enabled by the {@link #useFileCache} option. + * This method will also execute file caching from MSS, if enabled by the {@link #useFileCache} option. * * @throws Exception if there is an error processing a file */ void processRun() throws Exception { - LOGGER.info("processing run " + this.runSummary.getRun() + " ..."); - - // First cache all the files we will process, if necessary. + LOGGER.info("processing " + this.runSummary.getEvioFileList().size() + " files from run " + + this.runSummary.getRun()); + + // Cache files from MSS if this is enabled. if (this.useFileCache) { + LOGGER.info("caching files from MSS"); this.cacheFiles(); } - // Run the start of job hooks. - for (final EvioEventProcessor processor : this.processors) { - processor.startJob(); - } - - // Get the list of files, limited by max files setting. - final List<File> files = this.getFiles(); - - LOGGER.info("processing " + files.size() + " from run " + this.runSummary.getRun()); - - // Process all the files. - for (final File file : files) { - this.processFile(file); - } - - // Run the end job hooks. - LOGGER.info("running end of job hooks on EVIO processors ..."); - for (final EvioEventProcessor processor : this.processors) { - processor.endJob(); - } - + // Run processors over all files. + LOGGER.info("looping over all events"); + evioLoop.loop(-1); + + // Get run start date. + LOGGER.info("setting run start date"); + this.setRunStartDate(); + + // Get run end date. + LOGGER.info("setting run end date"); + this.setRunEndDate(); + + // Update run summary from processors. + LOGGER.info("updating run summary"); + this.updateRunSummary(); + + LOGGER.info("done processing run " + this.runSummary.getRun()); + } + + /** + * Set the run end date by getting meta data from the last file and copying it to the run summary. + */ + private void setRunEndDate() { + final File lastEvioFile = runSummary.getEvioFileList().get(runSummary.getEvioFileList().size() - 1); + LOGGER.info("getting meta data for " + lastEvioFile.getPath()); + final EvioFileMetaDataReader metaDataReader = new EvioFileMetaDataReader(); + final EvioFileMetaData metaData = metaDataReader.getMetaData(lastEvioFile); + LOGGER.info(metaData.toString()); + LOGGER.info("setting unix end time to " + metaData.getEndDate().getTime() + " from meta data"); + runSummary.setEndTimeUtc(metaData.getEndDate().getTime()); + runSummary.setEndOkay(metaData.hasEnd()); + } + + /** + * Set the run start date by getting meta data from the first file and copying it to the run summary. + */ + private void setRunStartDate() { + final File firstEvioFile = runSummary.getEvioFileList().get(0); + LOGGER.info("getting meta data for " + firstEvioFile.getPath()); + final EvioFileMetaDataReader metaDataReader = new EvioFileMetaDataReader(); + final EvioFileMetaData metaData = metaDataReader.getMetaData(firstEvioFile); + LOGGER.info(metaData.toString()); + LOGGER.info("setting unix start time to " + metaData.getStartDate().getTime() + " from meta data"); + runSummary.setStartTimeUtc(metaData.getStartDate().getTime()); + } + + /** + * Update the current run summary by copying data to it from the EVIO processors. + */ + private void updateRunSummary() { // Put scaler data from EVIO processor into run summary. runSummary.setScalerData(this.scalersProcessor.getScalerData()); - // Set the counts of event types on the run summary. - runSummary.setEventTypeCounts(eventCountProcessor.getEventCounts()); - // Set total number of events on the run summary from the event counter. - runSummary.setTotalEvents(this.eventCountProcessor.getTotalEventCount()); + runSummary.setTotalEvents((int) evioLoop.getTotalCountableConsumed()); // Set EpicsData for the run. runSummary.setEpicsData(this.epicsLog.getEpicsData()); - - LOGGER.info("done processing run " + this.runSummary.getRun()); - } - - /** - * Set the event print interval when running the EVIO processors. - * - * @param eventPrintInterval the event print interval when running the EVIO processors - */ - void setEventPrintInterval(final int eventPrintInterval) { - this.eventPrintInterval = eventPrintInterval; - } - - /** - * Set the maximum number of files to process. - * <p> - * This is intended primarily for debugging. - * - * @param maxFiles the maximum number of files to process - */ - void setMaxFiles(final int maxFiles) { - this.maxFiles = maxFiles; - LOGGER.config("max files set to " + maxFiles); } /** Copied: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryMap.java (from r3364, java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java) ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryMap.java Thu Aug 20 12:50:12 2015 @@ -1,32 +1,33 @@ package org.hps.record.evio.crawler; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.HashMap; import java.util.logging.Logger; import org.hps.record.run.RunSummary; import org.lcsim.util.log.LogUtil; /** - * This class contains information about a series of runs which each have a {@link RunSummary} object. + * This class maps run numbers to {@link RunSummary} objects. * * @author Jeremy McCormick, SLAC */ -final class RunLog { +@SuppressWarnings("serial") +final class RunSummaryMap extends HashMap<Integer, RunSummary> { /** * Setup logging. */ - private static final Logger LOGGER = LogUtil.create(RunLog.class); + private static final Logger LOGGER = LogUtil.create(RunSummaryMap.class); /** - * A map between run numbers and the run summary information. + * Get the collection of {@link RunSummary} objects. + * + * @return the collection of {@link RunSummary} objects */ - private final Map<Integer, RunSummary> runs = new LinkedHashMap<Integer, RunSummary>(); + public Collection<RunSummary> getRunSummaries() { + return this.values(); + } /** * Get a {@link RunSummary} by its run number. @@ -37,63 +38,10 @@ * @return the <code>RunSummary</code> for the run number */ public RunSummary getRunSummary(final int run) { - if (!this.runs.containsKey(run)) { + if (!this.containsKey(run)) { LOGGER.info("creating new RunSummary for run " + run); - this.runs.put(run, new RunSummary(run)); + this.put(run, new RunSummary(run)); } - return this.runs.get(run); + return this.get(run); } - - /** - * Get the collection of {@link RunSummary} objects. - * - * @return the collection of {@link RunSummary} objects - */ - public Collection<RunSummary> getRunSummaries() { - return this.runs.values(); - } - - /** - * Get a list of sorted run numbers from this run log. - * <p> - * This is a copy of the keys from the map, so modifying it will have no effect on the original. - * - * @return the list of sorted run numbers - */ - List<Integer> getSortedRunNumbers() { - final List<Integer> runList = new ArrayList<Integer>(this.runs.keySet()); - Collections.sort(runList); - return runList; - } - - /** - * Print out each {@link RunSummary} to <code>System.out</code>. - */ - void printRunSummaries() { - for (final int run : this.runs.keySet()) { - this.runs.get(run).printOut(System.out); - } - } - - /** - * Sort the file list for each run in place by EVIO sequence numbers. - */ - void sortFiles() { - for (final Integer run : this.runs.keySet()) { - this.runs.get(run).sortFiles(); - } - } - - /** - * Print the run numbers to the log. - */ - void printRunNumbers() { - // Print the list of runs that were found. - final StringBuffer sb = new StringBuffer(); - for (final Integer run : getSortedRunNumbers()) { - sb.append(run + " "); - } - LOGGER.info("found EVIO files from runs: " + sb.toString()); - } - } Added: java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDao.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDao.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDao.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,65 @@ +package org.hps.record.run; + +import java.util.List; + +import org.hps.record.epics.EpicsData; + +/** + * Database Access Object (DAO) API for EPICS data from the run database. + * + * @author Jeremy McCormick, SLAC + */ +public interface EpicsDataDao { + + /** + * Delete EPICS data from the database. + * + * @param epicsData the EPICS data to delete + */ + void deleteEpicsData(EpicsData epicsData); + + /** + * Delete all EPICS data for a run from the database. + * + * @param run the run number + */ + void deleteEpicsData(int run); + + /** + * Get all the EPICS data in the database. + * + * @return the list of EPICS data + */ + List<EpicsData> getAllEpicsData(); + + /** + * Get EPICS data by run. + * + * @param run the run number + * @return the EPICS data + */ + List<EpicsData> getEpicsData(int run); + + /** + * Get the list of unique variables names used in the database records. + * + * @return the list of unique variable names + */ + List<String> getVariableNames(); + + /** + * Insert a list of EPICS data into the database. + * <p> + * The run number comes from the header information. + * + * @param epicsDataList the list of EPICS data + */ + void insertEpicsData(List<EpicsData> epicsDataList); + + /** + * Updates EPICS data in the database. + * + * @param epicsData the EPICS data to update + */ + void updateEpicsData(EpicsData epicsData); +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDaoImpl.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDaoImpl.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/EpicsDataDaoImpl.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,362 @@ +package org.hps.record.run; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.hps.record.epics.EpicsData; +import org.hps.record.epics.EpicsHeader; + +/** + * Implementation of database operations for EPICS data. + * + * @author Jeremy McCormick, SLAC + */ +public class EpicsDataDaoImpl implements EpicsDataDao { + + /** + * SQL data query strings. + */ + private static class EpicsDataQuery { + + /** + * Delete by run number. + */ + private static final String DELETE_BY_RUN = "DELETE FROM run_epics WHERE run = ?"; + /** + * Delete by run and sequence number. + */ + private static final String DELETE_RUN_AND_SEQUENCE = "DELETE FROM run_epics WHERE run = ? and sequence = ?"; + /** + * Insert a record. + */ + private static final String INSERT = "INSERT INTO run_epics (run, sequence, timestamp, variable_name, value) VALUES (?, ?, ?, ?, ?)"; + /** + * Select all records. + */ + private static final String SELECT_ALL = "SELECT * FROM run_epics ORDER BY run, sequence"; + /** + * Select by run number. + */ + private static final String SELECT_RUN = "SELECT * FROM run_epics WHERE run = ? ORDER BY `sequence`"; + /** + * Select unique variable names. + */ + private static final String SELECT_VARIABLE_NAMES = "SELECT DISTINCT(variable_name) FROM run_epics ORDER BY variable_name"; + /** + * Update a record. + */ + private static final String UPDATE = "UPDATE run_epics SET run = ?, sequence = ?, timestamp = ?, variable_name = ?, value = ? WHERE run = ? and sequence = ? and variable_name = ?"; + } + + /** + * The database connection. + */ + private final Connection connection; + + /** + * Create a new DAO implementation for EPICS data. + * + * @param connection the database connection + */ + public EpicsDataDaoImpl(final Connection connection) { + if (connection == null) { + throw new IllegalArgumentException("The connection is null."); + } + this.connection = connection; + } + + /** + * Delete the record for this EPICS data object using its run and sequence number. + * + * @param run the run number + * @throws IllegalArgumentException if the EPICS data is missing a header object + */ + @Override + public void deleteEpicsData(final EpicsData epicsData) { + PreparedStatement preparedStatement = null; + try { + final EpicsHeader epicsHeader = epicsData.getEpicsHeader(); + if (epicsHeader == null) { + throw new IllegalArgumentException("The EPICS data is missing header information."); + } + preparedStatement = connection.prepareStatement(EpicsDataQuery.DELETE_RUN_AND_SEQUENCE); + preparedStatement.setInt(1, epicsHeader.getRun()); + preparedStatement.setInt(2, epicsHeader.getSequence()); + preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Delete all EPICS data for a run from the database. + * + * @param run the run number + */ + @Override + public void deleteEpicsData(final int run) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(EpicsDataQuery.DELETE_BY_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(); + } + } + } + } + + /** + * Get all the EPICS data in the database. + * + * @return the list of EPICS data + */ + @Override + public List<EpicsData> getAllEpicsData() { + PreparedStatement preparedStatement = null; + final List<EpicsData> epicsDataList = new ArrayList<EpicsData>(); + try { + preparedStatement = connection.prepareStatement(EpicsDataQuery.SELECT_ALL); + final ResultSet resultSet = preparedStatement.executeQuery(); + Integer currentRun = null; + Integer currentSequence = null; + EpicsData epicsData = new EpicsData(); + while (resultSet.next()) { + if (currentRun == null) { + currentRun = resultSet.getInt("run"); + } + if (currentSequence == null) { + currentSequence = resultSet.getInt("sequence"); + } + final int run = resultSet.getInt("run"); + final int sequence = resultSet.getInt("sequence"); + final int timestamp = resultSet.getInt("timestamp"); + final String variableName = resultSet.getString("variable_name"); + final double value = resultSet.getDouble("value"); + if (currentRun != run || currentSequence != sequence) { + epicsDataList.add(epicsData); + epicsData = new EpicsData(); + final EpicsHeader epicsHeader = new EpicsHeader(new int[] {run, sequence, timestamp}); + epicsData.setEpicsHeader(epicsHeader); + } + epicsData.setValue(variableName, value); + } + epicsDataList.add(epicsData); + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + return epicsDataList; + } + + /** + * Get EPICS data by run. + * + * @param run the run number + * @return the EPICS data + */ + @Override + public List<EpicsData> getEpicsData(final int run) { + PreparedStatement preparedStatement = null; + final List<EpicsData> epicsDataList = new ArrayList<EpicsData>(); + try { + preparedStatement = connection.prepareStatement(EpicsDataQuery.SELECT_RUN); + preparedStatement.setInt(1, run); + final ResultSet resultSet = preparedStatement.executeQuery(); + Integer currentSequence = null; + EpicsData epicsData = new EpicsData(); + EpicsHeader epicsHeader = null; + while (resultSet.next()) { + + // Get record data. + final int sequence = resultSet.getInt("sequence"); + final int timestamp = resultSet.getInt("timestamp"); + final String variableName = resultSet.getString("variable_name"); + final double value = resultSet.getDouble("value"); + + // Get sequence first time. + if (currentSequence == null) { + currentSequence = resultSet.getInt("sequence"); + } + + // Create EPICS header. + epicsHeader = new EpicsHeader(new int[] {run, sequence, timestamp}); + + // First time need to set header here. + if (epicsData.getEpicsHeader() == null) { + epicsData.setEpicsHeader(epicsHeader); + } + + // New sequence number occurred. + if (currentSequence != sequence) { + + // Add the EPICS data to the list. + epicsDataList.add(epicsData); + + // Use the new sequence number. + currentSequence = sequence; + + // Create new EPICS data. + epicsData = new EpicsData(); + + // Set header from current record. + epicsData.setEpicsHeader(epicsHeader); + } + + // Set the value of the variable from the current record. + epicsData.setValue(variableName, value); + } + + // Add the last object which will not happen inside the loop. + epicsDataList.add(epicsData); + + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + return epicsDataList; + } + + /** + * Get the list of unique variables names used in the database records. + * + * @return the list of unique variable names + */ + @Override + public List<String> getVariableNames() { + final List<String> variableNames = new ArrayList<String>(); + Statement statement = null; + try { + statement = connection.createStatement(); + final ResultSet resultSet = statement.executeQuery(EpicsDataQuery.SELECT_VARIABLE_NAMES); + while (resultSet.next()) { + variableNames.add(resultSet.getString(1)); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + return variableNames; + } + + /** + * Insert a list of EPICS data into the database. + * <p> + * The run number comes from the header information. + * + * @param epicsDataList the list of EPICS data + */ + @Override + public void insertEpicsData(final List<EpicsData> epicsDataList) { + if (epicsDataList.isEmpty()) { + throw new IllegalStateException("The EPICS data list is empty."); + } + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(EpicsDataQuery.INSERT); + for (final EpicsData epicsData : epicsDataList) { + final EpicsHeader epicsHeader = epicsData.getEpicsHeader(); + if (epicsHeader == null) { + throw new IllegalArgumentException("The EPICS data is missing header information."); + } + for (final String variableName : epicsData.getKeys()) { + preparedStatement.setInt(1, epicsData.getEpicsHeader().getRun()); + preparedStatement.setInt(2, epicsData.getEpicsHeader().getSequence()); + preparedStatement.setInt(3, epicsData.getEpicsHeader().getTimestamp()); + preparedStatement.setString(4, variableName); + preparedStatement.setDouble(5, epicsData.getValue(variableName)); + preparedStatement.executeUpdate(); + } + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Updates EPICS data in the database. + * + * @param epicsData the EPICS data to update + */ + @Override + public void updateEpicsData(final EpicsData epicsData) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(EpicsDataQuery.UPDATE); + final int run = epicsData.getEpicsHeader().getRun(); + final int sequence = epicsData.getEpicsHeader().getSequence(); + final int timestamp = epicsData.getEpicsHeader().getTimestamp(); + for (final String variableName : epicsData.getKeys()) { + preparedStatement.setInt(1, run); + preparedStatement.setInt(2, sequence); + preparedStatement.setInt(3, timestamp); + preparedStatement.setString(4, variableName); + preparedStatement.setDouble(5, epicsData.getValue(variableName)); + preparedStatement.setInt(6, run); + preparedStatement.setInt(7, sequence); + preparedStatement.setString(8, variableName); + preparedStatement.executeUpdate(); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + try { + if (preparedStatement != null) { + preparedStatement.close(); + } + } catch (final SQLException e) { + e.printStackTrace(); + } + try { + connection.setAutoCommit(true); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDao.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDao.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDao.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,43 @@ +package org.hps.record.run; + +import java.io.File; +import java.util.List; + +/** + * Database Access Object (DAO) interface to EVIO files in the run database. + * + * @author Jeremy McCormick, SLAC + */ +public interface EvioFilesDao { + + /** + * Delete the EVIO file records for a run. + * + * @param run the run number + */ + void deleteEvioFiles(int run); + + /** + * Get all EVIO files from the database. + * + * @return all EVIO files from the database + */ + List<File> getAllEvioFiles(); + + /** + * Get a list of EVIO files by run number. + * + * @param run the run number + * @return the list of EVIO files for the run + */ + List<File> getEvioFiles(int run); + + /** + * Insert the list of files for a run. + * + * @param fileList the list of files + * @param run the run number + */ + void insertEvioFiles(List<File> fileList, int run); + +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDaoImpl.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDaoImpl.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/EvioFilesDaoImpl.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,168 @@ +package org.hps.record.run; + +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of database operations for EVIO files in the run database. + * + * @author Jeremy McCormick, SLAC + */ +public class EvioFilesDaoImpl implements EvioFilesDao { + + /** + * SQL query strings. + */ + private static final class EvioFilesQuery { + + /** + * Delete files by run number. + */ + private static final String DELETE_RUN = "DELETE FROM run_files where run = ?"; + /** + * Insert files by run number. + */ + private static final String INSERT_RUN = "INSERT INTO run_files (run, directory, name) VALUES(?, ?, ?)"; + /** + * Select all records. + */ + private static final String SELECT_ALL = "SELECT directory, name FROM run_files"; + /** + * Select records by run number. + */ + private static final String SELECT_RUN = "SELECT directory, name FROM run_files WHERE run = ?"; + } + + /** + * The database connection. + */ + private final Connection connection; + + public EvioFilesDaoImpl(final Connection connection) { + this.connection = connection; + } + + /** + * Delete the EVIO file records for a run. + * + * @param run the run number + */ + @Override + public void deleteEvioFiles(final int run) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(EvioFilesQuery.DELETE_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(); + } + } + } + + } + + /** + * Get all EVIO files from the database. + * + * @return all EVIO files from the database + */ + @Override + public List<File> getAllEvioFiles() { + Statement statement = null; + final List<File> evioFileList = new ArrayList<File>(); + try { + statement = this.connection.createStatement(); + final ResultSet resultSet = statement.executeQuery(EvioFilesQuery.SELECT_ALL); + while (resultSet.next()) { + evioFileList.add(new File(resultSet.getString("directory") + File.separator + + resultSet.getString("name"))); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + return evioFileList; + } + + /** + * Get a list of EVIO files by run number. + * + * @param run the run number + * @return the list of EVIO files for the run + */ + @Override + public List<File> getEvioFiles(final int run) { + PreparedStatement preparedStatement = null; + final List<File> evioFileList = new ArrayList<File>(); + try { + preparedStatement = this.connection.prepareStatement(EvioFilesQuery.SELECT_RUN); + preparedStatement.setInt(1, run); + final ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + evioFileList.add(new File(resultSet.getString("directory") + File.separator + + resultSet.getString("name"))); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + return evioFileList; + } + + /** + * Insert the list of files for a run. + * + * @param fileList the list of files + * @param run the run number + */ + @Override + public void insertEvioFiles(final List<File> fileList, final int run) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(EvioFilesQuery.INSERT_RUN); + for (final File file : fileList) { + preparedStatement.setInt(1, run); + preparedStatement.setString(2, file.getParentFile().getPath()); + preparedStatement.setString(3, file.getName()); + 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/trunk/record-util/src/main/java/org/hps/record/run/RunManager.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/RunManager.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/run/RunManager.java Thu Aug 20 12:50:12 2015 @@ -1,9 +1,8 @@ package org.hps.record.run; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -14,19 +13,14 @@ import org.lcsim.util.log.LogUtil; /** - * Manages read-only access to the run database and creates a {@link RunSummary} object from the data for a specific - * run. - * <p> - * This class converts database records into {@link RunSummary}, {@link org.hps.record.epics.EpicsData}, - * {@link org.hps.record.scalers.ScalerData}, and {@link org.hps.record.evio.crawler.EvioFileList} objects using their - * corresponding {@link AbstractRunDatabaseReader} implementation classes. + * Manages read-only access to the run database and creates a {@link RunSummary} for a specific run. * * @author Jeremy McCormick, SLAC */ public final class RunManager implements ConditionsListener { /** - * The default connection parameters for read-only access to the run database using the standard 'hpsuser' account. + * The default connection parameters for read-only access to the run database. */ private static ConnectionParameters DEFAULT_CONNECTION_PARAMETERS = new ConnectionParameters("hpsuser", "darkphoton", "hps_run_db", "hpsdb.jlab.org"); @@ -61,7 +55,7 @@ /** * The database connection parameters, initially set to the default parameters. */ - private ConnectionParameters connectionParameters = DEFAULT_CONNECTION_PARAMETERS; + private final ConnectionParameters connectionParameters = DEFAULT_CONNECTION_PARAMETERS; /** * The run number; the -1 value indicates that this has not been set externally yet. @@ -73,11 +67,18 @@ */ private RunSummary runSummary = null; - void closeConnection() { - try { - this.connection.close(); - } catch (final SQLException e) { - e.printStackTrace(); + /** + * Close the database connection. + */ + private void closeConnection() { + if (!(this.connection == null)) { + try { + if (!this.connection.isClosed()) { + this.connection.close(); + } + } catch (final SQLException e) { + e.printStackTrace(); + } } } @@ -90,21 +91,21 @@ } /** - * Get the database connection. - * - * @return the database connection or <code>null</code> if it is not set - */ - Connection getConnection() { - return this.connection; - } - - /** - * Get the run number. + * Get the current run number. * * @return the run number */ public int getRun() { return run; + } + + /** + * Get the complete list of run numbers from the database. + * + * @return the complete list of run numbers + */ + List<Integer> getRuns() { + return new RunSummaryDaoImpl(this.connection).getRuns(); } /** @@ -117,122 +118,70 @@ } /** - * Read information from the run database and create a {@link RunSummary} from it. + * Open a new database connection from the connection parameters if the current one is closed or <code>null</code>. + * <p> + * This method does nothing if the connection is already open. */ - private void readRun() { - - // Read main RunSummary object but not objects that it references. - final RunSummaryReader runSummaryReader = new RunSummaryReader(); - runSummaryReader.setRun(this.getRun()); - runSummaryReader.setConnection(this.getConnection()); - runSummaryReader.read(); - this.setRunSummary(runSummaryReader.getData()); - - // Read EpicsData and set on RunSummary. - final EpicsDataReader epicsDataReader = new EpicsDataReader(); - epicsDataReader.setRun(this.getRun()); - epicsDataReader.setConnection(this.getConnection()); - epicsDataReader.read(); - this.getRunSummary().setEpicsData(epicsDataReader.getData()); - - // Read ScalerData and set on RunSummary. - final ScalerDataReader scalerDataReader = new ScalerDataReader(); - scalerDataReader.setRun(this.getRun()); - scalerDataReader.setConnection(this.getConnection()); - scalerDataReader.read(); - this.getRunSummary().setScalerData(scalerDataReader.getData()); - - // Read ScalerData and set on RunSummary. - final EvioFileListReader evioFileListReader = new EvioFileListReader(); - evioFileListReader.setRun(this.getRun()); - evioFileListReader.setConnection(this.getConnection()); - evioFileListReader.read(); - this.getRunSummary().setEvioFileList(evioFileListReader.getData()); + private void openConnection() { + try { + if (this.connection == null || this.connection.isClosed()) { + LOGGER.info("creating database connection"); + this.connection = connectionParameters.createConnection(); + } else { + LOGGER.warning("connection already open"); + } + } catch (final SQLException e) { + throw new RuntimeException("Error opening database connection.", e); + } } /** - * Check if the current run number exists in the run database. + * Set the connection externally if using a database other than the default one at JLAB. * - * @return <code>true</code> if run exists + * @param connection the database connection */ - private boolean runExists() throws SQLException { - PreparedStatement statement = null; - boolean exists = false; - try { - statement = connection.prepareStatement("SELECT run FROM runs where run = ?"); - statement.setInt(1, this.run); - final ResultSet resultSet = statement.executeQuery(); - exists = resultSet.next(); - } finally { - if (statement != null) { - try { - statement.close(); - } catch (final SQLException e) { - e.printStackTrace(); - } - } - } - return exists; + public void setConnection(final Connection connection) { + this.connection = connection; } /** - * Set the database connection parameters. - */ - public void setConnectionParameters(final ConnectionParameters connectionParameters) { - this.connectionParameters = connectionParameters; - } - - /** - * Set the run number and load the applicable {@link RunSummary} from the db. + * Set the run number and then load the applicable {@link RunSummary} from the database. * * @param run the run number */ - synchronized void setRun(final int run) { + public synchronized void setRun(final int run) { + // Check if run number is valid. if (run < 0) { throw new IllegalArgumentException("invalid run number: " + run); } - try { + // Setup the database connection. + this.openConnection(); - // Setup the database connection. - if (this.connection == null || this.connection.isClosed()) { - this.connection = connectionParameters.createConnection(); + // Initialize database interface. + final RunSummaryDao runSummaryDao = new RunSummaryDaoImpl(this.connection); + + // Set the current run number. + this.run = run; + + // Does the current run exist in the database? + if (runSummaryDao.runSummaryExists(this.getRun())) { + LOGGER.info("run " + run + " found in database"); + try { + // Read the records from the database and convert into complex Java object. + this.runSummary = runSummaryDao.readFullRunSummary(this.getRun()); + } catch (final Exception e) { + // There was some unknown error when reading in the run records. + LOGGER.log(Level.SEVERE, "Error reading from run database.", e); + throw new RuntimeException(e); } + } else { + // Run is not in the database. + LOGGER.warning("run database record does not exist for run " + run); + } - // Set the current run number. - this.run = run; - - // Does the current run exist in the database? - if (this.runExists()) { - LOGGER.info("run record found in hps_run_db for " + run); - try { - // Read the records from the database and convert into Java objects. - this.readRun(); - } catch (final Exception e) { - // There was some unknown error when reading in the run records. - LOGGER.log(Level.SEVERE, "Error reading from run database for run: " + run, e); - throw new RuntimeException(e); - } - } else { - // Run is not in the database. - LOGGER.warning("run database record does not exist for run " + run); - } - - // Close the database connection. - this.connection.close(); - - } catch (final SQLException e) { - throw new RuntimeException(e); - } - } - - /** - * Set the current {@link RunSummary}. - * - * @param runSummary the current {@link RunSummary} - */ - void setRunSummary(final RunSummary runSummary) { - this.runSummary = runSummary; + // Close the database connection. + this.closeConnection(); } } Modified: java/trunk/record-util/src/main/java/org/hps/record/run/RunSummary.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/RunSummary.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/run/RunSummary.java Thu Aug 20 12:50:12 2015 @@ -4,13 +4,16 @@ import java.io.PrintStream; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; import java.util.Map; import java.util.TimeZone; import org.hps.record.epics.EpicsData; -import org.hps.record.evio.crawler.EvioFileList; +import org.hps.record.evio.EvioFileSequenceComparator; import org.hps.record.scalers.ScalerData; /** @@ -26,11 +29,10 @@ * <li>number of EVIO files in the run</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> - * <li>list of EVIO files in the run</li> * </ul> - * There are also associated {@link org.hps.record.epics.EpicsData} and {@link org.hps.record.scalers.ScalerData} - * objects representing the EPICS and scaler data summaries for the run. The EPICS data is averaged over the whole run - * while the scalers are from the last scaler data bank that was found. + * <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. * * @author Jeremy McCormick, SLAC */ @@ -48,6 +50,9 @@ DATE_DISPLAY.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("America/New_York"))); } + /** + * Date this record was created. + */ private Date created; /** @@ -61,9 +66,9 @@ private long endTimeUtc; /** - * The combined EPICS information for the run (uses the mean values for each variable). - */ - private EpicsData epics; + * The EPICS data from the run. + */ + private List<EpicsData> epicsDataList; /** * The counts of different types of events that were found. @@ -73,7 +78,7 @@ /** * The list of EVIO files in the run. */ - private EvioFileList evioFileList = new EvioFileList(); + private List<File> evioFileList = new ArrayList<File>(); /** * The run number. @@ -86,9 +91,9 @@ private boolean runOkay = true; /** - * The scaler data from the last physics event in the run. - */ - private ScalerData scalerData; + * The scaler data for the run. + */ + private List<ScalerData> scalerDataList; /** * The run start time in UTC (milliseconds). @@ -165,14 +170,12 @@ } /** - * Get the EPICS data summary. - * <p> - * This is computed by taking the mean of each variable for the run. - * - * @return the EPICS data summary - */ - public EpicsData getEpicsData() { - return this.epics; + * Get the EPICS data from the run. + * + * @return the EPICS data from the run + */ + public List<EpicsData> getEpicsDataSet() { + return this.epicsDataList; } /** @@ -202,7 +205,7 @@ * * @return the list of EVIO files in this run */ - public EvioFileList getEvioFileList() { + public List<File> getEvioFileList() { return this.evioFileList; } @@ -225,12 +228,12 @@ } /** - * Get the scaler data of this run (last event only). - * - * @return the scaler data of this run from the last event - */ - public ScalerData getScalerData() { - return this.scalerData; + * Get the scaler data of this run. + * + * @return the scaler data of this run + */ + public List<ScalerData> getScalerData() { + return this.scalerDataList; } /** @@ -295,8 +298,8 @@ public void printOut(final PrintStream ps) { ps.println("--------------------------------------------"); ps.println("run: " + this.run); - ps.println("first file: " + this.evioFileList.first()); - ps.println("last file: " + this.evioFileList.last()); + ps.println("first file: " + this.evioFileList.get(0)); + ps.println("last file: " + this.evioFileList.get(evioFileList.size() - 1)); ps.println("started: " + DATE_DISPLAY.format(this.getStartDate())); ps.println("ended: " + DATE_DISPLAY.format(this.getEndDate())); ps.println("total events: " + this.getTotalEvents()); @@ -333,7 +336,7 @@ /** * Set the end date. * - * @param endDate the end date + * @param endTimeUtc the end date */ public void setEndTimeUtc(final long endTimeUtc) { this.endTimeUtc = endTimeUtc; @@ -344,8 +347,8 @@ * * @param epics the EPICS data for the run */ - public void setEpicsData(final EpicsData epics) { - this.epics = epics; + public void setEpicsData(final List<EpicsData> epicsDataList) { + this.epicsDataList = epicsDataList; } /** @@ -362,7 +365,7 @@ * * @param evioFileList the list of EVIO files for the run */ - public void setEvioFileList(final EvioFileList evioFileList) { + public void setEvioFileList(final List<File> evioFileList) { this.evioFileList = evioFileList; } @@ -380,14 +383,14 @@ * * @param scalerData the scaler data */ - public void setScalerData(final ScalerData scalerData) { - this.scalerData = scalerData; + public void setScalerData(final List<ScalerData> scalerDataList) { + this.scalerDataList = scalerDataList; } /** * Set the start date of the run. * - * @param startDate the start date of the run + * @param startTimeUtc the start date of the run */ public void setStartTimeUtc(final long startTimeUtc) { this.startTimeUtc = startTimeUtc; @@ -424,7 +427,7 @@ * Sort the files in the run by sequence number in place. */ public void sortFiles() { - this.evioFileList.sort(); + Collections.sort(this.evioFileList, new EvioFileSequenceComparator()); } /** Added: java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDao.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDao.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDao.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,99 @@ +package org.hps.record.run; + +import java.util.List; + +/** + * Data Access Object (DAO) API for managing run summary information in the run database. + * + * @author Jeremy McCormick, SLAC + */ +public 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 deleteFullRunSummary(RunSummary runSummary); + + /** + * Delete a run summary by run number. + * + * @param run the run number + */ + void deleteRunSummary(int run); + + /** + * Delete a run summary but not its objects. + * + * @param runSummary the run summary object + */ + void deleteRunSummary(RunSummary runSummary); + + /** + * Get the list of run numbers. + * + * @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. + * + * @param run the run number + * @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. + * + * @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. + * + * @param run the run number + * @return <code>true</code> if <code>run</code> exists in the database + */ + boolean runSummaryExists(int run); + + /** + * Update a run summary but not its objects. + * + * @param runSummary the run summary to update + */ + void updateRunSummary(RunSummary runSummary); +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDaoImpl.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDaoImpl.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/RunSummaryDaoImpl.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,488 @@ +package org.hps.record.run; + +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 java.util.logging.Level; +import java.util.logging.Logger; + +import org.lcsim.util.log.DefaultLogFormatter; +import org.lcsim.util.log.LogUtil; + +/** + * Implementation of database operations for {@link RunSummary} objects in the run database. + * + * @author Jeremy McCormick, SLAC + */ +public 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_time_utc, end_time_utc, 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_time_utc, end_time_utc, 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_time_utc, end_time_utc, nevents, nfiles, end_ok, run_ok WHERE run = ?"; + } + + /** + * Setup class logging. + */ + private static final Logger LOGGER = LogUtil.create(RunSummaryDaoImpl.class, new DefaultLogFormatter(), Level.ALL); + + /** + * The database connection. + */ + private final Connection connection; + + /** + * The database API for EPICS data. + */ + private EpicsDataDao epicsDataDao = null; + + /** + * The database API for EVIO file information. + */ + private EvioFilesDao evioFilesDao = null; + + /** + * The database API for scaler data. + */ + private ScalerDataDao scalerDataDao = null; + + /** + * Create a new DAO object for run summary information. + * + * @param connection the database connection + */ + public RunSummaryDaoImpl(final Connection connection) { + // Set the connection. + if (connection == null) { + throw new IllegalArgumentException("The connection is null."); + } + this.connection = connection; + + // Setup DAO API objects for managing complex object state. + epicsDataDao = new EpicsDataDaoImpl(this.connection); + scalerDataDao = new ScalerDataDaoImpl(this.connection); + evioFilesDao = new EvioFilesDaoImpl(this.connection); + } + + /** + * Delete a run summary from the database including its referenced objects such as EPICS data. + * + * @param runSummary the run summary to delete + */ + @Override + public void deleteFullRunSummary(final RunSummary runSummary) { + // Delete EPICS log. + epicsDataDao.deleteEpicsData(runSummary.getRun()); + + // Delete scaler data. + scalerDataDao.deleteScalerData(runSummary.getRun()); + + // Delete file list. + evioFilesDao.deleteEvioFiles(runSummary.getRun()); + + // Finally delete the run summary information. + this.deleteRunSummary(runSummary.getRun()); + } + + /** + * Delete a run summary by run number. + * + * @param run the run number + */ + @Override + public void deleteRunSummary(final int run) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(RunSummaryQuery.DELETE_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(); + } + } + } + } + + /** + * Delete a run summary but not its objects. + * + * @param runSummary the run summary object + */ + @Override + public void deleteRunSummary(final RunSummary runSummary) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(RunSummaryQuery.DELETE_RUN); + preparedStatement.setInt(1, runSummary.getRun()); + preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Get the list of run numbers. + * + * @return the list of run numbers + */ + @Override + public List<Integer> getRuns() { + final List<Integer> runs = new ArrayList<Integer>(); + PreparedStatement preparedStatement = null; + try { + preparedStatement = this.connection.prepareStatement("SELECT distinct(run) FROM runs ORDER BY run"); + final ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + final Integer run = resultSet.getInt(1); + runs.add(run); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + 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 RunSummary runSummary = new RunSummary(resultSet.getInt("run")); + runSummary.setStartTimeUtc(resultSet.getLong("start_time_utc")); + runSummary.setEndTimeUtc(resultSet.getLong("end_time_utc")); + 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. + * + * @param run the run number + * @return the run summary object + */ + @Override + public RunSummary getRunSummary(final int run) { + PreparedStatement statement = null; + RunSummary runSummary = null; + try { + statement = this.connection.prepareStatement(RunSummaryQuery.SELECT_RUN); + statement.setInt(1, run); + final ResultSet resultSet = statement.executeQuery(); + if (!resultSet.next()) { + throw new IllegalArgumentException("No record exists for run " + run + " in database."); + } + + runSummary = new RunSummary(run); + runSummary.setStartTimeUtc(resultSet.getLong("start_time_utc")); + runSummary.setEndTimeUtc(resultSet.getLong("end_time_utc")); + 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")); + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + return runSummary; + } + + /** + * 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.deleteFullRunSummary(runSummary); + } 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); + + LOGGER.getHandlers()[0].flush(); + } + + 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) { + + // Insert basic run log info. + this.insertRunSummary(runSummary); + + // Insert list of files. + LOGGER.info("inserting EVIO " + runSummary.getEvioFileList().size() + " files"); + evioFilesDao.insertEvioFiles(runSummary.getEvioFileList(), runSummary.getRun()); + + // Insert EPICS data. + LOGGER.info("inserting " + runSummary.getEpicsDataSet().size() + " EPICS records"); + epicsDataDao.insertEpicsData(runSummary.getEpicsDataSet()); + + // Insert scaler data. + LOGGER.info("inserting " + runSummary.getScalerData().size() + " scaler data records"); + scalerDataDao.insertScalerData(runSummary.getScalerData(), runSummary.getRun()); + } + + /** + * 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.setLong(2, runSummary.getStartTimeUtc()); + preparedStatement.setLong(3, runSummary.getEndTimeUtc()); + preparedStatement.setInt(4, runSummary.getTotalEvents()); + preparedStatement.setInt(5, runSummary.getEvioFileList().size()); + 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 RunSummary runSummary = this.getRunSummary(run); + + // Read EPICS data and set on RunSummary. + runSummary.setEpicsData(epicsDataDao.getEpicsData(run)); + + // Read scaler data and set on RunSummary. + runSummary.setScalerData(scalerDataDao.getScalerData(run)); + + // Read EVIO file list and set on RunSummary. + runSummary.setEvioFileList(evioFilesDao.getEvioFiles(run)); + + return runSummary; + } + + /** + * Return <code>true</code> if a run summary exists in the database for the run number. + * + * @param run the run number + * @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 = ?"); + preparedStatement.setInt(1, run); + final ResultSet rs = preparedStatement.executeQuery(); + return rs.first(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Update a run summary but not its complex state. + * + * @param runSummary the run summary to update + */ + @Override + public void updateRunSummary(final RunSummary runSummary) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(RunSummaryQuery.UPDATE_RUN); + preparedStatement.setLong(1, runSummary.getStartTimeUtc()); + preparedStatement.setLong(2, runSummary.getEndTimeUtc()); + preparedStatement.setInt(3, runSummary.getTotalEvents()); + preparedStatement.setInt(4, runSummary.getEvioFileList().size()); + preparedStatement.setBoolean(5, runSummary.getEndOkay()); + preparedStatement.setBoolean(6, runSummary.getRunOkay()); + preparedStatement.setInt(7, runSummary.getRun()); + preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + } +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDao.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDao.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDao.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,36 @@ +package org.hps.record.run; + +import java.util.List; + +import org.hps.record.scalers.ScalerData; + +/** + * Database Access Object (DAO) for scaler data in the run database. + * + * @author Jeremy McCormick, SLAC + */ +public interface ScalerDataDao { + + /** + * Delete scaler data for the run. + * + * @param run the run number + */ + void deleteScalerData(int run); + + /** + * Get scaler data for a run. + * + * @param run the run number + * @return the scaler data for the run + */ + List<ScalerData> getScalerData(int run); + + /** + * Insert scaler data for a run. + * + * @param scalerData the list of scaler data + * @param run the run number + */ + void insertScalerData(List<ScalerData> scalerData, int run); +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDaoImpl.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDaoImpl.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/ScalerDataDaoImpl.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,175 @@ +package org.hps.record.run; + +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.scalers.ScalerData; + +/** + * Implementation of database API for {@link org.hps.record.scalers.ScalerData} in the run database. + * + * @author Jeremy McCormick, SLAC + */ +public class ScalerDataDaoImpl implements ScalerDataDao { + + /** + * SQL query strings. + */ + private static final class ScalerDataQuery { + + /** + * Delete by run. + */ + private static final String DELETE_RUN = "DELETE FROM run_scalers WHERE run = ?"; + /** + * Insert a record. + */ + private static final String INSERT = "INSERT INTO run_scalers (run, event, idx, value) VALUES (?, ?, ?, ?)"; + /** + * Select by run. + */ + private static final String SELECT_RUN = "SELECT event, idx, value FROM run_scalers WHERE run = ? ORDER BY event, idx"; + } + + /** + * The database connection. + */ + private final Connection connection; + + /** + * Create object for managing scaler data in the run database. + * + * @param connection the database connection + */ + public ScalerDataDaoImpl(final Connection connection) { + if (connection == null) { + throw new IllegalArgumentException("The connection is null."); + } + this.connection = connection; + } + + /** + * Delete scaler data for the run. + * + * @param run the run number + */ + @Override + public void deleteScalerData(final int run) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = connection.prepareStatement(ScalerDataQuery.DELETE_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(); + } + } + } + } + + /** + * Get scaler data for a run. + * + * @param run the run number + * @return the scaler data for the run + */ + @Override + public List<ScalerData> getScalerData(final int run) { + PreparedStatement preparedStatement = null; + final List<ScalerData> scalerDataList = new ArrayList<ScalerData>(); + try { + preparedStatement = this.connection.prepareStatement(ScalerDataQuery.SELECT_RUN); + preparedStatement.setInt(1, run); + final ResultSet resultSet = preparedStatement.executeQuery(); + + int[] scalerArray = new int[ScalerData.ARRAY_SIZE]; + int event = 0; + + while (resultSet.next()) { + + // Get record data. + event = resultSet.getInt("event"); + final int idx = resultSet.getInt("idx"); + final int value = resultSet.getInt("value"); + + // Is this the start of a new scaler data set and not the first one? + if (idx == 0 && resultSet.getRow() > 1) { + // Create new scaler data object and add to list. + final ScalerData scalerData = new ScalerData(scalerArray, event); + scalerDataList.add(scalerData); + + // Reset the data array for next object. + scalerArray = new int[ScalerData.ARRAY_SIZE]; + } + + // Set value by index. + scalerArray[idx] = value; + } + + // Add the last object which will not happen inside the loop. + if (scalerArray != null) { + scalerDataList.add(new ScalerData(scalerArray, event)); + } + + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + return scalerDataList; + } + + /** + * Insert scaler data for a run. + * + * @param scalerData the list of scaler data + * @param run the run number + */ + @Override + public void insertScalerData(final List<ScalerData> scalerDataList, final int run) { + PreparedStatement preparedStatement = null; + try { + preparedStatement = this.connection.prepareStatement(ScalerDataQuery.INSERT); + for (final ScalerData scalerData : scalerDataList) { + final int size = scalerData.size(); + final Integer event = scalerData.getEventId(); + if (event == null) { + throw new IllegalStateException("The scaler data is missing the event ID."); + } + for (int i = 0; i < size; i++) { + preparedStatement.setInt(1, run); + preparedStatement.setInt(2, event); + preparedStatement.setInt(3, i); + preparedStatement.setInt(4, scalerData.getValue(i)); + preparedStatement.executeUpdate(); + } + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } finally { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } catch (final SQLException e) { + e.printStackTrace(); + } + } + } + } +} Added: java/trunk/record-util/src/main/java/org/hps/record/run/package-info.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/run/package-info.java (added) +++ java/trunk/record-util/src/main/java/org/hps/record/run/package-info.java Thu Aug 20 12:50:12 2015 @@ -0,0 +1,4 @@ +/** + * API for accessing the HPS run database with run summary information. + */ +package org.hps.record.run; Modified: java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalerData.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalerData.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalerData.java Thu Aug 20 12:50:12 2015 @@ -13,6 +13,11 @@ * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a> */ public final class ScalerData { + + /** + * Fixed array size of scaler data in the EVIO bank. + */ + public static final int ARRAY_SIZE = 72; /** * Default name of scaler data collection in LCSim events. @@ -38,10 +43,11 @@ public static ScalerData read(final EventHeader event, final String collectionName) { ScalerData data = null; if (event.hasCollection(GenericObject.class, collectionName)) { - //System.out.println("ScalerData - found collection"); + // System.out.println("ScalerData - found collection"); final List<GenericObject> objects = event.get(GenericObject.class, collectionName); data = new ScalerData(); data.fromGenericObject(objects.get(0)); + data.setEventId(event.getEventNumber()); } return data; } @@ -50,6 +56,11 @@ * The scaler data values. */ private int[] data; + + /** + * The event ID of the data. + */ + private Integer eventId; /** * This is the no argument constructor which is for package internal use only. @@ -62,9 +73,10 @@ * * @param data the scaler data */ - public ScalerData(final int[] data) { + public ScalerData(final int[] data, final int eventId) { this.data = new int[data.length]; System.arraycopy(data, 0, this.data, 0, data.length); + this.eventId = eventId; } /** @@ -80,6 +92,18 @@ } /** + * Get the event ID of the scaler data. + * <p> + * This information is not persisted to the LCIO. + * + * @return the event ID of the scaler data + */ + public Integer getEventId() { + // Null value will be returned here to indicate not set. + return this.eventId; + } + + /** * Get the scaler data value at the index. * * @param index the scaler data index @@ -88,14 +112,23 @@ public Integer getValue(final int index) { return this.data[index]; } - + /** * Get the value using a {@link ScalerDataIndex} enum. - * + * * @return the value at the index */ - public Integer getValue(ScalerDataIndex scalarDataIndex) { + public Integer getValue(final ScalerDataIndex scalarDataIndex) { return this.data[scalarDataIndex.index()]; + } + + /** + * Set the event ID of the scaler data. + * + * @param eventId the event ID of the scaler data + */ + void setEventId(final int eventId) { + this.eventId = eventId; } /** @@ -154,5 +187,5 @@ final List<GenericObject> collection = new ArrayList<GenericObject>(); collection.add(this.toGenericObject()); event.put(collectionName, collection, GenericObject.class, 0); - } + } } Modified: java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalersEvioProcessor.java ============================================================================= --- java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalersEvioProcessor.java (original) +++ java/trunk/record-util/src/main/java/org/hps/record/scalers/ScalersEvioProcessor.java Thu Aug 20 12:50:12 2015 @@ -1,10 +1,15 @@ package org.hps.record.scalers; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.hps.record.evio.EvioEventConstants; 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.util.log.DefaultLogFormatter; @@ -15,38 +20,36 @@ * * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a> */ -public final class ScalersEvioProcessor extends EvioEventProcessor { +public class ScalersEvioProcessor extends EvioEventProcessor { - private static final Logger LOGGER = LogUtil.create(ScalersEvioProcessor.class, new DefaultLogFormatter(), Level.INFO); + private static final Logger LOGGER = LogUtil.create(ScalersEvioProcessor.class, new DefaultLogFormatter(), + Level.ALL); /** * Currently cached ScalerData object which was created by the process method. */ - private ScalerData data; + private ScalerData currentScalerData; + private boolean resetEveryEvent = true; - boolean resetEveryEvent = true; + private Set<ScalerData> scalerDataSet = new LinkedHashSet<ScalerData>(); + + public ScalerData getCurrentScalerData() { + return this.currentScalerData; + } /** * Get the current scaler data or null if there was none in the last event processed. * * @return the current scaler data or <code>null</code> if none exists */ - public ScalerData getScalerData() { - return this.data; + public List<ScalerData> getScalerData() { + return new ArrayList<ScalerData>(this.scalerDataSet); } - /** - * This method will create a <code>ScalerData</code> object and cache it. The current object is first reset to <code>null</code> every time this - * method is called. - * - * @param evio the EVIO event data - */ - @Override - public void process(final EvioEvent evio) { - if (resetEveryEvent) { - this.data = null; - } - for (final BaseStructure bank : evio.getChildrenList()) { + private ScalerData getScalerData(final EvioEvent evioEvent) { + ScalerData scalerData = null; + // Proceed if sync bit checking is not enabled or sync bit is on. + outerBankLoop: for (final BaseStructure bank : evioEvent.getChildrenList()) { // Does the crate tag match? if (bank.getHeader().getTag() == EvioEventConstants.SCALERS_CRATE_TAG) { if (bank.getChildrenList() != null) { @@ -54,19 +57,48 @@ // Does the bank tag match? if (subBank.getHeader().getTag() == EvioEventConstants.SCALERS_BANK_TAG) { - LOGGER.fine("found scaler data in bank " + subBank.getHeader().getTag() + " and EVIO event " + evio.getEventNumber()); + LOGGER.fine("found scaler data in bank " + subBank.getHeader().getTag() + + " and EVIO event " + evioEvent.getEventNumber()); // Scaler data exists in event so create object and stop processing. - this.data = new ScalerData(subBank.getIntData()); - break; + scalerData = new ScalerData(subBank.getIntData(), + EvioEventUtilities.getEventIdData(evioEvent)[0]); + + break outerBankLoop; } } } } + } + return scalerData; + } + + /** + * This method will create a <code>ScalerData</code> object and cache it. The current object is first reset to + * <code>null</code> every time this method is called. + * + * @param evioEvent the EVIO event data + */ + @Override + public void process(final EvioEvent evioEvent) { + if (resetEveryEvent) { + // Reset the cached data object. + this.currentScalerData = null; + } + + final ScalerData scalerData = this.getScalerData(evioEvent); + if (scalerData != null) { + this.currentScalerData = scalerData; + this.scalerDataSet.add(this.currentScalerData); } } public void setResetEveryEvent(final boolean resetEveryEvent) { this.resetEveryEvent = resetEveryEvent; } + + @Override + public void startJob() { + this.scalerDataSet = new LinkedHashSet<ScalerData>(); + } }