Author: [log in to unmask] Date: Mon Mar 23 16:23:56 2015 New Revision: 2509 Log: Add first pass at EPICS data parsing from EVIO. (This will be moved to another module at some point.) Added: java/trunk/evio/src/main/java/org/hps/evio/epics/ java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsEvioProcessor.java java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsGenericObject.java java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsScalarData.java java/trunk/evio/src/test/java/org/hps/evio/epics/ java/trunk/evio/src/test/java/org/hps/evio/epics/EpicsScalarDataTest.java Modified: java/trunk/evio/pom.xml Modified: java/trunk/evio/pom.xml ============================================================================= --- java/trunk/evio/pom.xml (original) +++ java/trunk/evio/pom.xml Mon Mar 23 16:23:56 2015 @@ -19,6 +19,10 @@ <dependency> <groupId>org.hps</groupId> <artifactId>hps-tracking</artifactId> + </dependency> + <dependency> + <groupId>org.hps</groupId> + <artifactId>hps-record-util</artifactId> </dependency> </dependencies> <build> Added: java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsEvioProcessor.java ============================================================================= --- java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsEvioProcessor.java (added) +++ java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsEvioProcessor.java Mon Mar 23 16:23:56 2015 @@ -0,0 +1,55 @@ +package org.hps.evio.epics; + +import org.hps.record.evio.EvioEventConstants; +import org.hps.record.evio.EvioEventProcessor; +import org.jlab.coda.jevio.BaseStructure; +import org.jlab.coda.jevio.EvioEvent; + +/** + * This is an EVIO event processor that will read EPICS events (event tag 31) + * and turn them into {@link EpicsScalarData} objects. + * + * @author Jeremy McCormick <[log in to unmask]> + */ +public final class EpicsEvioProcessor extends EvioEventProcessor { + + public void process(EvioEvent evio) { + + if (evio.getHeader().getTag() != EvioEventConstants.EPICS_EVENT_TAG) { + // Just silently skip these events because otherwise too many error messages might print. + return; + } + + System.out.println("Epics EVIO event " + evio.getEventNumber()); + System.out.println("Dumping EPICS event ..."); + System.out.println(evio.toXML()); + + // Find the bank with the EPICS information. + BaseStructure epicsBank = null; + BaseStructure topBank = evio.getChildrenList().get(0); + System.out.println("got top bank: " + topBank.getHeader().getTag()); + for (BaseStructure childBank : topBank.getChildrenList()) { + System.out.println("found child bank tag: " + childBank.getHeader().getTag()); + if (childBank.getHeader().getTag() == EvioEventConstants.EPICS_BANK_TAG) { + System.out.println("found EPICS bank tag: " + childBank.getHeader().getTag()); + epicsBank = childBank; + break; + } + } + + if (epicsBank != null) { + System.out.println("found EPICS bank with tag " + epicsBank.getHeader().getTag()); + String epicsData = epicsBank.getStringData()[0]; + System.out.println("dumping EPICS string data ..."); + System.out.println(epicsData); + + EpicsScalarData data = new EpicsScalarData(); + data.fromString(epicsData); + + System.out.println("parsed EPICS data ..."); + System.out.println(data.toString()); + } else { + System.out.println("did not find EPICS data bank in event"); + } + } +} Added: java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsGenericObject.java ============================================================================= --- java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsGenericObject.java (added) +++ java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsGenericObject.java Mon Mar 23 16:23:56 2015 @@ -0,0 +1,56 @@ +package org.hps.evio.epics; + +import org.lcsim.event.GenericObject; + +/** + * This is an implementation of GenericObject for reading and writing EPICS data. + * There is no functionality here. Users that need this data in their <code>Driver</code> + * classes should instead use {@link EpicsScalarData#read(org.lcsim.event.EventHeader)} + * to create the class with the actual API. + * + * @author Jeremy McCormick <[log in to unmask]> + */ +final class EpicsGenericObject implements GenericObject { + + String[] keys; + double[] values; + + @Override + public int getNInt() { + return 0; + } + + @Override + public int getNFloat() { + return 0; + } + + @Override + public int getNDouble() { + return values.length; + } + + @Override + public int getIntVal(int index) { + return 0; + } + + @Override + public float getFloatVal(int index) { + return 0; + } + + @Override + public double getDoubleVal(int index) { + return values[index]; + } + + @Override + public boolean isFixedSize() { + return false; + } + + public String getKey(int index) { + return keys[index]; + } +} Added: java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsScalarData.java ============================================================================= --- java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsScalarData.java (added) +++ java/trunk/evio/src/main/java/org/hps/evio/epics/EpicsScalarData.java Mon Mar 23 16:23:56 2015 @@ -0,0 +1,175 @@ +package org.hps.evio.epics; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.lcsim.event.EventHeader; +import org.lcsim.event.GenericObject; + +/** + * <p> + * This is an API for reading and writing EPICS scalar data to LCIO events, + * as well as parsing the scalar data from a CDATA section of an EVIO string + * data bank. + * <p>Sample data:<br/> + * <pre> + * 2010.350952 MBSY2C_energy + * 0.000000 PSPECIRBCK + * 2.190000 HPS:LS450_2:FIELD + * -8974.000000 HPS:LS450_1:FIELD + * 2400.000000 MTIRBCK + * 3.882200 VCG2C21 + * 4.579233 VCG2C21A + * 6.799115 VCG2C24A + * 6.552529 VCG2H00A + * 5.429465 VCG2H01A + * 5.741360 VCG2H02A + * -0.069630 scaler_calc1 + * 0.000000 scalerS12b + * 0.000000 scalerS13b + * 0.000000 scalerS14b + * 0.000000 scalerS15b + * 0.000000 hallb_IPM2C21A_XPOS + * 0.000000 hallb_IPM2C21A_YPOS + * 0.000000 hallb_IPM2C21A_CUR + * 0.000000 hallb_IPM2C24A_XPOS + * 0.000000 hallb_IPM2C24A_YPOS + * 0.000000 hallb_IPM2C24A_CUR + * 0.000000 hallb_IPM2H00_XPOS + * 0.000000 hallb_IPM2H00_YPOS + * 0.000000 hallb_IPM2H00_XPOS + * 0.000000 hallb_IPM2H00_XPOS + * 0.000000 hallb_IPM2H00_CUR + * 0.000000 hallb_IPM2H02_YPOS + * 0.000000 hallb_IPM2H02_XPOS + * </pre> + * + * @author Jeremy McCormick <[log in to unmask]> +*/ +// TODO: This API needs to be accessible to recon and analysis modules. +public final class EpicsScalarData extends LinkedHashMap<String, Double> { + + // Used in collection parameter map as name of the key list. + static final String EPICS_SCALAR_NAMES = "EPICS_SCALAR_NAMES"; + + // Default collection name in the LCIO event. + static final String DEFAULT_COLLECTION_NAME = "EpicsScalarData"; + + // Dummy collection parameter maps to try and make LCIO happy. + static final Map<String, int[]> DUMMY_INT_MAP = new HashMap<String, int[]>(); + static final Map<String, float[]> DUMMY_FLOAT_MAP = new HashMap<String, float[]>(); + + /** + * Convert this object to a string. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + for (Entry<String, Double> entry : this.entrySet()) { + sb.append(entry.getKey() + " " + entry.getValue() + '\n'); + } + return sb.toString(); + } + + /** + * Parse a raw data string from the EVIO data bank and + * turn it into a list of keys and values within this object. + * @param rawData The raw data in the form of a string. + */ + void fromString(String rawData) { + String lines[] = rawData.split("\\r?\\n"); + for (String line : lines) { + String trimmed = line.trim(); + if (trimmed.length() == 0) { + continue; + } + String[] data = trimmed.split(" "); + Double value = Double.parseDouble(data[0]); + String key = data[1]; + System.out.println("adding key, value: " + data[1] + " " + data[0]); + put(key, value); + } + } + + /** + * Convert this object into a {@link org.lcsim.event.GenericObject} + * that can be written into an LCIO collection. + * @return The GenericObject representing this data. + */ + EpicsGenericObject toGenericObject() { + EpicsGenericObject newObject = new EpicsGenericObject(); + newObject.keys = new String[this.size()]; + newObject.values = new double[this.size()]; + int index = 0; + for (String key : this.keySet()) { + newObject.keys[index] = key; + newObject.values[index] = this.get(key); + index++; + } + return newObject; + } + + /** + * Given a list of keys, read the double values from the + * {@link org.lcsim.event.GenericObject} into the map. + * @param object + * @param keys + */ + void fromGenericObject(GenericObject object, String[] keys) { + for (int index = 0; index < keys.length; index++) { + this.put(keys[index], object.getDoubleVal(index)); + } + } + + /** + * Write this object into an LCIO event under the given collection name. + * @param event The LCIO event. + * @param collectionName The name of the collection in the event. + */ + void write(EventHeader event, String collectionName) { + List<GenericObject> collection = new ArrayList<GenericObject>(); + EpicsGenericObject object = this.toGenericObject(); + collection.add(object); + Map<String, String[]> stringMap = new HashMap<String, String[]>(); + stringMap.put(EPICS_SCALAR_NAMES, object.keys); + event.put(collectionName, collection, GenericObject.class, 0, DUMMY_INT_MAP, DUMMY_FLOAT_MAP, stringMap); + } + + /** + * Write this object into an LCIO event using the default collection name. + * @param event The LCIO event. + */ + void write(EventHeader event) { + write(event, DEFAULT_COLLECTION_NAME); + } + + /** + * Read data into this object from an LCIO event from the given collection name. + * @param event The LCIO event. + * @param collectionName The collection name. + * @return The EPICS data from the LCIO event. + */ + EpicsScalarData read(EventHeader event, String collectionName) { + List<GenericObject> collection = event.get(GenericObject.class, collectionName); + @SuppressWarnings("rawtypes") + Map stringMap = event.getMetaData(collection).getStringParameters(); + String[] keys = (String[]) stringMap.get(EPICS_SCALAR_NAMES); + EpicsScalarData data = new EpicsScalarData(); + data.fromGenericObject(collection.get(0), keys); + return data; + } + + /** + * Read data into this object from an LCIO event using the default collection name. + * This is the primary method for users to read the EPICS data into their Drivers + * in the <code>process</code> method. + * @param event The LCIO event. + * @return The EPICS data from the event. + */ + public EpicsScalarData read(EventHeader event) { + return read(event, DEFAULT_COLLECTION_NAME); + } +} Added: java/trunk/evio/src/test/java/org/hps/evio/epics/EpicsScalarDataTest.java ============================================================================= --- java/trunk/evio/src/test/java/org/hps/evio/epics/EpicsScalarDataTest.java (added) +++ java/trunk/evio/src/test/java/org/hps/evio/epics/EpicsScalarDataTest.java Mon Mar 23 16:23:56 2015 @@ -0,0 +1,28 @@ +package org.hps.evio.epics; + +import junit.framework.TestCase; + +import org.hps.record.composite.CompositeLoop; +import org.hps.record.composite.CompositeLoopConfiguration; +import org.hps.record.enums.DataSourceType; +import org.hps.record.enums.ProcessingStage; + +public class EpicsScalarDataTest extends TestCase { + + public void test() { + + CompositeLoopConfiguration configuration = new CompositeLoopConfiguration(); + configuration.add(new EpicsEvioProcessor()); + configuration.setDataSourceType(DataSourceType.EVIO_FILE); + configuration.setFilePath("/u1/data/hps/eng_run/hps_004385.evio.0"); + configuration.setProcessingStage(ProcessingStage.EVIO); + configuration.setStopOnEndRun(false); + configuration.setStopOnErrors(false); + + CompositeLoop loop = new CompositeLoop(); + loop.setConfiguration(configuration); + + loop.loop(-1); + } + +}