Print

Print


Author: [log in to unmask]
Date: Tue Aug  4 15:10:48 2015
New Revision: 3332

Log:
Add persistency of EPICS header to LCIO.  HPSJAVA-567

Added:
    java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsHeader.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

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	Tue Aug  4 15:10:48 2015
@@ -14,7 +14,8 @@
 /**
  * This is an API for reading and writing EPICS data to LCIO events, as well as parsing the data from a CDATA section
  * within an EVIO string data bank. The {@link #read(EventHeader)} method should be used to create one of these objects
- * from an LCIO event.
+ * from an LCIO event. The keys are stored in the string parameters of the collection, because
+ * <code>GenericObject</code> cannot persist string data.
  *
  * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a>
  */
@@ -26,80 +27,79 @@
     private static final String DEFAULT_COLLECTION_NAME = "EpicsData";
 
     /**
-     * This map contains the list of EPICS key descriptions from the<br/>
+     * Dummy float parameters to make LCIO persistency work.
+     */
+    private static final Map<String, float[]> DUMMY_FLOAT_MAP = new HashMap<String, float[]>();
+
+    /**
+     * Dummy int parameters to make LCIO persistency work.
+     */
+    private static final Map<String, int[]> DUMMY_INT_MAP = new HashMap<String, int[]>();
+
+    /**
+     * Collection parameter that has the EPICS variable names.
+     */
+    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> DESCRIPTIONS = new HashMap<String, String>();
-
-    /**
-     * Dummy float parameters to make LCIO persistency work.
-     */
-    private static final Map<String, float[]> DUMMY_FLOAT_MAP = new HashMap<String, float[]>();
-
-    /**
-     * Dummy int parameters to make LCIO persistency work.
-     */
-    private static final Map<String, int[]> DUMMY_INT_MAP = new HashMap<String, int[]>();
-
-    /**
-     * Collection parameter that has the EPICS variable names.
-     */
-    private static final String EPICS_VARIABLE_NAMES = "EPICS_VARIABLE_NAMES";
-
-    /**
-     * List of descriptions.
-     */
-    // FIXME: Maybe this should not be listed here.
+    private final static Map<String, String> VARIABLES = new HashMap<String, String>();
+
+    /**
+     * List of possible EPICS keys.
+     */
     static {
-        DESCRIPTIONS.put("MBSY2C_energy", "Beam energy according to Hall B BSY dipole string");
-        DESCRIPTIONS.put("PSPECIRBCK", "Pair Spectrometer Current Readback");
-        DESCRIPTIONS.put("HPS:LS450_2:FIELD", "Frascati probe field");
-        DESCRIPTIONS.put("HPS:LS450_1:FIELD", "Pair Spectrometer probe field");
-        DESCRIPTIONS.put("MTIRBCK", "Frascati Current Readback");
-        DESCRIPTIONS.put("VCG2C21 2C21", "Vacuum gauge pressure");
-        DESCRIPTIONS.put("VCG2C21A", "2C21A Vacuum gauge pressure");
-        DESCRIPTIONS.put("VCG2C24A", "2C24A Vacuum gauge pressure");
-        DESCRIPTIONS.put("VCG2H00A", "2H00 Vacuum gauge pressure");
-        DESCRIPTIONS.put("VCG2H01A", "2H01 Vacuum gauge pressure");
-        DESCRIPTIONS.put("VCG2H02A", "2H02 Vacuum gauge pressure");
-        DESCRIPTIONS.put("scaler_calc1", "Faraday cup current");
-        DESCRIPTIONS.put("scalerS12b", "HPS-Left beam halo count");
-        DESCRIPTIONS.put("scalerS13b", "HPS-Right beam halo count");
-        DESCRIPTIONS.put("scalerS14b", "HPS-Top beam halo count");
-        DESCRIPTIONS.put("scalerS15b", "HPS-SC beam halo count");
-        DESCRIPTIONS.put("hallb_IPM2C21A_XPOS", "Beam position X at 2C21");
-        DESCRIPTIONS.put("hallb_IPM2C21A_YPOS", "Beam position Y at 2C21");
-        DESCRIPTIONS.put("hallb_IPM2C21A_CUR", "Current at 2C21");
-        DESCRIPTIONS.put("hallb_IPM2C24A_XPOS", "Beam position X at 2C24");
-        DESCRIPTIONS.put("hallb_IPM2C24A_YPOS", "Beam position Y at 2C24");
-        DESCRIPTIONS.put("hallb_IPM2C24A_CUR", "Current at 2C24");
-        DESCRIPTIONS.put("hallb_IPM2H00_XPOS", "Beam position X at 2H00");
-        DESCRIPTIONS.put("hallb_IPM2H00_YPOS", "Beam position Y at 2H00");
-        DESCRIPTIONS.put("hallb_IPM2H00_CUR", "Current at 2H00");
-        DESCRIPTIONS.put("hallb_IPM2H02_YPOS", "Beam position X at 2H02");
-        DESCRIPTIONS.put("hallb_IPM2H02_XPOS", "Beam position Y at 2H02");
+        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()}.
+     * instead use the method {@link #getKeys()} method.
      *
      * @return the set of default EPICS variable names
      */
-    public static Set<String> getDefaultKeys() {
-        return DESCRIPTIONS.keySet();
+    public static Set<String> getVariableNames() {
+        return VARIABLES.keySet();
     };
-
-    /**
-     * Get the description of a named EPICS variable.
-     *
-     * @param name the name of the variable
-     */
-    public static String getDescription(final String name) {
-        return DESCRIPTIONS.get(name);
-    }
 
     /**
      * <p>
@@ -142,6 +142,26 @@
     private final Map<String, Double> dataMap = new LinkedHashMap<String, Double>();
 
     /**
+     * The EPICS header information.
+     */
+    private EpicsHeader epicsHeader;
+
+    /**
+     * Class constructor.
+     */
+    public EpicsData() {
+    }
+
+    /**
+     * Class constructor that parses string data.
+     *
+     * @param data the string data
+     */
+    EpicsData(final String data) {
+        this.fromString(data);
+    }
+
+    /**
      * Given a list of names, read the double values from the {@link org.lcsim.event.GenericObject} into the data map of
      * this object.
      *
@@ -149,8 +169,16 @@
      * @param names The list of names.
      */
     private void fromGenericObject(final GenericObject object, final String[] names) {
+
+        // Read data from double array.
         for (int index = 0; index < names.length; index++) {
             this.dataMap.put(names[index], object.getDoubleVal(index));
+        }
+
+        // Read header data if set.
+        if (object.getNInt() > 0) {
+            final int[] headerData = new int[] {object.getIntVal(0), object.getIntVal(1), object.getIntVal(2)};
+            this.epicsHeader = new EpicsHeader(headerData);
         }
     }
 
@@ -174,10 +202,17 @@
     }
 
     /**
+     * Get the EPICS header information or <code>null</code> if not set.
+     */
+    public EpicsHeader getEpicsHeader() {
+        return this.epicsHeader;
+    }
+
+    /**
      * Get the list of EPICS variables used by this object.
      * <p>
-     * This could potentially be different than the list of default names from {@link #getDefaultKeys()} but it will
-     * usually be the same.
+     * 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
      */
@@ -186,7 +221,7 @@
     }
 
     /**
-     * Get a double value from the key which should be a valid EPICS variable name.
+     * Get a double value from the key, which should be a valid EPICS variable name.
      *
      * @return the value from the key
      */
@@ -204,6 +239,15 @@
     }
 
     /**
+     * Set the EPICS header information.
+     *
+     * @param epicsHeader the {@link EpicsHeader} object
+     */
+    void setEpicsHeader(final EpicsHeader epicsHeader) {
+        this.epicsHeader = epicsHeader;
+    }
+
+    /**
      * Set a double value by name.
      *
      * @return the value from the key
@@ -218,15 +262,28 @@
      * @return the <code>GenericObject</code> representing this data
      */
     private EpicsGenericObject toGenericObject() {
+
+        // Create new GenericObject.
         final EpicsGenericObject newObject = new EpicsGenericObject();
+
         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);
             newObject.setValue(index, this.dataMap.get(key));
             index++;
         }
+
+        // Write header information into the object's int array.
+        if (epicsHeader != null) {
+            final int[] headerData = new int[] {
+                    epicsHeader.getRun(), 
+                    epicsHeader.getSequence(),
+                    epicsHeader.getTimeStamp()};
+            newObject.setHeaderData(headerData);
+        }
         return newObject;
     }
 
@@ -261,9 +318,13 @@
      * @param collectionName the name of the collection in the output event
      */
     private void write(final EventHeader event, final String collectionName) {
+
+        // Create the new collection and add the GenericObject to it.
         final List<GenericObject> collection = new ArrayList<GenericObject>();
         final EpicsGenericObject object = this.toGenericObject();
         collection.add(object);
+
+        // Write out the collection to the event, including the string parameters with the key names.
         final Map<String, String[]> stringMap = new HashMap<String, String[]>();
         stringMap.put(EPICS_VARIABLE_NAMES, object.getKeys());
         event.put(collectionName, collection, GenericObject.class, 0, DUMMY_INT_MAP, DUMMY_FLOAT_MAP, stringMap);

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	Tue Aug  4 15:10:48 2015
@@ -17,7 +17,7 @@
  *
  * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a>
  */
-public final class EpicsEvioProcessor extends EvioEventProcessor {
+public class EpicsEvioProcessor extends EvioEventProcessor {
 
     /**
      * Setup class logger.
@@ -55,12 +55,22 @@
             // Find the bank with the EPICS data string.
             final BaseStructure epicsBank = EvioBankTag.EPICS_STRING.findBank(evio);
 
-            // Was EPICS data found?
+            // Was EPICS data found in the event?
             if (epicsBank != null) {
+
                 // Create EpicsData object from bank's string data.
-                final String epicsData = epicsBank.getStringData()[0];
-                this.data = new EpicsData();
-                this.data.fromString(epicsData);
+                this.data = new EpicsData(epicsBank.getStringData()[0]);
+
+                // Find the header information in the event.
+                final BaseStructure headerBank = EvioBankTag.EPICS_HEADER.findBank(evio);
+
+                if (headerBank != null) {
+                    // Set the header object.
+                    this.data.setEpicsHeader(EpicsHeader.fromEvio(headerBank.getIntData()));
+                } else {
+                    LOGGER.warning("No EPICS header bank found in event.");
+                }
+
             } 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.");
@@ -69,7 +79,7 @@
             }
         }
     }
-    
+
     /**
      * Reset the current <code>EpicsData</code> object to <code>null</code>.
      */

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	Tue Aug  4 15:10:48 2015
@@ -3,12 +3,18 @@
 import org.lcsim.event.GenericObject;
 
 /**
- * This is an implementation of GenericObject for reading and writing EPICS data. There is no functionality here intended for ends users. Instead, the
- * EPICS data should be accessed using {@link EpicsData#read(org.lcsim.event.EventHeader)} to create the data object from input event data.
+ * This is an implementation of GenericObject for reading and writing EPICS data. There is no functionality here
+ * intended for ends users. Instead, the EPICS data should be accessed using
+ * {@link EpicsData#read(org.lcsim.event.EventHeader)} to create the data object from input event data.
  *
  * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a>
  */
 final class EpicsGenericObject implements GenericObject {
+
+    /**
+     * The header information.
+     */
+    private int[] headerData;
 
     /**
      * The names of the EPICS variables.
@@ -37,7 +43,7 @@
 
     @Override
     public int getIntVal(final int index) {
-        return 0;
+        return headerData[index];
     }
 
     /**
@@ -76,7 +82,7 @@
 
     @Override
     public int getNInt() {
-        return 0;
+        return this.headerData.length;
     }
 
     /**
@@ -85,6 +91,15 @@
     @Override
     public boolean isFixedSize() {
         return false;
+    }
+
+    /**
+     * Set the header data.
+     *
+     * @param data the header data array
+     */
+    void setHeaderData(final int[] headerData) {
+        this.headerData = headerData;
     }
 
     /**

Added: 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	(added)
+++ java/trunk/record-util/src/main/java/org/hps/record/epics/EpicsHeader.java	Tue Aug  4 15:10:48 2015
@@ -0,0 +1,80 @@
+package org.hps.record.epics;
+
+/**
+ * Representation of EPICs header data (run, sequence, time stamp).
+ *
+ * @author Jeremy McCormick, SLAC
+ */
+public final class EpicsHeader {
+
+    /**
+     * Create an {@link EpicsHeader} from an int array in the EVIO bank.
+     * <p>
+     * This reads in indices 1 to 3 as 0 and 5 are unused.
+     *
+     * @param headerBank the header bank data
+     * @return the {@link EpicsHeader} object
+     */
+    static EpicsHeader fromEvio(final int[] headerBank) {
+        final int[] headerData = new int[] {headerBank[1], headerBank[2], headerBank[3]};
+        return new EpicsHeader(headerData);
+    }
+
+    /**
+     * The run number.
+     */
+    private final int run;
+
+    /**
+     * The sequence number.
+     */
+    private final int sequence;
+
+    /**
+     * The time stamp in seconds (Unix).
+     */
+    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>.
+     *
+     * @param data the header data with length 3
+     */
+    public EpicsHeader(final int[] data) {
+        if (data.length != 3) {
+            throw new IllegalArgumentException("Bad array length: " + data.length);
+        }
+        run = data[0];
+        sequence = data[1];
+        timestamp = data[2];
+    }
+
+    /**
+     * Get the run number.
+     *
+     * @return the run number
+     */
+    public int getRun() {
+        return run;
+    }
+
+    /**
+     * Get the sequence number.
+     *
+     * @return the sequence number
+     */
+    public int getSequence() {
+        return sequence;
+    }
+
+    /**
+     * Get the time stamp.
+     *
+     * @return the time stamp
+     */
+    public int getTimeStamp() {
+        return timestamp;
+    }
+}