package org.yamcs.ui;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
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.Commanding.CommandQueueEntry;
import org.yamcs.protobuf.Commanding.CommandQueueEvent;
import org.yamcs.protobuf.Commanding.CommandQueueEvent.Type;
import org.yamcs.protobuf.Commanding.CommandQueueInfo;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketExceptionData;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketSubscriptionData;
import org.yamcs.web.websocket.CommandQueueResource;
import io.netty.handler.codec.http.HttpMethod;
/**
* Controls yamcs command queues via Web
* Allows to register one CommandQueueListener per (instance, yprocessor)
*
* @author nm
*
*/
public class CommandQueueControlClient implements ConnectionListener, WebSocketClientCallback, WebSocketResponseHandler {
YamcsConnector yconnector;
List<CommandQueueListener> listeners=new CopyOnWriteArrayList<CommandQueueListener>(); //listeners for instance.chanelName
public CommandQueueControlClient(YamcsConnector yconnector) {
this.yconnector=yconnector;
yconnector.addConnectionListener(this);
}
public void addCommandQueueListener(CommandQueueListener cmdQueueListener) {
listeners.add(cmdQueueListener);
}
@Override
public void connected(String url) {
WebSocketRequest wsr = new WebSocketRequest(CommandQueueResource.RESOURCE_NAME, CommandQueueResource.OP_subscribe);
yconnector.performSubscription(wsr, this, this);
}
private void sendLog(String message) {
for(int i=0;i<listeners.size();i++) {
listeners.get(i).log(message);
}
}
/**
* Send a message to the server to change the queue state.
* If newState=ENABLED, then changing the queue state may result in some commands being sent.
*
* @param cqi command queue info
* @param rebuild The rebuild flag indicates that the command shall be rebuild (new timestamp, new pvt checks, etc) if the queue enabling results in commands being sent TODO
*/
public void setQueueState(CommandQueueInfo cqi, boolean rebuild) {
RestClient restClient = yconnector.getRestClient();
// PATCH /api/processors/:instance/:processor/cqueues/:name
String resource = "/processors/"+cqi.getInstance()+"/"+cqi.getProcessorName()+"/cqueues/"+cqi.getName()+"?state="+cqi.getState().toString();
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.PATCH);
cf.whenComplete((result, exception) -> {
if(exception!=null) {
sendLog("Exception setting queue state: "+exception.getMessage());
}
});
}
/**
* Send a message to the server to release the command
* @param cqe - reference to the command to be released
* @param rebuild - indicate that the binary shall be rebuilt (new timestamp, new pvt checks, etc)
* @throws YamcsException
*/
public void releaseCommand(CommandQueueEntry cqe, boolean rebuild) throws YamcsApiException, YamcsException {
//PATCH /api/processors/:instance/:processor/cqueues/:cqueue/entries/:uuid
RestClient restClient = yconnector.getRestClient();
String resource = "/processors/"+cqe.getInstance()+"/"+cqe.getProcessorName()+"/cqueues/"+cqe.getQueueName()+"/entries/"+cqe.getUuid()+"?state=released";
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.PATCH);
cf.whenComplete((result, exception) -> {
if(exception!=null) {
sendLog("Exception releasing command: "+exception.getMessage());
}
});
}
/**
* Send a message to the server to reject the command
* @param cqe
*/
public void rejectCommand(CommandQueueEntry cqe) {
//PATCH /api/processors/:instance/:processor/cqueues/:cqueue/entries/:uuid
RestClient restClient = yconnector.getRestClient();
String resource = "/processors/"+cqe.getInstance()+"/"+cqe.getProcessorName()+"/cqueues/"+cqe.getQueueName()+"/entries/"+cqe.getUuid()+"?state=rejected";
CompletableFuture<byte[]> cf = restClient.doRequest(resource, HttpMethod.PATCH);
cf.whenComplete((result, exception) -> {
if(exception!=null) {
sendLog("Exception releasing command: "+exception.getMessage());
}
});
}
@Override
public void connecting(String url) { }
@Override
public void connectionFailed(String url, YamcsException exception) {}
@Override
public void disconnected() {}
@Override
public void log(String message) {}
@Override
public void onMessage(WebSocketSubscriptionData data) {
if(data.hasCommandQueueInfo()) {
CommandQueueInfo cmdQueueInfo = data.getCommandQueueInfo();
for(CommandQueueListener cql: listeners) {
cql.updateQueue(cmdQueueInfo);
}
}
if(data.hasCommandQueueEvent()) {
CommandQueueEvent cmdQueueEvent = data.getCommandQueueEvent();
CommandQueueEntry cqe = cmdQueueEvent.getData();
Type eventType = cmdQueueEvent.getType();
for(CommandQueueListener cql: listeners) {
switch(eventType) {
case COMMAND_ADDED:
cql.commandAdded(cqe);
break;
case COMMAND_REJECTED:
cql.commandRejected(cqe);
break;
case COMMAND_SENT:
cql.commandSent(cqe);
break;
}
}
}
}
@Override
public void onException(WebSocketExceptionData e) {
/*
try {
String eventName=msg.getStringProperty(HDR_EVENT_NAME);
for(int i=0;i<listeners.size();i++) {
CommandQueueListener cql=listeners.get(i);
if("commandAdded".equals(eventName)) {
CommandQueueEntry cqe=(CommandQueueEntry)Protocol.decode(msg, CommandQueueEntry.newBuilder());
cql.commandAdded(cqe);
} else if("commandRejected".equals(eventName)) {
CommandQueueEntry cqe=(CommandQueueEntry)Protocol.decode(msg, CommandQueueEntry.newBuilder());
cql.commandRejected(cqe);
} else if("commandSent".equals(eventName)) {
CommandQueueEntry cqe=(CommandQueueEntry)Protocol.decode(msg, CommandQueueEntry.newBuilder());
cql.commandSent(cqe);
} else if("queueUpdated".equals(eventName)) {
CommandQueueInfo cqi=(CommandQueueInfo)Protocol.decode(msg, CommandQueueInfo.newBuilder());
listeners.get(i).updateQueue(cqi);
} else {
cql.log("received an unknown event on the command update queue: '"+eventName+"'");
}
}
} catch (YamcsApiException e) {
sendLog("error when decoding command queue info message: "+e);
}*/
}
}