package lcm.logging;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import lcm.util.*;
import lcm.lcm.*;
/**
* A class for reading and writing LCM log files.
*/
public class Log
{
BufferedRandomAccessFile raf;
static final int LOG_MAGIC = 0xEDA1DA01;
String path;
/** Used to count the number of messages written so far. **/
long numMessagesWritten = 0;
/**
* Represents a single received LCM message.
*/
public static class Event
{
/**
* Time of message receipt, represented in microseconds since 00:00:00
* UTC January 1, 1970.
*/
public long utime;
/**
* Event number assigned to the message in the log file.
*/
public long eventNumber;
/**
* Raw data bytes of the message body.
*/
public byte data[];
/**
* Channel on which the message was received.
*/
public String channel;
}
/**
* Opens a log file for reading or writing.
*
* @param path the filename to open
* @param mode Specifies the access mode, must be one of "r", "rw",
* "rws", or "rwd". See {@link java.io.RandomAccessFile#RandomAccessFile RandomAccessFile} for more detail.
*/
public Log(String path, String mode) throws IOException
{
this.path = path;
raf = new BufferedRandomAccessFile(path, mode);
//raf = new RandomAccessFile(path, mode);
}
/**
* Retrieves the path to the log file.
* @return the path to the log file
*/
public String getPath()
{
return path;
}
/**
* Flush any unwritten data to the underlying file descriptor.
**/
public void flush() throws IOException
{
raf.flush();
}
/**
* Reads the next event in the log file
*
* @throws java.io.EOFException if the end of the file has been reached.
*/
public synchronized Event readNext() throws IOException
{
int magic = 0;
Event e = new Event();
int channellen = 0, datalen = 0;
while (true)
{
int v = raf.readByte()&0xff;
magic = (magic<<8) | v;
if (magic != LOG_MAGIC)
continue;
e.eventNumber = raf.readLong();
e.utime = raf.readLong();
channellen = raf.readInt();
datalen = raf.readInt();
if (channellen <= 0 || datalen <= 0 || channellen >= 256 || datalen >= 2147483647) {
System.out.printf("Bad log event eventnumber = 0x%08x utime = 0x%08x channellen = 0x%08x datalen=0x%08x\n",
e.eventNumber, e.utime, channellen, datalen);
continue;
}
break;
}
byte bchannel[] = new byte[channellen];
e.data = new byte[datalen];
raf.readFully(bchannel);
e.channel = new String(bchannel);
raf.readFully(e.data);
return e;
}
public synchronized double getPositionFraction() throws IOException
{
return raf.getFilePointer()/((double) raf.length());
}
/**
* Seek to a position in the log file, specified by a fraction.
*
* @param frac a number in the range [0, 1)
*/
public synchronized void seekPositionFraction(double frac) throws IOException
{
raf.seek((long) (raf.length()*frac));
}
/**
* Writes an event to the log file. The user is responsible for
* filling in the eventNumber field, which should be sequentially
* increasing integers starting with 0.
*/
public synchronized void write(Event e) throws IOException
{
byte[] channelb = e.channel.getBytes();
raf.writeInt(LOG_MAGIC);
raf.writeLong(e.eventNumber);
raf.writeLong(e.utime);
raf.writeInt(channelb.length);
raf.writeInt(e.data.length);
raf.write(channelb, 0, channelb.length);
raf.write(e.data, 0, e.data.length);
}
/** A convenience method for write. It internally manages the
* eventNumber field, and so calls to this method should not be
* mixed with calls to the other write methods. **/
public synchronized void write(long utime, String channel, LCMEncodable msg) throws IOException
{
Log.Event le = new Log.Event();
le.utime = utime;
le.channel = channel;
LCMDataOutputStream outs = new LCMDataOutputStream();
msg.encode(outs);
le.data = outs.toByteArray();
le.eventNumber = numMessagesWritten;
write(le);
numMessagesWritten++;
}
/**
* Closes the log file and releases and system resources used by it.
*/
public synchronized void close() throws IOException
{
raf.close();
}
}