/******************************************************************************* * Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University * as Operator of the SLAC National Accelerator Laboratory. * Copyright (c) 2011 Brookhaven National Laboratory. * EPICS archiver appliance is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. *******************************************************************************/ package edu.stanford.slac.archiverappliance.PlainPB; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Timestamp; import java.util.Iterator; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.EventStream; import org.epics.archiverappliance.common.TimeUtils; import org.epics.archiverappliance.common.YearSecondTimestamp; import org.epics.archiverappliance.config.ArchDBRTypes; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; import org.epics.archiverappliance.retrieval.RemotableOverRaw; import edu.stanford.slac.archiverappliance.PB.search.FileEventStreamSearch; import edu.stanford.slac.archiverappliance.PB.utils.LineByteStream; /** * An eventstream that spans multiple PB files. * You can only get one iterator out of this event stream. This condition is also checked for. * This is typically used with/after PlainPBFileNameUtility.getFilesWithData * @author mshankar * */ public class MultiFilePBEventStream implements EventStream, RemotableOverRaw { private static final Logger logger = Logger.getLogger(MultiFilePBEventStream.class); private LineByteStreamCreator istreams[] = null; private String pvName; private ArchDBRTypes type; private RemotableEventStreamDesc desc; private MultiFilePBEventStreamIterator theIterator = null; public MultiFilePBEventStream(Path[] paths, String pvName, ArchDBRTypes dbrtype, Timestamp startTime, Timestamp endTime) throws IOException { this.pvName = pvName; this.type = dbrtype; YearSecondTimestamp startYTS = TimeUtils.convertToYearSecondTimestamp(startTime); YearSecondTimestamp endYTS = TimeUtils.convertToYearSecondTimestamp(endTime); int startSecondsIntoYear = startYTS.getSecondsintoyear(); int endSecondsIntoYear = endYTS.getSecondsintoyear(); // We need at least two files for this event stream to work correctly. assert(paths.length > 1); PBFileInfo pbinfo = new PBFileInfo(paths[0]); this.desc = new RemotableEventStreamDesc(this.pvName, pbinfo.getInfo()); istreams = new LineByteStreamCreator[paths.length]; for(int i = 0; i < paths.length; i++) { Path path = paths[i]; pbinfo = new PBFileInfo(path); try { if(i == 0) { if(pbinfo.getDataYear() == startYTS.getYear()) { logger.debug("Looking for start position in file " + path.toAbsolutePath().toString()); FileEventStreamSearch bsstart = new FileEventStreamSearch(path, pbinfo.getPositionOfFirstSample()); boolean startfound = bsstart.seekToTime(dbrtype, startSecondsIntoYear); long startPosition = 0; if(startfound) { startPosition = bsstart.getFoundPosition(); logger.debug("Found start position " + startPosition + " in file " + path.toAbsolutePath().toString()); LineByteStream lis = new LineByteStream(path, startPosition); if(startPosition == 0L) { lis.readLine(); } else { lis.seekToFirstNewLine(); } istreams[i] = new LineByteStreamCreator(lis, pvName, type); } else { logger.warn("Did not find start position in file " + path.toAbsolutePath().toString() + " for time " + TimeUtils.convertToISO8601String(startTime) + " using entire file"); istreams[i] = new LineByteStreamCreator(path, pvName, type); } } else { logger.debug("Start year in file " + pbinfo.getDataYear() + " is not the same as the start time " + startYTS.getYear() + " using entire file"); istreams[i] = new LineByteStreamCreator(path, pvName, type); } } else if (i == (paths.length-1)) { if(pbinfo.getDataYear() == endYTS.getYear()) { FileEventStreamSearch bsend = new FileEventStreamSearch(path, pbinfo.positionOfFirstSample); boolean endfound = bsend.seekToTime(dbrtype, endSecondsIntoYear); long endPosition = Files.size(path); if(endfound) { endPosition = bsend.getFoundPosition(); logger.debug("Found end position " + endPosition + " in file " + path.toAbsolutePath().toString()); LineByteStream lis = new LineByteStream(path, 0, endPosition); PBFileInfo.checkPayloadInfo(lis, pvName, type); istreams[i] = new LineByteStreamCreator(lis, pvName, type); } else { logger.warn("Did not find end position in file " + path.toAbsolutePath().toString() + " for time " + TimeUtils.convertToISO8601String(endTime) + " using entire file"); istreams[i] = new LineByteStreamCreator(path, pvName, type); } } else { logger.debug("End year in file " + pbinfo.getDataYear() + " is not the same as the end time " + endYTS.getYear() + " using entire file"); istreams[i] = new LineByteStreamCreator(path, pvName, type); } } else { // Use whole file for chunks in the middle. logger.debug("Using all the data from file " + path.toAbsolutePath().toString()); istreams[i] = new LineByteStreamCreator(path, pvName, type); } } catch(IOException ex) { throw new IOException("Exception processing file " + path.toAbsolutePath().toString(), ex); } } } @Override public Iterator<Event> iterator() { if(theIterator != null) { logger.error("We can only support one iterator per MultiFilePBEventStream. This one already has an iterator created."); return null; } try { theIterator = new MultiFilePBEventStreamIterator(istreams, this.pvName, this.desc.getYear(), this.type); return theIterator; } catch (IOException ex) { logger.error("Exception creating iterator", ex); return null; } } @Override public void close() throws IOException { for(LineByteStreamCreator lis : istreams) { lis.safeClose(); } } @Override public RemotableEventStreamDesc getDescription() { return desc; } }