package org.yamcs.ui;
import java.io.IOException;
import java.io.StringWriter;
import java.util.concurrent.CompletableFuture;
import org.yamcs.YamcsException;
import org.yamcs.api.YamcsApiException;
import org.yamcs.api.rest.RestClient;
import org.yamcs.api.ws.ConnectionListener;
import org.yamcs.api.ws.WebSocketClientCallback;
import org.yamcs.api.ws.WebSocketRequest;
import org.yamcs.api.ws.WebSocketResponseHandler;
import org.yamcs.protobuf.Rest.CreateProcessorRequest;
import org.yamcs.protobuf.SchemaYamcs.ReplayRequest;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketExceptionData;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketSubscriptionData;
import org.yamcs.protobuf.Yamcs;
import org.yamcs.protobuf.YamcsManagement.ClientInfo;
import org.yamcs.protobuf.YamcsManagement.ClientInfo.ClientState;
import org.yamcs.protobuf.YamcsManagement.ProcessorInfo;
import org.yamcs.protobuf.YamcsManagement.ServiceState;
import org.yamcs.protobuf.YamcsManagement.Statistics;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.web.websocket.ManagementResource;
import io.netty.handler.codec.http.HttpMethod;
import io.protostuff.JsonIOUtil;
/**
* controls processors in yamcs server via websocket
*
* @author nm
*
*/
public class ProcessorControlClient implements ConnectionListener, WebSocketClientCallback, WebSocketResponseHandler {
YamcsConnector yconnector;
ProcessorListener yamcsMonitor;
public ProcessorControlClient(YamcsConnector yconnector) {
this.yconnector = yconnector;
yconnector.addConnectionListener(this);
}
public void setProcessorListener(ProcessorListener yamcsMonitor) {
this.yamcsMonitor=yamcsMonitor;
}
public void destroyProcessor(String name) throws YamcsApiException {
// TODO Auto-generated method stub
}
public CompletableFuture<byte[]> createProcessor(String instance, String name, String type, Yamcs.ReplayRequest spec, boolean persistent, int[] clients) throws YamcsException, YamcsApiException{
CreateProcessorRequest.Builder cprb = CreateProcessorRequest.newBuilder().setName(name).setType(type);
cprb.setPersistent(persistent);
for(int cid:clients) {
cprb.addClientId(cid);
}
if(spec!=null) {
StringWriter writer = new StringWriter();
try {
JsonIOUtil.writeTo(writer, spec, ReplayRequest.WRITE, false);
} catch (IOException e) {
throw new YamcsApiException("Error encoding the request to json", e);
}
cprb.setConfig(writer.toString());
}
RestClient restClient = yconnector.getRestClient();
//POST "/api/processors/:instance"
String resource = "/processors/"+instance;
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.POST, cprb.build().toByteArray());
cf.whenComplete((result, exception) -> {
if(exception!=null) {
yamcsMonitor.log("Exception creating processor: "+exception.getMessage());
}
});
return cf;
}
public CompletableFuture<Void> connectToProcessor(String instance, String processorName, int[] clients) throws YamcsException, YamcsApiException {
RestClient restClient = yconnector.getRestClient();
CompletableFuture<byte[]>[] cfs = new CompletableFuture[clients.length];
for(int i=0; i<clients.length;i++) {
//PATCH /api/clients/:id
String resource = "/clients/"+clients[i]+"?processor="+processorName+"&instance="+instance;
cfs[i] = restClient.doRequest(resource, HttpMethod.PATCH);
cfs[i].whenComplete((result, exception) -> {
if(exception!=null) {
yamcsMonitor.log("Exception connecting client to processor: "+exception.getMessage());
}
});
}
return CompletableFuture.allOf(cfs);
}
public void pauseArchiveReplay(String instance, String name) throws YamcsException, YamcsApiException {
RestClient restClient = yconnector.getRestClient();
// PATCH /api/processors/:instance/:name
String resource = "/processors/"+instance+"/"+name+"?state=PAUSED";
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.PATCH);
cf.whenComplete((result, exception) -> {
if(exception!=null) {
yamcsMonitor.log("Exception pauysing the processor: "+exception.getMessage());
}
});
}
public void resumeArchiveReplay(String instance, String name) throws YamcsApiException, YamcsException {
RestClient restClient = yconnector.getRestClient();
// PATCH /api/processors/:instance/:name
String resource = "/processors/"+instance+"/"+name+"?state=RUNNING";
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.PATCH);
cf.whenComplete((result, exception) -> {
if(exception!=null) {
yamcsMonitor.log("Exception resuming the processor: "+exception.getMessage());
}
});
}
public void seekArchiveReplay(String instance, String name, long newPosition) throws YamcsApiException, YamcsException {
RestClient restClient = yconnector.getRestClient();
// PATCH /api/processors/:instance/:name
String resource = "/processors/"+instance+"/"+name+"?seek="+TimeEncoding.toString(newPosition);
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.PATCH);
cf.whenComplete((result, exception) -> {
if(exception!=null) {
yamcsMonitor.log("Exception seeking the processor: "+exception.getMessage());
}
});
}
@Override
public void connecting(String url) { }
public void receiveInitialConfig() {
WebSocketRequest wsr = new WebSocketRequest(ManagementResource.RESOURCE_NAME, ManagementResource.OP_subscribe);
yconnector.performSubscription(wsr, this, this);
}
@Override
public void connected(String url) {
receiveInitialConfig();
}
@Override
public void onMessage(WebSocketSubscriptionData data) {
if(data.hasProcessorInfo()) {
ProcessorInfo procInfo = data.getProcessorInfo();
ServiceState servState = procInfo.getState();
if(servState==ServiceState.TERMINATED || servState==ServiceState.FAILED) {
yamcsMonitor.processorClosed(procInfo);
} else {
yamcsMonitor.processorUpdated(procInfo);
}
}
if(data.hasClientInfo()) {
ClientInfo cinfo = data.getClientInfo();
ClientState cstate = cinfo.getState();
if(cstate==ClientState.DISCONNECTED) {
yamcsMonitor.clientDisconnected(cinfo);
} else {
yamcsMonitor.clientUpdated(cinfo);
}
}
if(data.hasStatistics()) {
Statistics s = data.getStatistics();
yamcsMonitor.updateStatistics(s);
}
}
@Override
public void connectionFailed(String url, YamcsException exception) { }
@Override
public void disconnected() {
}
@Override
public void log(String message) {}
@Override
public void onException(WebSocketExceptionData e) {
yamcsMonitor.log("Exception when performing subscription:" +e.getMessage());
}
}