/******************************************************************************* * 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.Files; import java.nio.file.Path; import org.apache.log4j.Logger; import org.epics.archiverappliance.ByteArray; import org.epics.archiverappliance.config.ArchDBRTypes; import org.epics.archiverappliance.data.DBRTimeEvent; import edu.stanford.slac.archiverappliance.PB.EPICSEvent.PayloadInfo; import edu.stanford.slac.archiverappliance.PB.data.DBR2PBTypeMapping; import edu.stanford.slac.archiverappliance.PB.data.PBParseException; import edu.stanford.slac.archiverappliance.PB.utils.LineByteStream; import edu.stanford.slac.archiverappliance.PB.utils.LineEscaper; /** * Gets some information about PB files. * Important information includes the first and last event. * * @author mshankar * */ public class PBFileInfo { private static final Logger logger = Logger.getLogger(PBFileInfo.class); PayloadInfo info; DBRTimeEvent firstEvent = null; DBRTimeEvent lastEvent = null; long positionOfFirstSample = 0L; long positionOfLastSample = 0; public PBFileInfo(Path path) throws IOException { this(path, true); } public PBFileInfo(Path path, boolean lookupLastEvent) throws IOException { try(LineByteStream lis = new LineByteStream(path)) { byte[] payloadLine = LineEscaper.unescapeNewLines(lis.readLine()); info = PayloadInfo.parseFrom(payloadLine); logger.debug("PayloadInfo PVName: " + info.getPvname() + " is of type " + info.getType().name() + " and data is for the year " + info.getYear()); positionOfFirstSample = lis.getCurrentPosition(); // This is not strictly correct; but this will be adjusted below. positionOfLastSample = Files.size(path); ArchDBRTypes type = ArchDBRTypes.valueOf(info.getType()); Constructor<? extends DBRTimeEvent> unmarshallingConstructor = DBR2PBTypeMapping.getPBClassFor(type).getUnmarshallingFromByteArrayConstructor(); // Let's read the first line lis.seekToFirstNewLine(); byte[] firstLine = lis.readLine(); if(firstLine != null) { firstEvent = (DBRTimeEvent) unmarshallingConstructor.newInstance(getDataYear(), new ByteArray(firstLine)); if(lookupLastEvent) { // If we do not have a first line, we probably do not have a last line this.lookupLastEvent(path, lis, unmarshallingConstructor); } } else { logger.debug("File " + path.toAbsolutePath().toString() + " does not seem to have any first line?"); } } catch(Exception e) { logger.warn("Exception determing header information from file " + path.toAbsolutePath().toString(), e); throw new IOException(e); } } public String getPVName() { return info.getPvname(); } public short getDataYear() { return (short) info.getYear(); } public ArchDBRTypes getType() { return ArchDBRTypes.valueOf(info.getType()); } public PayloadInfo getInfo() { return info; } public DBRTimeEvent getFirstEvent() { return firstEvent; } public DBRTimeEvent getLastEvent() { return lastEvent; } public long getFirstEventEpochSeconds() { return (firstEvent != null) ? firstEvent.getEpochSeconds() : 0; } public long getLastEventEpochSeconds() { return (lastEvent != null) ? lastEvent.getEpochSeconds() : 0; } public long getPositionOfFirstSample() { return positionOfFirstSample; } public long getPositionOfLastSample() { return positionOfLastSample; } /** * Checks the payload info and makes sure we are using appropriate files. * This assumes that the lis is positioned at the start and subsequently positions the lis just past the first line. * So if we need to position the lis elsewhere, the caller needs to do that manually after this call. * @param lis The line bytes stream * @param pvName The PV name * @param type Enum ArchDBRTypes * @throws IOException   */ public static void checkPayloadInfo(LineByteStream lis, String pvName, ArchDBRTypes type) throws IOException { byte[] payloadLine = LineEscaper.unescapeNewLines(lis.readLine()); PayloadInfo info = PayloadInfo.parseFrom(payloadLine); if(!pvName.equals(info.getPvname())) { logger.error("File " + lis.getAbsolutePath() + " is being used to read data for pv " + pvName + " but it actually contains data for pv " + info.getPvname()); } if(!type.equals(ArchDBRTypes.valueOf(info.getType()))) { throw new IOException("File " + lis.getAbsolutePath() + " contains " + ArchDBRTypes.valueOf(info.getType()).toString() + " we are expecting " + type.toString()); } } private void lookupLastEvent(Path path, LineByteStream lis, Constructor<? extends DBRTimeEvent> unmarshallingConstructor) throws Exception { // If we do not have a first line, we probably do not have a last line lis.seekToBeforeLastLine(); long posn = lis.getCurrentPosition(); int lineTries = 0; byte[] lastLine = lis.readLine(); while(lastLine == null && posn > 0 && lineTries < 1000) { lis.seekToBeforePreviousLine(posn-2); posn = lis.getCurrentPosition(); lastLine = lis.readLine(); lineTries++; } int tries = 0; // Potential infinite loop here; we'll try about 1000 times while(lastEvent == null && lastLine != null && tries < 1000) { try { lastEvent = (DBRTimeEvent) unmarshallingConstructor.newInstance(getDataYear(), new ByteArray(lastLine)); lastEvent.getEventTimeStamp(); positionOfLastSample = posn; return; } catch(PBParseException ex) { logger.warn(path.toString() + " seems to have some data corruption at the end of the file; moving onto the previous line", ex); lastEvent = null; lis.seekToBeforePreviousLine(posn-2); posn = lis.getCurrentPosition(); lineTries = 0; lastLine = lis.readLine(); while(lastLine == null && posn > 0 && lineTries < 1000) { lis.seekToBeforePreviousLine(posn-2); posn = lis.getCurrentPosition(); lastLine = lis.readLine(); lineTries++; } } tries++; } logger.debug("File " + path.toAbsolutePath().toString() + " does not seem to have any last line?"); lastEvent = null; } }