package org.epics.archiverappliance.retrieval.postprocessors; import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Set; 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.TimeUtils; import org.epics.archiverappliance.config.ArchDBRTypes; import org.epics.archiverappliance.data.DBRTimeEvent; import org.epics.archiverappliance.data.ScalarValue; import org.epics.archiverappliance.data.VectorValue; import org.epics.archiverappliance.engine.membuf.ArrayListEventStream; import org.epics.archiverappliance.retrieval.ChangeInYearsException; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; import org.epics.archiverappliance.retrieval.RemotableOverRaw; import org.epics.archiverappliance.retrieval.postprocessors.SummaryStatsPostProcessor.SummaryValue; /** * An event stream over a list of SummaryStatsCollectors; typically used by post processors that return consolidated results. * * @author mshankar * @see PostProcessorWithConsolidatedEventStream */ public class SummaryStatsCollectorEventStream implements EventStream, RemotableOverRaw { private static Logger logger = Logger.getLogger(SummaryStatsCollectorEventStream.class.getName()); private final RemotableEventStreamDesc desc; private LinkedHashMap<Long, SummaryValue> consolidatedData; private long firstBin; private long lastBin; private int intervalSecs; private boolean inheritValuesFromPreviousBins; private boolean zeroOutEmptyBins; private Iterator<Event> theOneAndOnlyIterator; private final boolean vectorType; private final ArchDBRTypes dbrType; public SummaryStatsCollectorEventStream(long firstBin, long lastBin, int intervalSecs, RemotableEventStreamDesc desc, LinkedHashMap<Long, SummaryValue> consolidatedData, boolean inheritValuesFromPreviousBins, boolean zeroOutEmptyBins, boolean vectorType, int elementCount) { this.vectorType = vectorType; this.firstBin = firstBin; this.lastBin = lastBin; this.intervalSecs = intervalSecs; this.desc = new RemotableEventStreamDesc(desc); // Summaries of scalars are always double. That's what commons.math returns. this.dbrType = vectorType ? ArchDBRTypes.DBR_WAVEFORM_DOUBLE : ArchDBRTypes.DBR_SCALAR_DOUBLE; this.desc.setArchDBRType(dbrType); if (vectorType) { this.desc.setElementCount(elementCount); } this.consolidatedData = consolidatedData; this.inheritValuesFromPreviousBins = inheritValuesFromPreviousBins; this.zeroOutEmptyBins = zeroOutEmptyBins; } @Override public void close() throws IOException { } @Override public Iterator<Event> iterator() { if(theOneAndOnlyIterator != null) { return theOneAndOnlyIterator; } else { theOneAndOnlyIterator = new SummaryStatsCollectorEventStreamIterator(); return theOneAndOnlyIterator; } } @Override public RemotableEventStreamDesc getDescription() { return desc; } private class SummaryStatsCollectorEventStreamIterator implements Iterator<Event> { ArrayListEventStream strm = new ArrayListEventStream(consolidatedData.size(), desc); short currentYear = -1; int currentEvent = 0; int totalEvents = -1; SummaryStatsCollectorEventStreamIterator() { SummaryValue summaryValue = null; boolean foundValue = false; int nanos = ((intervalSecs % 2) == 0) ? 0 : 500000000; if(consolidatedData.isEmpty()) { logger.info("We not seem to have any events"); totalEvents = 0; return; } Set<Long> bins = consolidatedData.keySet(); if(firstBin == 0) { firstBin = Collections.min(bins); } if(lastBin == Long.MAX_VALUE) { lastBin = Collections.max(bins); } for(long binNum = firstBin; binNum <= lastBin; binNum++) { if(consolidatedData.containsKey(binNum)) { summaryValue = consolidatedData.get(binNum); foundValue = true; } else { if(inheritValuesFromPreviousBins) { if(foundValue) { logger.debug("Inheriting previous value for bin " + binNum); if(SummaryStatsCollectorEventStream.this.zeroOutEmptyBins) { summaryValue = new SummaryValue(0.0, 0, false); } } } else { foundValue = false; logger.debug("Skipping inheriting previous value for bin " + binNum); } } if(foundValue) { long epochSeconds = binNum*intervalSecs + intervalSecs/2; POJOEvent pojoEvent; if (vectorType) { pojoEvent = new POJOEvent(dbrType, TimeUtils.convertFromEpochSeconds(epochSeconds, nanos), new VectorValue<>(summaryValue.values), 0, summaryValue.severity); } else { pojoEvent = new POJOEvent(dbrType, TimeUtils.convertFromEpochSeconds(epochSeconds, nanos), new ScalarValue<Double>(summaryValue.value), 0, summaryValue.severity); } DBRTimeEvent pbevent = (DBRTimeEvent) pojoEvent.makeClone(); if(summaryValue.connectionChanged) { pbevent.addFieldValue("connectionChange", "true"); } if(summaryValue.additionalCols != null && !summaryValue.additionalCols.isEmpty()) { for(String addnName : summaryValue.additionalCols.keySet()) { String addnValue = summaryValue.additionalCols.get(addnName); pbevent.addFieldValue(addnName, addnValue); } } strm.add(pbevent); if(currentYear == -1) { // Initialize the current year as the year of the first bin with a value it it. currentYear = TimeUtils.computeYearForEpochSeconds(epochSeconds); SummaryStatsCollectorEventStream.this.desc.setYear(currentYear); } } } totalEvents = strm.size(); } @Override public boolean hasNext() { return currentEvent < totalEvents; } @Override public Event next() { Event next = strm.get(currentEvent); short eventYear = TimeUtils.computeYearForEpochSeconds(next.getEpochSeconds()); if(eventYear != currentYear) { logger.info("Detected a change in years eventYear " + eventYear + " and currentYear is " + eventYear); SummaryStatsCollectorEventStream.this.desc.setYear(eventYear); short tempCurrentYear = currentYear; currentYear = eventYear; throw new ChangeInYearsException(tempCurrentYear, eventYear); } currentEvent++; return next; } @Override public void remove() { throw new UnsupportedOperationException(); } } }