/******************************************************************************* * 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.lang.reflect.Constructor; import java.nio.file.Path; import java.sql.Timestamp; import org.apache.log4j.Logger; import org.epics.archiverappliance.ByteArray; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.common.TimeUtils; import org.epics.archiverappliance.config.ArchDBRTypes; import org.epics.archiverappliance.data.DBRTimeEvent; import com.google.protobuf.InvalidProtocolBufferException; import edu.stanford.slac.archiverappliance.PB.data.DBR2PBTypeMapping; import edu.stanford.slac.archiverappliance.PB.data.PBParseException; import edu.stanford.slac.archiverappliance.PB.utils.LineByteStream; /** * An iterator for a FileBackedPBEventStream. * @author mshankar * */ public class FileBackedPBEventStreamTimeBasedIterator implements FileBackedPBEventStreamIterator { private static Logger logger = Logger.getLogger(FileBackedPBEventStreamTimeBasedIterator.class.getName()); private long startTimeEpochSeconds = 0; private long endTimeEpochSeconds = 0; private short year; private LineByteStream lbs = null; private ArchDBRTypes type; private DBR2PBTypeMapping mapping; private Constructor<? extends DBRTimeEvent> unmarshallingConstructor; Events events = new Events(); private class Events { private ByteArray line1 = new ByteArray(LineByteStream.MAX_LINE_SIZE); private Event event1 = null; private ByteArray line2 = new ByteArray(LineByteStream.MAX_LINE_SIZE); private Event event2 = null; void readEvents(LineByteStream lbs) throws Exception { if(event1 == null) { boolean done = false; while(!done) { try { lbs.readLine(line1); if(!line1.isEmpty()) { event1 = (Event) unmarshallingConstructor.newInstance(year, line1); long event1EpochSeconds = event1.getEpochSeconds(); done = true; if(event1EpochSeconds >= endTimeEpochSeconds) { event1 = null; line1.reset(); return; } } done = true; } catch(InvalidProtocolBufferException|PBParseException ex) { logger.error("InvalidProtocolBufferException|PBParseException processing PB event near " + lbs.getCurrentPosition() + "//0"); } } } if(event2 == null) { boolean done = false; while(!done) { try { lbs.readLine(line2); if(!line2.isEmpty()) { event2 = (Event) unmarshallingConstructor.newInstance(year, line2); long event2EpochSeconds = event2.getEpochSeconds(); done = true; if(event2EpochSeconds >= endTimeEpochSeconds) { event2 = null; line2.reset(); return; } } done = true; } catch(InvalidProtocolBufferException|PBParseException ex) { logger.error("InvalidProtocolBufferException|PBParseException processing PB event near " + lbs.getCurrentPosition() + "//1"); } } } } boolean startFound() { if(event1 != null && event2 != null) { long event1EpochSeconds = event1.getEpochSeconds(); long event2EpochSeconds = event2.getEpochSeconds(); if(event1EpochSeconds >= startTimeEpochSeconds) { logger.debug("We have reached an event whose start time is greater than the requested start already. Terminating the search."); return true; } if(event1EpochSeconds < startTimeEpochSeconds && event2EpochSeconds >= startTimeEpochSeconds) return true; return false; } if(event1 != null) { assert(event2 == null); long event1EpochSeconds = event1.getEpochSeconds(); logger.debug("Only one event found. As long as this is before the end time, we claim we found something."); if(event1EpochSeconds <= endTimeEpochSeconds) return true; return false; } return false; } Event popEvent() { Event previousEvent = event1; ByteArray previousByteArray = line1; if(event2 != null) { event1 = event2; line1 = line2; event2 = null; line2 = previousByteArray; } else { event1 = null; } return previousEvent; } boolean isEmpty() { return event1 == null; } void clear() { event1 = null; event2 = null; line1.reset(); line2.reset(); } } public FileBackedPBEventStreamTimeBasedIterator(Path path, Timestamp startTime, Timestamp endTime, short year, ArchDBRTypes type) throws IOException { this.startTimeEpochSeconds = TimeUtils.convertToEpochSeconds(startTime); this.endTimeEpochSeconds = TimeUtils.convertToEpochSeconds(endTime); this.type = type; mapping = DBR2PBTypeMapping.getPBClassFor(this.type); unmarshallingConstructor = mapping.getUnmarshallingFromByteArrayConstructor(); assert(startTimeEpochSeconds >= 0); assert(endTimeEpochSeconds >= 0); assert(endTimeEpochSeconds >= startTimeEpochSeconds); this.year = year; lbs = new LineByteStream(path); try { lbs.readLine(events.line1); // This should read the header.. events.readEvents(lbs); while(!events.isEmpty() && !events.startFound()) { events.popEvent(); events.readEvents(lbs); } } catch(Exception ex) { logger.error("Exception getting next event from path " + path.toString(), ex); events.clear(); } } @Override public boolean hasNext() { if(!events.isEmpty()) return true; try { events.readEvents(lbs); if(!events.isEmpty()) return true; } catch(Exception ex) { logger.error("Exception creating event object", ex); } return false; } @Override public Event next() { Event retVal = events.popEvent(); return retVal; } @Override public void remove() { throw new UnsupportedOperationException(); } public void close() { lbs.safeClose(); } }