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; + } +}