package org.yamcs.web.rest.archive;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.api.MediaType;
import org.yamcs.archive.GPBHelper;
import org.yamcs.archive.XtceTmRecorder;
import org.yamcs.protobuf.Rest.ListPacketsResponse;
import org.yamcs.protobuf.SchemaRest;
import org.yamcs.protobuf.SchemaYamcs;
import org.yamcs.protobuf.Yamcs.TmPacketData;
import org.yamcs.web.HttpException;
import org.yamcs.web.InternalServerErrorException;
import org.yamcs.web.NotFoundException;
import org.yamcs.web.rest.RestHandler;
import org.yamcs.web.rest.RestRequest;
import org.yamcs.web.rest.RestRequest.IntervalResult;
import org.yamcs.web.rest.RestStreamSubscriber;
import org.yamcs.web.rest.RestStreams;
import org.yamcs.web.rest.Route;
import org.yamcs.web.rest.SqlBuilder;
import org.yamcs.yarch.Stream;
import org.yamcs.yarch.Tuple;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.handler.codec.http.HttpResponseStatus;
public class ArchivePacketRestHandler extends RestHandler {
private static final Logger log = LoggerFactory.getLogger(ArchivePacketRestHandler.class);
@Route(path = "/api/archive/:instance/packets/:gentime?", method = "GET")
public void listPackets(RestRequest req) throws HttpException {
String instance = verifyInstance(req, req.getRouteParam("instance"));
long pos = req.getQueryParameterAsLong("pos", 0);
int limit = req.getQueryParameterAsInt("limit", 100);
Set<String> nameSet = new HashSet<>();
for (String names : req.getQueryParameterList("name", Collections.emptyList())) {
for (String name : names.split(",")) {
nameSet.add(name.trim());
}
}
SqlBuilder sqlb = new SqlBuilder(XtceTmRecorder.TABLE_NAME);
IntervalResult ir = req.scanForInterval();
if (ir.hasInterval()) {
sqlb.where(ir.asSqlCondition("gentime"));
}
if (req.hasRouteParam("gentime")) {
sqlb.where("gentime = " + req.getDateRouteParam("gentime"));
}
if (!nameSet.isEmpty()) {
sqlb.where("pname in ('" + String.join("','", nameSet) + "')");
}
sqlb.descend(req.asksDescending(true));
if (req.asksFor(MediaType.OCTET_STREAM)) {
ByteBuf buf = req.getChannelHandlerContext().alloc().buffer();
ByteBufOutputStream bufOut = new ByteBufOutputStream(buf);
RestStreams.stream(instance, sqlb.toString(), new RestStreamSubscriber(pos, limit) {
@Override
public void processTuple(Stream stream, Tuple tuple) {
TmPacketData pdata = GPBHelper.tupleToTmPacketData(tuple);
try {
pdata.getPacket().writeTo(bufOut);
} catch (IOException e) {
log.warn("ignoring packet", e);
// should improve to somehow throw upwards
}
}
@Override
public void streamClosed(Stream stream) {
try {
bufOut.close();
completeOK(req, MediaType.OCTET_STREAM, buf);
} catch (IOException e) {
completeWithError(req, new InternalServerErrorException(e));
}
}
});
} else {
ListPacketsResponse.Builder responseb = ListPacketsResponse.newBuilder();
RestStreams.stream(instance, sqlb.toString(), new RestStreamSubscriber(pos, limit) {
@Override
public void processTuple(Stream stream, Tuple tuple) {
TmPacketData pdata = GPBHelper.tupleToTmPacketData(tuple);
responseb.addPacket(pdata);
}
@Override
public void streamClosed(Stream stream) {
completeOK(req, responseb.build(), SchemaRest.ListPacketsResponse.WRITE);
}
});
}
}
@Route(path = "/api/archive/:instance/packets/:gentime/:seqnum", method = "GET")
public void getPacket(RestRequest req) throws HttpException {
String instance = verifyInstance(req, req.getRouteParam("instance"));
long gentime = req.getDateRouteParam("gentime");
int seqNum = req.getIntegerRouteParam("seqnum");
SqlBuilder sqlb = new SqlBuilder(XtceTmRecorder.TABLE_NAME)
.where("gentime = " + gentime, "seqNum = " + seqNum);
List<TmPacketData> packets = new ArrayList<>();
RestStreams.stream(instance, sqlb.toString(), new RestStreamSubscriber(0, 2) {
@Override
public void processTuple(Stream stream, Tuple tuple) {
TmPacketData pdata = GPBHelper.tupleToTmPacketData(tuple);
packets.add(pdata);
}
@Override
public void streamClosed(Stream stream) {
if (packets.isEmpty()) {
sendRestError(req, HttpResponseStatus.NOT_FOUND, new NotFoundException(req, "No packet for id (" + gentime + ", " + seqNum + ")"));
} else if (packets.size() > 1) {
sendRestError(req, HttpResponseStatus.INTERNAL_SERVER_ERROR, new InternalServerErrorException("Too many results"));
} else {
completeOK(req, packets.get(0), SchemaYamcs.TmPacketData.WRITE);
}
}
});
}
}