package org.yamcs.web.websocket; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.ProcessorException; import org.yamcs.Processor; import org.yamcs.commanding.CommandQueue; import org.yamcs.commanding.CommandQueueListener; import org.yamcs.commanding.CommandQueueManager; import org.yamcs.commanding.PreparedCommand; import org.yamcs.management.ManagementGpbHelper; import org.yamcs.management.ManagementService; 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.SchemaCommanding; import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketReplyData; import org.yamcs.protobuf.Yamcs.ProtoDataType; /** * Provides realtime command queue subscription via web. */ public class CommandQueueResource extends AbstractWebSocketResource implements CommandQueueListener { private static final Logger log = LoggerFactory.getLogger(CommandQueueResource.class); public static final String RESOURCE_NAME = "cqueues"; public static final String OP_subscribe = "subscribe"; public static final String OP_unsubscribe = "unsubscribe"; public CommandQueueResource(WebSocketProcessorClient client) { super(client); } @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 { try { WebSocketReplyData reply = toAckReply(requestId); wsHandler.sendReply(reply); doSubscribe(); return null; } catch (IOException e) { log.error("Exception when sending data", e); return null; } } private WebSocketReplyData unsubscribe(int requestId) throws WebSocketException { doUnsubscribe(); return toAckReply(requestId); } @Override public void quit() { doUnsubscribe(); } private void doSubscribe() { ManagementService mservice = ManagementService.getInstance(); CommandQueueManager cqueueManager = mservice.getCommandQueueManager(processor); if (cqueueManager != null) { cqueueManager.registerListener(this); for (CommandQueue q : cqueueManager.getQueues()) { sendInitialUpdateQueue(q); } } } private void doUnsubscribe() { ManagementService mservice = ManagementService.getInstance(); CommandQueueManager cqueueManager = mservice.getCommandQueueManager(processor); if (cqueueManager != null) { cqueueManager.removeListener(this); } } @Override public void switchProcessor(Processor oldProcessor, Processor newProcessor) throws ProcessorException { doUnsubscribe(); super.switchProcessor(oldProcessor, newProcessor); doSubscribe(); } /** * right after subcription send the full queeue content (commands included). * Afterwards the clinets get notified by command added/command removed when the queue gets modified. * * @param q */ private void sendInitialUpdateQueue(CommandQueue q) { CommandQueueInfo info = ManagementGpbHelper.toCommandQueueInfo(q, true); try { wsHandler.sendData(ProtoDataType.COMMAND_QUEUE_INFO, info, SchemaCommanding.CommandQueueInfo.WRITE); } catch (Exception e) { log.warn("got error when sending command queue info, quitting", e); quit(); } } @Override public void updateQueue(CommandQueue q) { CommandQueueInfo info = ManagementGpbHelper.toCommandQueueInfo(q, false); try { wsHandler.sendData(ProtoDataType.COMMAND_QUEUE_INFO, info, SchemaCommanding.CommandQueueInfo.WRITE); } catch (Exception e) { log.warn("got error when sending command queue info, quitting", e); quit(); } } @Override public void commandAdded(CommandQueue q, PreparedCommand pc) { CommandQueueEntry data = ManagementGpbHelper.toCommandQueueEntry(q, pc); try { CommandQueueEvent.Builder evtb = CommandQueueEvent.newBuilder(); evtb.setType(Type.COMMAND_ADDED); evtb.setData(data); wsHandler.sendData(ProtoDataType.COMMAND_QUEUE_EVENT, evtb.build(), SchemaCommanding.CommandQueueEvent.WRITE); } catch (Exception e) { log.warn("got error when sending command queue event, quitting", e); quit(); } } @Override public void commandRejected(CommandQueue q, PreparedCommand pc) { CommandQueueEntry data = ManagementGpbHelper.toCommandQueueEntry(q, pc); try { CommandQueueEvent.Builder evtb = CommandQueueEvent.newBuilder(); evtb.setType(Type.COMMAND_REJECTED); evtb.setData(data); wsHandler.sendData(ProtoDataType.COMMAND_QUEUE_EVENT, evtb.build(), SchemaCommanding.CommandQueueEvent.WRITE); } catch (Exception e) { log.warn("got error when sending command queue event, quitting", e); quit(); } } @Override public void commandSent(CommandQueue q, PreparedCommand pc) { CommandQueueEntry data = ManagementGpbHelper.toCommandQueueEntry(q, pc); try { CommandQueueEvent.Builder evtb = CommandQueueEvent.newBuilder(); evtb.setType(Type.COMMAND_SENT); evtb.setData(data); wsHandler.sendData(ProtoDataType.COMMAND_QUEUE_EVENT, evtb.build(), SchemaCommanding.CommandQueueEvent.WRITE); } catch (Exception e) { log.warn("got error when sending command queue event, quitting", e); quit(); } } }