package org.epics.archiverappliance.retrieval.postprocessors; import java.io.IOException; import java.sql.Timestamp; import java.util.concurrent.Callable; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.EventStream; import org.epics.archiverappliance.EventStreamDesc; import org.epics.archiverappliance.common.TimeUtils; import org.epics.archiverappliance.config.PVTypeInfo; import org.epics.archiverappliance.engine.membuf.ArrayListEventStream; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; import edu.stanford.slac.archiverappliance.PB.data.PBParseException; /** * This takes "interval" argument and returns only the first sample in that interval... * @author mshankar * */ public class FirstSamplePP implements PostProcessor, AfterAllStreams { private static Logger logger = Logger.getLogger(FirstSamplePP.class.getName()); private int intervalSecs = PostProcessors.DEFAULT_SUMMARIZING_INTERVAL; private long firstBin = 0; private long lastBin = Long.MAX_VALUE; private long previousBinNum = -1; Event lastSampleBeforeStart = null; boolean lastSampleBeforeStartAdded = false; private EventStreamDesc lastSampleDesc; @Override public void initialize(String userarg, String pvName) throws IOException { if(userarg != null && userarg.contains("_")) { String[] userparams = userarg.split("_"); String intervalStr = userparams[1]; intervalSecs = Integer.parseInt(intervalStr); logger.debug("FirstSamplePP, using user supplied interval of " + intervalSecs); } else { logger.debug("FirstSamplePP, using the default interval of " + intervalSecs + " as the user has not specified the interval argument."); } } @Override public long estimateMemoryConsumption(String pvName, PVTypeInfo typeInfo, Timestamp start, Timestamp end, HttpServletRequest req) { firstBin = TimeUtils.convertToEpochSeconds(start)/intervalSecs; lastBin = TimeUtils.convertToEpochSeconds(end)/intervalSecs; float storageRate = typeInfo.getComputedStorageRate(); long numSeconds = TimeUtils.convertToEpochSeconds(end) - TimeUtils.convertToEpochSeconds(start); // Add a fudge factor of 2 for java long estimatedMemoryConsumption = (long) (storageRate*numSeconds*2/intervalSecs); return estimatedMemoryConsumption; } @Override public Callable<EventStream> wrap(final Callable<EventStream> callable) { return new Callable<EventStream>() { @Override public EventStream call() throws Exception { try(EventStream strm = callable.call()) { ArrayListEventStream buf = new ArrayListEventStream(0, (RemotableEventStreamDesc) strm.getDescription()); for(Event e : strm) { try { long epochSeconds = e.getEpochSeconds(); long binNumber = epochSeconds/intervalSecs; if(binNumber >= firstBin && binNumber <= lastBin) { if(binNumber != previousBinNum) { if(!lastSampleBeforeStartAdded && lastSampleBeforeStart != null) { logger.info("Adding the lastSampleBeforeStart at " + TimeUtils.convertToHumanReadableString(lastSampleBeforeStart.getEventTimeStamp()) + " into the result stream"); buf.add(lastSampleBeforeStart); lastSampleBeforeStartAdded = true; lastSampleDesc = null; } buf.add(e.makeClone()); previousBinNum = binNumber; logger.debug("Bin Number " + binNumber + " First: " + firstBin + " Last: " + lastBin); } } else if(binNumber < firstBin) { // Michael Davidsaver's special case; keep track of the last value before the start time and then add that in as a single sample. if(!lastSampleBeforeStartAdded) { if(lastSampleBeforeStart != null) { if(e.getEpochSeconds() >= lastSampleBeforeStart.getEpochSeconds()) { lastSampleBeforeStart = e.makeClone(); lastSampleDesc = strm.getDescription(); logger.info("Resetting the lastSampleBeforeStart to " + TimeUtils.convertToHumanReadableString(lastSampleBeforeStart.getEventTimeStamp())); } } else { lastSampleBeforeStart = e.makeClone(); lastSampleDesc = strm.getDescription(); logger.info("Setting the lastSampleBeforeStart to " + TimeUtils.convertToHumanReadableString(lastSampleBeforeStart.getEventTimeStamp())); } } } } catch(PBParseException ex) { logger.error("Skipping possible corrupted event for pv " + strm.getDescription()); } } return buf; } } }; } @Override public String getIdentity() { return "firstSample"; } @Override public String getExtension() { if(intervalSecs == PostProcessors.DEFAULT_SUMMARIZING_INTERVAL) { return "firstSample"; } else { return "firstSample_" + Integer.toString(intervalSecs); } } @Override public EventStream anyFinalData() { if(!lastSampleBeforeStartAdded && lastSampleBeforeStart != null) { ArrayListEventStream buf = new ArrayListEventStream(0, (RemotableEventStreamDesc) lastSampleDesc); logger.info("Returning the lastSampleBeforeStart at " + TimeUtils.convertToHumanReadableString(lastSampleBeforeStart.getEventTimeStamp()) + " into the result stream after all the other streams have been processed."); buf.add(lastSampleBeforeStart); lastSampleBeforeStartAdded = true; lastSampleDesc = null; return buf; } return null; } }