package org.yamcs.web.websocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.ProcessorException;
import org.yamcs.Processor;
import org.yamcs.archive.EventRecorder;
import org.yamcs.protobuf.SchemaYamcs;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketReplyData;
import org.yamcs.protobuf.Yamcs.Event;
import org.yamcs.protobuf.Yamcs.ProtoDataType;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.yarch.Stream;
import org.yamcs.yarch.StreamSubscriber;
import org.yamcs.yarch.Tuple;
import org.yamcs.yarch.YarchDatabase;
/**
* Provides realtime event subscription via web.
*/
public class EventResource extends AbstractWebSocketResource {
private static final Logger log = LoggerFactory.getLogger(EventResource.class);
public static final String RESOURCE_NAME = "events";
public static final String OP_subscribe = "subscribe";
public static final String OP_unsubscribe = "unsubscribe";
private Stream stream;
private StreamSubscriber streamSubscriber;
public EventResource(WebSocketProcessorClient client) {
super(client);
YarchDatabase ydb = YarchDatabase.getInstance(processor.getInstance());
stream = ydb.getStream(EventRecorder.REALTIME_EVENT_STREAM_NAME);
}
@Override
public WebSocketReplyData processRequest(WebSocketDecodeContext ctx, WebSocketDecoder decoder) throws WebSocketException {
switch (ctx.getOperation()) {
case OP_subscribe:
return subscribe(ctx.getRequestId());
case OP_unsubscribe:
return unsubscribe(ctx.getRequestId());
default:
throw new WebSocketException(ctx.getRequestId(), "Unsupported operation '"+ctx.getOperation()+"'");
}
}
private WebSocketReplyData subscribe(int requestId) throws WebSocketException {
doUnsubscribe(); // Only one subscription at a time
doSubscribe();
return toAckReply(requestId);
}
@Override
public void switchProcessor(Processor oldProcessor, Processor newProcessor) throws ProcessorException {
doUnsubscribe();
super.switchProcessor(oldProcessor, newProcessor);
YarchDatabase ydb = YarchDatabase.getInstance(processor.getInstance());
stream = ydb.getStream(EventRecorder.REALTIME_EVENT_STREAM_NAME);
doSubscribe();
}
private WebSocketReplyData unsubscribe(int requestId) throws WebSocketException {
doUnsubscribe();
return toAckReply(requestId);
}
@Override
public void quit() {
doUnsubscribe();
}
private void doSubscribe() {
if (stream != null) {
streamSubscriber = new StreamSubscriber() {
@Override
public void onTuple(Stream stream, Tuple tuple) {
try {
Event event = (Event) tuple.getColumn("body");
event = Event.newBuilder(event)
.setGenerationTimeUTC(TimeEncoding.toString(event.getGenerationTime()))
.setReceptionTimeUTC(TimeEncoding.toString(event.getReceptionTime()))
.build();
wsHandler.sendData(ProtoDataType.EVENT, event, SchemaYamcs.Event.WRITE);
} catch (Exception e) {
log.warn("got error when sending event, quitting", e);
quit();
}
}
@Override
public void streamClosed(Stream stream) {
}
};
stream.addSubscriber(streamSubscriber);
}
}
private void doUnsubscribe() {
if (streamSubscriber != null) {
stream.removeSubscriber(streamSubscriber);
}
streamSubscriber = null;
}
}