org.yamcs.archive.YarchReplay Maven / Gradle / Ivy
package org.yamcs.archive;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.YamcsException;
import org.yamcs.archive.SpeedSpec.Type;
import org.yamcs.mdb.Mdb;
import org.yamcs.protobuf.Yamcs.EndAction;
import org.yamcs.protobuf.Yamcs.ReplayStatus;
import org.yamcs.protobuf.Yamcs.ReplayStatus.ReplayState;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.utils.parser.ParseException;
import org.yamcs.yarch.SqlBuilder;
import org.yamcs.yarch.Stream;
import org.yamcs.yarch.StreamSubscriber;
import org.yamcs.yarch.Tuple;
import org.yamcs.yarch.YarchDatabase;
import org.yamcs.yarch.YarchDatabaseInstance;
import org.yamcs.yarch.protobuf.Db.ProtoDataType;
import org.yamcs.yarch.streamsql.StreamSqlException;
/**
* Performs a replay from Yarch So far supported are: TM packets, PP groups, Events, Parameters and Command History.
*
* It relies on handlers for each data type. Each handler creates a stream, the streams are merged and the output is
* sent to the listener This class can also handle pause/resume: simply stop sending data seek: closes the streams and
* creates new ones with a different starting time.
*
* @author nm
*
*/
public class YarchReplay implements StreamSubscriber {
/**
* maximum time to wait if SPEED is ORIGINAL meaning that if there is a gap in the data longer than this, we
* continue)
*/
public final static long MAX_WAIT_TIME = 10000;
ReplayServer replayServer;
volatile String streamName;
volatile boolean quitting = false;
private volatile ReplayState state = ReplayState.INITIALIZATION;
static Logger log = LoggerFactory.getLogger(YarchReplay.class.getName());
private volatile String errorString = "";
final String instance;
static AtomicInteger counter = new AtomicInteger();
Mdb mdb;
volatile ReplayOptions currentRequest;
Map handlers;
private long lastDataSentTime = -1; // time when the last data has been sent
private long lastDataTime; // time of the last data
private Semaphore semaphore = new Semaphore(0);
boolean dropTuple = false; // set to true when jumping to a different time
volatile boolean ignoreClose;
volatile boolean sleeping;
ReplayListener listener;
volatile long replayTime;
public YarchReplay(ReplayServer replayServer, ReplayOptions rr, ReplayListener listener, Mdb mdb)
throws YamcsException {
this.listener = listener;
this.replayServer = replayServer;
this.mdb = mdb;
this.instance = replayServer.getYamcsInstance();
setRequest(rr);
}
private void setRequest(ReplayOptions req) throws YamcsException {
if (state != ReplayState.INITIALIZATION && state != ReplayState.STOPPED) {
throw new YamcsException("changing the request only supported in the INITIALIZATION and STOPPED states");
}
if (log.isDebugEnabled()) {
log.debug("Replay request for time: [{}, {}]",
(req.hasRangeStart() ? TimeEncoding.toString(req.getRangeStart()) : "-"),
(req.hasRangeStop() ? TimeEncoding.toString(req.getRangeStop()) : "-"));
}
if (req.hasRangeStart() && req.hasRangeStop() && req.getRangeStart() > req.getRangeStop()) {
log.warn("throwing new packetexception: stop time has to be greater than start time");
throw new YamcsException("stop has to be greater than start");
}
currentRequest = req;
handlers = new HashMap<>();
if (currentRequest.hasParameterRequest()) {
throw new YamcsException(
"The replay cannot handle directly parameters. Please create a replay processor for that");
}
if (currentRequest.hasEventRequest()) {
handlers.put(ProtoDataType.EVENT, new EventReplayHandler());
}
if (currentRequest.hasPacketRequest()) {
handlers.put(ProtoDataType.TM_PACKET, new XtceTmReplayHandler(mdb));
}
if (currentRequest.hasPpRequest()) {
handlers.put(ProtoDataType.PP, new ParameterReplayHandler(mdb));
}
if (currentRequest.hasCommandHistoryRequest()) {
handlers.put(ProtoDataType.CMD_HISTORY, new CommandHistoryReplayHandler(instance, mdb));
}
for (ReplayHandler rh : handlers.values()) {
rh.setRequest(req);
}
}
public ReplayState getState() {
return state;
}
public synchronized void start() {
switch (state) {
case RUNNING:
log.warn("start called when already running, call ignored");
return;
case INITIALIZATION:
case STOPPED:
try {
initReplay();
state = ReplayState.RUNNING;
} catch (Exception e) {
log.error("Got exception when creating the stream: ", e);
errorString = e.toString();
state = ReplayState.ERROR;
}
break;
case PAUSED:
state = ReplayState.RUNNING;
break;
case ERROR:
case CLOSED:
// do nothing?
}
}
private void initReplay() throws StreamSqlException, ParseException {
streamName = "replay_stream" + counter.incrementAndGet();
StringBuilder sb = new StringBuilder();
sb.append("CREATE STREAM " + streamName + " AS ");
if (handlers.size() > 1) {
sb.append("MERGE ");
}
List