package org.epics.archiverappliance.retrieval.postprocessors; import java.io.IOException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import javax.servlet.http.HttpServletRequest; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.EventStream; import org.epics.archiverappliance.common.POJOEvent; import org.epics.archiverappliance.common.TimeSpan; import org.epics.archiverappliance.config.PVTypeInfo; import org.epics.archiverappliance.data.AlarmInfo; import org.epics.archiverappliance.engine.membuf.ArrayListEventStream; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; /** * * <code>Optimized</code> expects one parameter at initialization, which is the number of requested points. * If there are less samples in the time interval than requested (with a certain deadband), all samples * will be returned. If there are more samples than requested, the samples will be collected into bins. * Mean, std, min, max and count of each bin is calculated and returned as a single sample. * * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a> * */ public class Optimized implements PostProcessor, PostProcessorWithConsolidatedEventStream, FillNoFillSupport { private static final Logger LOGGER = Logger.getLogger(Optimized.class); private static final int DEFAULT_NUMBER_OF_POINTS = 1000; private static final String IDENTITY = "optimized"; private int numEvents; private ArrayListEventStream allEvents; private ArrayListEventStream transformedRawEvents; private int numberOfPoints = DEFAULT_NUMBER_OF_POINTS; private final Statistics statisticsPostProcessor = new Statistics(){ @Override public SummaryStatsVectorCollector getCollector() { return new SummaryStatsVectorCollector() { private SummaryStatistics stats = new SummaryStatistics(); @Override public void setBinParams(int intervalSecs, long binNum) { } @Override public boolean haveEventsBeenAdded() { return stats.getN() > 0; } @Override public List<Double> getVectorValues() { List<Double> list = new ArrayList<>(5); list.add(stats.getMean()); list.add(stats.getStandardDeviation()); list.add(stats.getMin()); list.add(stats.getMax()); list.add((double)stats.getN()); return list; } @Override public double getStat() { return Double.NaN; } @Override public void addEvent(Event e) { if (numEvents < numberOfPoints) { allEvents.add(e); } numEvents++; double val = e.getSampleValue().getValue().doubleValue(); if(!Double.isNaN(val)) { stats.addValue(val); } else { LOGGER.warn("Skipping NAN"); } } }; } }; @Override public void initialize(String userarg, String pvName) throws IOException { if(userarg != null && userarg.contains("_")) { String[] userparams = userarg.split("_"); String numberStr = userparams[1]; numberOfPoints = Integer.parseInt(numberStr); } numEvents = 0; } @Override public long estimateMemoryConsumption(String pvName, PVTypeInfo typeInfo, Timestamp start, Timestamp end, HttpServletRequest req) { int intervalSecs = (int)((end.getTime() - start.getTime())/(1000 * numberOfPoints)); try { statisticsPostProcessor.initialize(getIdentity() + "_" + Integer.toString(intervalSecs),pvName); } catch (IOException e) { LOGGER.error("Error initializing the optimized post processor.",e); } return statisticsPostProcessor.estimateMemoryConsumption(pvName,typeInfo,start,end,req); } @Override public void doNotInheritValuesFromPrevioisBins() { statisticsPostProcessor.doNotInheritValuesFromPrevioisBins(); } @Override public boolean zeroOutEmptyBins() { return statisticsPostProcessor.zeroOutEmptyBins(); } @Override public EventStream getConsolidatedEventStream() { if (numEvents > allEvents.size()) { return statisticsPostProcessor.getConsolidatedEventStream(); } else { return transformedRawEvents; } } @Override public long getStartBinEpochSeconds() { return statisticsPostProcessor.getStartBinEpochSeconds(); } @Override public long getEndBinEpochSeconds() { return statisticsPostProcessor.getEndBinEpochSeconds(); } @Override public LinkedList<TimeSpan> getBinTimestamps() { return statisticsPostProcessor.getBinTimestamps(); } @Override public String getIdentity() { return IDENTITY; } @Override public String getExtension() { if(numberOfPoints == DEFAULT_NUMBER_OF_POINTS) { return getIdentity(); } else { return getIdentity() + "_" + Integer.toString(numberOfPoints); } } @Override public Callable<EventStream> wrap(final Callable<EventStream> callable) { return new Callable<EventStream>() { public EventStream call() throws Exception { if (allEvents == null) { EventStream strm = callable.call(); RemotableEventStreamDesc org = (RemotableEventStreamDesc)strm.getDescription(); RemotableEventStreamDesc desc = new RemotableEventStreamDesc(org); allEvents = new ArrayListEventStream(numberOfPoints, desc); } Callable<EventStream> stCall = statisticsPostProcessor.wrap(callable); EventStream stream = stCall.call(); if (numEvents > allEvents.size()) { return stream; } else { transformedRawEvents = new ArrayListEventStream(allEvents.size(),allEvents.getDescription()); for (Event e : allEvents) { if (e instanceof AlarmInfo) { transformedRawEvents.add(new POJOEvent(e.getDBRType(),e.getEventTimeStamp(),e.getSampleValue(),((AlarmInfo)e).getStatus(),((AlarmInfo)e).getSeverity())); } else { transformedRawEvents.add(new POJOEvent(e.getDBRType(),e.getEventTimeStamp(),e.getSampleValue(),0,0)); } } return transformedRawEvents; } } }; } }