package lcm.lcm;
import java.io.*;
import lcm.logging.*;
public class LogFileProvider implements Provider
{
LCM lcm;
Log log;
double speed; // how fast do we play? <=0 for "as fast as possible"
double delay; // how many seconds to delay before starting to play? (crude race-condition hack)
boolean verbose; // report actual speed periodically
double skip; // skip a fraction of the log file [0, 1.0]
boolean writemode;
long nanotime_start;
long utime_start;
ReaderThread reader;
public LogFileProvider(LCM lcm, URLParser up) throws IOException
{
this.lcm = lcm;
String logPath = up.get("network","");
speed = up.get("speed", 1.0);
delay = up.get("delay", 0.5);
verbose = up.get("verbose", false);
skip = up.get("skip", 0.0); // skip this fraction of the log file.
writemode = up.get("mode", "r").equals("w");
if(writemode) {
log = new Log(logPath, "rw");
nanotime_start = System.nanoTime();
utime_start = System.currentTimeMillis() * 1000;
} else {
log = new Log(logPath, "r");
reader = new ReaderThread();
reader.start();
}
}
boolean publishWarned = false;
public synchronized void publish(String channel, byte data[], int offset, int length)
{
if(!writemode) {
if (publishWarned)
return;
System.err.println("LogFileProvider opened in read mode, no publishing allowed.");
publishWarned = true;
}
Log.Event event = new Log.Event();
event.utime = utime_start + (System.nanoTime() - nanotime_start) / 1000;
event.eventNumber = 0;
event.data = new byte[length];
System.arraycopy(data, offset, event.data, 0, length);
event.channel = channel;
try {
log.write(event);
} catch (IOException ex) {
System.err.println("ex: "+ex);
}
}
public synchronized void subscribe(String channel) { }
public void unsubscribe(String channel) { }
public synchronized void close() {
if (reader != null) {
reader.interrupt();
try {
reader.join();
} catch (InterruptedException ex) {
}
}
reader = null;
try {
log.close();
} catch (IOException ex) {
}
log = null;
}
class ReaderThread extends Thread
{
ReaderThread()
{
setDaemon(true);
}
public void run()
{
try {
runEx();
} catch (InterruptedException ex) {
return;
} catch (IOException ex) {
ex.printStackTrace();
}
}
void runEx() throws IOException, InterruptedException
{
log.seekPositionFraction(skip);
while (lcm.getNumSubscriptions()==0)
Thread.sleep(10);
Thread.sleep((int) (delay*1000));
long lastLocalUtime = System.nanoTime()/1000;
long lastEventUtime = -1;
double delayAccumulator = 0; // how long do we need to delay in real time?
double verboseAccumulator = 0;
long verboseLastEventUtime = -1;
while (true) {
Log.Event ev = log.readNext();
// how much time elapsed when the log was recorded?
// we have to wait some fraction of the time...
if (lastEventUtime > 0) {
double dt = (ev.utime - lastEventUtime) / 1000000.0;
if (dt > 0 && speed > 0)
delayAccumulator += dt / speed;
}
lastEventUtime = ev.utime;
// subtract any clock time that has elapsed.
long localUtime = System.nanoTime()/1000;
double dt = (localUtime - lastLocalUtime)/1000000.0;
lastLocalUtime = localUtime;
delayAccumulator -= dt;
verboseAccumulator += dt;
// spit out some info at 1Hz
if (verboseAccumulator > 1.0 && verbose) {
double eventDt = (lastEventUtime - verboseLastEventUtime)/1000000.0;
verboseLastEventUtime = lastEventUtime;
System.err.printf("LogFile: rate = %8.3f, position = %8.3f %%\n",
eventDt/verboseAccumulator,
log.getPositionFraction()*100.0);
verboseAccumulator = 0;
}
// sleep if necessary.
if (delayAccumulator > 0.001) {
int ms = (int) (delayAccumulator*1000);
Thread.sleep(ms);
}
// dispatch the message
lcm.receiveMessage(ev.channel, ev.data, 0, ev.data.length);
}
}
}
}