lrgs.archive.MsgArchive Maven / Gradle / Ivy
Show all versions of opendcs Show documentation
/*
* $Id$
*
* This is open-source software written by ILEX Engineering, Inc., under
* contract to the federal government. You are free to copy and use this
* source code for your own purposes, except that no part of this source
* code may be claimed to be proprietary.
*
* Except for specific contractual terms between ILEX and the federal
* government, this source code is provided completely without warranty.
* For more information contact: [email protected]
*/
package lrgs.archive;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.TimeZone;
import decodes.util.ChannelMap;
import decodes.util.Pdt;
import decodes.util.PdtEntry;
import ilex.util.EnvExpander;
import ilex.util.IDateFormat;
import ilex.util.Logger;
import lrgs.common.DcpMsg;
import lrgs.common.DcpMsgFlag;
import lrgs.common.DcpMsgIndex;
import lrgs.common.DcpAddress;
import lrgs.common.LrgsErrorCode;
import lrgs.common.ArchiveUnavailableException;
import lrgs.common.SearchTimeoutException;
import lrgs.lrgsmain.JavaLrgsStatusProvider;
import lrgs.lrgsmain.LrgsConfig;
import lrgs.lrgsmain.LrgsInputInterface;
/**
Top-level archive for the DCS Toolkit.
*/
public class MsgArchive
implements MsgValidatee
{
/** Used for log messages. */
public static final String module = "Archive";
/** Event Num for bad index file. */
public static final int EVT_BAD_INDEX = 1;
/** Event Num for bad minute file. */
public static final int EVT_BAD_MINUTE_FILE = 2;
/** Event Num for bad archive. */
public static final int EVT_BAD_ARCHIVE = 3;
/** Event Num for hash file. */
public static final int EVT_BAD_HASH = 4;
/** The directory in which archive files are saved/found. */
private File archiveDir;
/** Each archive file will start with this name. */
public static final String namePrefix = "arc-";
public static final String lrgs6namePrefix = "arch-";
public static final String lrgs7namePrefix = "archv-";
/** Used to format & parse the date suffixes for archive files. */
public static SimpleDateFormat nameDateFormat
= new SimpleDateFormat("yyyyMMdd");
static
{
nameDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
/** Number of periods to keep (1 period = 1 index file). */
private int numPeriods;
/** IndexPtr entries hashed by DCP address. */
IndexPtrHash lastMsgHash = new IndexPtrHash();
/** time_t that the last message archive was called. */
// private int lastArchiveSec;
/** Vector of MsgPeriodArchive objects. */
private PeriodArchiveVec periodArchives;
/** The current period archive being written to. */
private MsgPeriodArchive currentArchive;
/** Used to periodically save hash, and cleanup cache. */
private CheckpointThread checkpointThread;
/** Merge Filter */
private MergeFilter mergeFilter;
/**
* Return value to search() when search is complete.
* This means that no indexes have been returned and no further calls should
* be made.
*/
public static final int SEARCH_RESULT_DONE = 1;
/**
* Return value to search() meaning to keep calling for more data.
* This means one or more indexes have been returned. When these
* are processed, call again for more.
*/
public static final int SEARCH_RESULT_MORE = 2;
/**
* Special case for real-time retrieval (no until time). No messages
* have been returned, and caller should pause before trying again.
*/
public static final int SEARCH_RESULT_PAUSE = 3;
/**
* The 45 second timeout was reached before any messages were found.
*/
public static final int SEARCH_RESULT_TIMELIMIT = 4;
/** We report the archive status to the status-provicer. */
private JavaLrgsStatusProvider statusProvider;
private DailyDomsatSeqMap todaySeqMap;
private DailyDomsatSeqMap yesterdaySeqMap;
private static final int SPD = 3600*24;
private MsgValidator validator = null;
private HashMap lastRealGoesTime = new HashMap();
/**
* Constructor -- set defaults.
* @param archiveDirName name of acrhive directory (may contain $env-var)
*/
public MsgArchive(String archiveDirName)
{
archiveDir = new File(EnvExpander.expand(archiveDirName));
setPeriodParams(31); // default is 31 day-files
periodArchives = new PeriodArchiveVec();
currentArchive = null;
mergeFilter = new MergeFilter(this);
statusProvider = null;
todaySeqMap = null;
yesterdaySeqMap = null;
}
/**
* Sets number of periods & period duration
* @param numPeriods number of periods (default = 31).
*/
public void setPeriodParams(int numPeriods)
{
this.numPeriods = numPeriods;
}
/**
* Sets the status provider.
* @param sp the status provider.
*/
public void setStatusProvider(JavaLrgsStatusProvider sp)
{
statusProvider = sp;
}
/**
* Called once after creation, and after loading configuration.
* Reload IndexPtr hashmap from disk.
* Construct a MsgPeriodArchive for each of numPeriods.
*/
public synchronized void init( )
throws InvalidArchiveException
{
Logger.instance().debug1(module + " MsgArchive.init()");
if (!archiveDir.isDirectory())
if (!archiveDir.mkdirs())
throw new InvalidArchiveException(
"Archive Directory '" + archiveDir.getPath()
+ "' does not exist and cannot be created.");
String files[] = archiveDir.list();
if (files == null) // means archiveDir is not a directory
throw new InvalidArchiveException("Archive Directory '"
+ archiveDir.getPath() + "' cannot be listed.");
int now = (int)(System.currentTimeMillis() / 1000L);
for(int i=0; i 0)
{
for(int i=0; i 0)
{
// Retrieve the index
MsgPeriodArchive mpa = getPeriodArchive(fileStartTime, false);
if (mpa == null || fileStartTime != mpa.startTime)
{
Logger.instance().debug1(
module + " DCP " + dcpAddr
+ " Inactive -- Fell off archive");
pdtEntry.active_flag = 'N';
return;
}
DcpMsgIndex idx = mpa.getIndexEntrySync(idxNum);
if (idx == null)
{
// Logger.instance().debug1(
// module + " Missing squelched -- invalid index ptr");
pdtEntry.active_flag = 'N';
return;
}
if (!dcpAddr.equals(idx.getDcpAddress()))
{
// Logger.instance().debug1(
// module + " Missing squelched DCP " + dcpAddr
// + " -- invalid dcp addr");
pdtEntry.active_flag = 'N';
return;
}
if (idx.getXmitTime().before(too_old))
{
// Logger.instance().debug1(
// module + " Missing squelched: DCP " + dcpAddr
// + " Inactive -- last msg > 48 hrs ago");
pdtEntry.active_flag = 'N';
return;
}
char fc = idx.getFailureCode();
if (fc == 'G' || fc == '?')
{
lastRealGoesTime.put(dcpAddr, idx.getXmitTime());
// Logger.instance().debug1(
// module + " DCP " + dcpAddr
// + " Missing - last msg received at "
// + idx.getXmitTime());
break; // We have a real msg within the last 48 hours!
}
fileStartTime = idx.getPrevFileThisDcp();
idxNum = idx.getPrevIdxNumThisDcp();
}
}
}
DcpMsg statmsg = makeStatMsg(msg, failureCode, explanation);
doArchiveMsg(statmsg, src, t);
}
/**
* This method does the actual archiving and merge-filtering.
* @param msg The message to archive
* @param src the input interface that is the source of the message
* @return true if message was archived, false if it didn't pass merge filter.
*/
private boolean doArchiveMsg(DcpMsg msg, LrgsInputInterface src, Date now)
{
int slot = src == null ? -1 : src.getSlot();
int nowTT = (int)(now.getTime() / 1000L);
msg.setLocalReceiveTime(now);
// Determine the DCP address, assign special constant if none.
DcpAddress addr = msg.getDcpAddress();
IndexPtr lastPtr = lastMsgHash.get(addr);
if (lastPtr == null)
{
lastPtr = new IndexPtr(0, 0, 0, 0, 0);
lastMsgHash.put(addr, lastPtr);
}
int mergeResult = MergeFilter.SAVE_DCPMSG;
msg.mergeFilterCode = 0;
int seqNum = msg.getSequenceNum();
IndexPtr origPtr = new IndexPtr(0, 0, 0, 0, 0);
int domsatTT = 0;
mergeResult = mergeFilter.getMergeResult(msg, lastPtr, origPtr);
msg.mergeFilterCode = mergeFilter.lastCode;
domsatTT = msg.getDomsatTime() == null ? 0
: (int)(msg.getDomsatTime().getTime()/1000L);
// Get ptr to previous msg from this DCP.
if (!msg.isGoesMessage())
{
Logger.instance().debug1(module +
(mergeResult == MergeFilter.DISCARD ? " NOT" : "")
+ " Archiving NON-GOES message for address '" + addr.toString()
+ "' from input slot "
+ slot + ", len=" + msg.getMsgLength() + ", timestamp="
+ msg.getDapsTime()+ ", failcode="
+ msg.getFailureCode() + ", lastPtr=" + lastMsgHash.get(addr)
+ ", mergeResult=" + mergeResult
+ ", mergeFilterCode=" + msg.mergeFilterCode
+ ", seqNum=" + msg.getSequenceNum());
}
if (mergeResult != MergeFilter.DISCARD)
{
lastPtr.indexNumber = currentArchive.archiveMsg(msg, lastPtr);
lastPtr.indexFileStartTime = currentArchive.startTime;
lastPtr.msgTime = (int)(msg.getDapsTime().getTime() / 1000L);
lastPtr.flagBits = msg.flagbits;
lastPtr.msgLength = msg.getMsgLength();
if (domsatTT > 0 && seqNum >= 0
&& (msg.flagbits & DcpMsgFlag.MSG_NO_SEQNUM) == 0)
{
//Logger.instance().info("MJM First mapping seq # " + seqNum);
if (domsatTT >= todaySeqMap.startTime
&& domsatTT < todaySeqMap.startTime + SPD)
todaySeqMap.add(domsatTT*1000L, seqNum,
(short)(domsatTT / SPD), lastPtr.indexNumber);
else if (domsatTT >= yesterdaySeqMap.startTime
&& domsatTT < yesterdaySeqMap.startTime + SPD)
yesterdaySeqMap.add(domsatTT*1000L, seqNum,
(short)(domsatTT / SPD), lastPtr.indexNumber);
}
// MJM 2011 11/16 If we get a real GOES msg, mark the pdt as active.
if (msg.isGoesMessage())
{
char failureCode = msg.getFailureCode();
if (failureCode == 'G' || failureCode == '?')
{
PdtEntry pe = Pdt.instance().find(msg.getDcpAddress());
if (pe != null)
pe.active_flag = 'Y';
}
}
}
else
{
// This msg WAS discarded. If this one HAS a domsat seqnum and
// the one we're replacing DOES NOT have one. THEN store this
// seqnum refering to the msg already in storage.
// Logger.instance().debug3(module +
// " Discarding duplicate or redundant msg from " + addr);
if (domsatTT > 0 && seqNum >= 0
&& (msg.flagbits & DcpMsgFlag.MSG_NO_SEQNUM) == 0)
{
if ((origPtr.flagBits & DcpMsgFlag.MSG_NO_SEQNUM) != 0)
{
// Logger.instance().info("MJM RE mapping seq # " + seqNum +
// " because previously-saved msg does NOT have seq#. this.flags=0x"
// + Integer.toHexString(msg.flagbits) + ", prev.flags=0x"
// + Integer.toHexString(origPtr.flagBits));
short idxDayNum = (short)(origPtr.indexFileStartTime / SPD);
if (domsatTT >= todaySeqMap.startTime
&& domsatTT < todaySeqMap.startTime + SPD)
todaySeqMap.add(domsatTT*1000L, seqNum,
idxDayNum, origPtr.indexNumber);
else if (domsatTT >= yesterdaySeqMap.startTime
&& domsatTT < yesterdaySeqMap.startTime + SPD)
yesterdaySeqMap.add(domsatTT*1000L, seqNum,
idxDayNum, origPtr.indexNumber);
// Now mark the old message as HAVING a sequence num.
origPtr.flagBits &= (~DcpMsgFlag.MSG_NO_SEQNUM);
MsgPeriodArchive mpa =
getPeriodArchive(origPtr.indexFileStartTime, false);
if (mpa != null)
{
mpa.addDomsatSequence(origPtr.indexNumber,
seqNum, domsatTT*1000L);
}
}
else
{
// Logger.instance().info("MJM NOT mapping seq # " + seqNum +
// " because prev-saved msg already has seq#. this.flags=0x"
// + Integer.toHexString(msg.flagbits) + ", prev.flags=0x"
// + Integer.toHexString(origPtr.flagBits));
}
}
}
if (slot != -1)
statusProvider.receivedMsg(slot, nowTT, msg.getFailureCode(),
msg.getSequenceNum(),
mergeResult != MergeFilter.DISCARD ? lastPtr.indexNumber : -1,
mergeResult);
return mergeResult != MergeFilter.DISCARD;
}
/**
* A synchronized wrapper around checkCurrentArchive so it can be called
* from LrgsMain at the beginning of every day.
* This ensures that the new archive is created at the start of every day
* even if no messages are coming in.
*/
public synchronized void doCheckCurrentArchive()
{
checkCurrentArchive((int)(System.currentTimeMillis()/1000L));
}
/**
* Use current time and lastArchiveSec to determine if it's time to
* start a new MsgPeriodArchive. If so, do it.
*/
private void checkCurrentArchive(int now)
{
if (currentArchive == null
|| now >= currentArchive.startTime + MsgPeriodArchive.periodDuration)
{
int startTime = (now / MsgPeriodArchive.periodDuration)
* MsgPeriodArchive.periodDuration;
String arcRootPath = archiveDir.getPath() + File.separator
+ lrgs7namePrefix
+ nameDateFormat.format(new Date(startTime*1000L));
try
{
if (currentArchive != null)
{
lastMsgHash.saveIndexPtrHash(currentArchive.getRootPath()
+ MsgPeriodArchive.IHASH_EXT, this);
currentArchive.finish();
}
currentArchive =
new MsgPeriodArchive(arcRootPath, startTime, true);
Logger.instance().info("Adding log with root path '"
+ arcRootPath + "'");
periodArchives.add(currentArchive);
}
catch(IOException ioex)
{
Logger.instance().failure(module + ":" + EVT_BAD_ARCHIVE
+ "- Cannot create archive at '"
+ arcRootPath + "': " + ioex);
currentArchive = null;
}
}
if (todaySeqMap == null)
{
todaySeqMap = new DailyDomsatSeqMap(currentArchive.startTime, this);
yesterdaySeqMap = new DailyDomsatSeqMap(
currentArchive.startTime - (3600 * 24), this);
}
else if (currentArchive.startTime > todaySeqMap.startTime)
{
Logger.instance().info(module
+ " Rotating DOMSAT Sequence Map");
yesterdaySeqMap = todaySeqMap;
todaySeqMap = new DailyDomsatSeqMap(currentArchive.startTime, this);
}
}
/**
* Returns the specific MsgPeriodArchive that would contain the first
* message with a time equal or greater than the passed time.
* @param sinceSec Unix time_t of the message time.
* @param earliest true to return earliest period if sinceSec is before
* all periods.
* @return period archive containing time, or null if not found.
*/
public MsgPeriodArchive getPeriodArchive(int sinceSec,
boolean earliest)
{
// Look in the currentArchive first
if (currentArchive != null
&& sinceSec >= currentArchive.startTime)
return currentArchive;
return periodArchives.getPeriodArchive(sinceSec, earliest);
}
/**
* Initialize a search & return a new handle.
* There are two algorithms:
*
* 1. SM_INDEXSEARCH: seek to starting minute in the period specified
* by SINCE time and read forward to the UNTIL time, reading and
* testing every index.
*
* 2. SM_BACKREF: For each DCP specified, find the most recent message,
* then seek backward through the linked list of indexes.
*
* The first choice is better if there are many DCPs or if we are searching
* for a time range that occurred far in the past.
*/
public SearchHandle startSearch( MsgFilter filter )
{
SearchHandle handle = new SearchHandle(filter);
// The formula is:
// if (n * 12 * (r/1440) * t < m) then SM_BACKREF, else INDEXSEARCH
// where
// n = number of DCPs requested
// 12 : Assume 12 messages per DCP per Day on average
// r = complete range in minutes: now - since
// 1440 = minutes per day
// t = time coefficient: ratio of how long it takes to read
// a single index to reading a 1000-index buffer full.
// m = minutes requested = until - since
DcpAddress[] addresses = filter.getDcpAddresses();
int n = addresses == null ? 0 : addresses.length;
Date until = filter.getUntilTime();
Date since = filter.getSinceTime();
// If no DCPs specified, or if this is a real-time retrieval,
// Then we have to use INDEXSEARCH
if (n == 0 || until == null || filter.forceAscending())
{
handle.searchMethod = SearchHandle.SM_INDEXSEARCH;
startIndexSearch(handle);
return handle;
}
// Compute pointer read range as a number of minutes.
long now = System.currentTimeMillis();
long sincel = (since == null) ? now - (numPeriods*24*60*60*1000L)
: since.getTime();
int r = (int)((now - sincel) / 60000L);
double t = .5;
// Compute m = Number of minutes in since ... until
// This will be the approx # of reads for an index search.
int m = (int)((until.getTime() - sincel) / 60000L);
// Weighted approx # read for a pointer search:
int wnrp = (int)(n * 12 * (r/1440.0) * t);
Logger.instance().debug2(module + " startSearch: n=" + n + ", t=" + t
+ ", r=" + r + ", m=" + m
+ ", n * 12 * (r/1440.0) * t = " + wnrp);
if (wnrp < m)
{
Logger.instance().debug2(module
+ " Starting BACKWARD POINTER Search");
handle.searchMethod = SearchHandle.SM_BACKREF;
}
else
{
Logger.instance().debug2(module + " Starting FORWARD INDEX Search");
handle.searchMethod = SearchHandle.SM_INDEXSEARCH;
startIndexSearch(handle);
}
return handle;
}
/**
* Initializes the handle for a forward-index search.
* Find the staring period index and call it to set the starting minute.
* @param handle the handle to initialize
*/
private void startIndexSearch(SearchHandle handle)
{
Date since = handle.filter.getSinceTime();
int sinceSec = since == null ? 0 : (int)(since.getTime() / 1000L);
//String msg = module + " MsgArchive.startIndexSearch sinceSec="
//+ sinceSec + " since='" + since + "'";
//Logger.instance().debug1(msg);
MsgPeriodArchive mpa = getPeriodArchive(sinceSec, true);
if (mpa == null)
handle.periodStartTime = 0;
else
{
handle.periodStartTime = mpa.startTime;
mpa.startIndexSearch(handle);
}
}
/**
* Search for the next batch of max messages.
* Place retrieved indexes (each containing a message)
* In the array stored in the passed handle.
*
* @return SEARCH_RESULT_DONE, SEARCH_RESULT_MORE, SEARCH_RESULT_PAUSE,
* or SEARCH_RESULT_TIMELIMIT
* @throws ArchiveUnavailableException if can't init search criteria
* @throws SearchTimeoutException if searchStopMsec reached with no results.
*/
public int search(SearchHandle handle, long stopSearchMsec)
throws ArchiveUnavailableException, SearchTimeoutException
{
long start = System.currentTimeMillis();
if (handle.searchMethod == SearchHandle.SM_INDEXSEARCH)
{
//Logger.instance().info("Doing SM_INDEXSEARCH");
MsgPeriodArchive mpa =
getPeriodArchive(handle.periodStartTime, true);
if (mpa == null)
{
String msg = "No archive for start time="
+ IDateFormat.time_t2string(handle.periodStartTime);
Logger.instance().warning(module + " " + msg);
throw new ArchiveUnavailableException(msg,
LrgsErrorCode.DBADSINCE);
}
try
{
int result;
while((result = mpa.searchIndex(handle, stopSearchMsec))
== MsgArchive.SEARCH_RESULT_MORE
&& handle.capacity() > 0)
{
mpa = getPeriodArchive(handle.periodStartTime, true);
}
//if (handle.filter.getClientName().contains("verizon.net"))
//Logger.instance().info("SM_INDEXSEARCH returning result " + result
//+ ", handle.idxBufFillLength=" + handle.idxBufFillLength
//+ ", handle.nextIdxBufNum=" + handle.nextIdxBufNum);
return result;
}
catch(ArchiveUnavailableException ex)
{
Logger.instance().warning(module + " Corrupt period "
+ mpa.myname);
throw ex;
}
}
else if (handle.searchMethod == SearchHandle.SM_BACKREF)
{
return doBackRefSearch(handle, start);
}
throw new ArchiveUnavailableException("No Such Search Algorithm",
LrgsErrorCode.DDDSINTERNAL);
}
/**
* Internal method to implement the backward pointer reference search.
*/
private int doBackRefSearch(SearchHandle handle, long searchStart)
throws ArchiveUnavailableException, SearchTimeoutException
{
// client name is hostname plus unique numeric ID.
//boolean isTestClient = handle.filter.getClientName().contains("verizon.net");
DcpAddress[] addresses = handle.filter.getDcpAddresses();
Date since = handle.filter.getSinceTime();
int sinceTT = since == null ? 0 : (int)(since.getTime() / 1000L);
MsgPeriodArchive mpa = null;
long searchEnd = searchStart + 45000L;
//if (isTestClient)
//Logger.instance().info("doBackRefSearch: addresses.length = "
//+ addresses.length + ", curaddr=" + handle.curaddr);
while(handle.curaddr < addresses.length
&& handle.capacity() > 0
&& System.currentTimeMillis() < searchEnd)
{
// nextIndexNum == -1 means to start the next address.
if (handle.nextIndexNum == -1)
{
if (++handle.curaddr < addresses.length)
{
DcpAddress addr = addresses[handle.curaddr];
//if (isTestClient)
//Logger.instance().info(module
//+ " Starting ptr search for '" + addr + "'");
IndexPtr idxPtr = lastMsgHash.get(addr);
if (idxPtr != null)
{
handle.periodStartTime = idxPtr.indexFileStartTime;
handle.nextIndexNum = idxPtr.indexNumber;
//if (isTestClient)
//Logger.instance().info(module + " periodStartTime=" + handle.periodStartTime
//+ ", indexNum=" + handle.nextIndexNum);
}
else
Logger.instance().debug1(
module + " No idxPtr entry for '" + addr + "'");
}
continue;
}
if (mpa == null || handle.periodStartTime != mpa.startTime)
{
if (handle.periodStartTime == 0
|| (mpa = getPeriodArchive(handle.periodStartTime, false))
== null
|| handle.periodStartTime != mpa.startTime)
{
handle.nextIndexNum = -1; // Done with this address.
//if (isTestClient)
//Logger.instance().debug1(module + " done with address '"
//+ addresses[handle.curaddr] + "'");
continue;
}
}
//if (isTestClient)
//Logger.instance().info(module + " Retrieving index "
//+ handle.nextIndexNum + " in file " + mpa.myname);
DcpMsgIndex dmi = mpa.getIndexEntrySync(handle.nextIndexNum);
if (dmi == null)
{
Logger.instance().warning(
"Corrupt pointer for dcp '"
+ addresses[handle.curaddr].toString() + "' in archive "
+ mpa.myname + "' index num=" + handle.nextIndexNum);
handle.nextIndexNum = -1; // Skip rest of this address.
continue;
}
if (dmi.getXmitTime().getTime()/1000 < sinceTT)
{
//if (isTestClient)
//Logger.instance().info("doBackRefSearch stopped because time before sinceTT");
handle.nextIndexNum = -1; // Done with this address.
continue;
}
if ((dmi.getFlagbits() & DcpMsgFlag.MSG_DELETED) == 0
&& handle.filter.passes(dmi))
{
mpa.readMessage(dmi);
handle.addIndex(dmi);
}
//else if (isTestClient)
//Logger.instance().info("doBackRefSearch not including following index because it doesn't pass crit: " + dmi.toString());
handle.periodStartTime = dmi.getPrevFileThisDcp();
handle.nextIndexNum = dmi.getPrevIdxNumThisDcp();
}
//if (isTestClient)
//Logger.instance().info("doBackRefSearch loop stopped: curaddr="
//+handle.curaddr + ", addresses.length=" + addresses.length
//+", capacity=" + handle.capacity()
//+ ", curtime=" + System.currentTimeMillis()
//+ ",searchEnd=" + searchEnd);
if (!handle.isEmpty() || handle.curaddr < addresses.length)
return SEARCH_RESULT_MORE;
else
return SEARCH_RESULT_DONE;
}
/**
* Called periodically to checkpoint certain transient info to the disk.
*/
public void checkpoint()
{
Logger.instance().debug3(module + " MsgArchive.checkpoint()");
lastMsgHash.saveIndexPtrHash(archiveDir + "/index-hash", this);
int startTimeCutoff =
(int)(System.currentTimeMillis() / 1000L)
- (numPeriods * MsgPeriodArchive.periodDuration);
String cutoffStr = nameDateFormat.format(
new Date(startTimeCutoff*1000L));
periodArchives.checkpointAll(startTimeCutoff, cutoffStr);
}
/**
* @return total number of messages in all archives.
*/
public int getTotalMessageCount()
{
return periodArchives.getTotalMessageCount();
}
/**
* @return Unix time_t message time stamp of oldest message.
*/
public int getOldestDapsTime()
{
return periodArchives.getOldestDapsTime();
}
/**
* Retrieve messages by DOMSAT sequence number range.
* @param approxDomsatTime approximate domsat time of outage.
* @param seqStart first missing sequence number
* @param seqEnd last missing sequence number
* @param msgs return messages by storing them here
* @return number of messages stored.
*/
public int getMsgsBySeqNum(long fromDomsatTime, long untilDomsatTime,
int seqStart, int seqEnd, ArrayList msgs)
throws ArchiveUnavailableException
{
int r = 0;
int tt = (int)(fromDomsatTime / 1000L) - 15;
if (yesterdaySeqMap != null
&& tt >= yesterdaySeqMap.startTime
&& tt < yesterdaySeqMap.startTime + SPD)
r += yesterdaySeqMap.getMsgsBySeqNum(fromDomsatTime, untilDomsatTime,
seqStart, seqEnd, msgs);
tt = (int)(untilDomsatTime / 1000L) + 15;
if (todaySeqMap != null
&& tt >= todaySeqMap.startTime
&& tt < todaySeqMap.startTime + SPD)
r += todaySeqMap.getMsgsBySeqNum(fromDomsatTime, untilDomsatTime,
seqStart, seqEnd, msgs);
return r;
}
/**
* Make a DAPS Status Message.
* @param origMsg the original real DCP message.
* @param failcode the failure code to use.
* @param expl and explanation of the failure placed in msg body.
*/
private DcpMsg makeStatMsg(DcpMsg origMsg, char failcode, String expl)
{
byte newdata[] = new byte[37 + expl.length()];
byte origData[] = origMsg.getData();
for(int i=0; i<32; i++)
newdata[i] = origData[i];
newdata[DcpMsg.IDX_FAILCODE] = (byte)failcode;
byte[] explb = expl.getBytes();
for(int i=0; i