package org.yamcs.web.rest.processor; import java.util.ArrayList; import java.util.List; import org.yamcs.ErrorInCommand; import org.yamcs.NoPermissionException; import org.yamcs.Processor; import org.yamcs.YamcsException; import org.yamcs.commanding.CommandQueue; import org.yamcs.commanding.CommandQueueManager; import org.yamcs.commanding.PreparedCommand; import org.yamcs.management.ManagementGpbHelper; import org.yamcs.protobuf.Commanding; import org.yamcs.protobuf.Rest; import org.yamcs.protobuf.Rest.IssueCommandRequest; import org.yamcs.protobuf.Rest.IssueCommandRequest.Assignment; import org.yamcs.protobuf.Rest.IssueCommandResponse; import org.yamcs.protobuf.SchemaRest; import org.yamcs.security.InvalidAuthenticationToken; import org.yamcs.utils.StringConverter; import org.yamcs.web.BadRequestException; import org.yamcs.web.ForbiddenException; import org.yamcs.web.HttpException; import org.yamcs.web.InternalServerErrorException; import org.yamcs.web.rest.RestHandler; import org.yamcs.web.rest.RestRequest; import org.yamcs.web.rest.Route; import org.yamcs.xtce.*; import org.yamcs.xtceproc.XtceDbFactory; import com.google.protobuf.ByteString; import org.yaml.snakeyaml.util.UriEncoder; /** * Processes command requests */ public class ProcessorCommandRestHandler extends RestHandler { @Route(path = "/api/processors/:instance/:processor/commands/:name*", method = "POST") public void issueCommand(RestRequest req) throws HttpException { Processor processor = verifyProcessor(req, req.getRouteParam("instance"), req.getRouteParam("processor")); if (!processor.hasCommanding()) { throw new BadRequestException("Commanding not activated for this processor"); } String requestCommandName = UriEncoder.decode(req.getRouteParam("name")); XtceDb mdb = XtceDbFactory.getInstance(processor.getInstance()); MetaCommand cmd = verifyCommand(req, mdb, requestCommandName); String origin = ""; int sequenceNumber = 0; boolean dryRun = false; String comment = null; List<ArgumentAssignment> assignments = new ArrayList<>(); if (req.hasBody()) { IssueCommandRequest request = req.bodyAsMessage(SchemaRest.IssueCommandRequest.MERGE).build(); if (request.hasOrigin()) { origin = request.getOrigin(); } if (request.hasDryRun()) { dryRun = request.getDryRun(); } if (request.hasSequenceNumber()) { sequenceNumber = request.getSequenceNumber(); } if (request.hasComment()) { comment = request.getComment(); } for (Assignment a : request.getAssignmentList()) { assignments.add(new ArgumentAssignment(a.getName(), a.getValue())); } } // Override with params from query string for (String p : req.getQueryParameters().keySet()) { switch (p) { case "origin": origin = req.getQueryParameter("origin"); break; case "sequenceNumber": sequenceNumber = req.getQueryParameterAsInt("sequenceNumber"); break; case "dryRun": dryRun = req.getQueryParameterAsBoolean("dryRun"); break; case "pretty": case "nolink": break; default: String value = req.getQueryParameter(p); assignments.add(new ArgumentAssignment(p, value)); } } // Prepare the command PreparedCommand preparedCommand; try { preparedCommand = processor.getCommandingManager().buildCommand(cmd, assignments, origin, sequenceNumber, req.getAuthToken()); //make the source - should perhaps come from the client StringBuilder sb = new StringBuilder(); sb.append(requestCommandName); sb.append("("); boolean first = true; for(ArgumentAssignment aa:assignments) { Argument a = preparedCommand.getMetaCommand().getArgument(aa.getArgumentName()); if(!first) { sb.append(", "); } else { first = false; } sb.append(aa.getArgumentName()).append(": "); boolean needDelimiter = a !=null && (a.getArgumentType() instanceof StringArgumentType || a.getArgumentType() instanceof EnumeratedArgumentType); if(needDelimiter) { sb.append("\""); } sb.append(aa.getArgumentValue()); if(needDelimiter) { sb.append("\""); } } sb.append(")"); preparedCommand.setSource(sb.toString()); } catch (NoPermissionException e) { throw new ForbiddenException(e); } catch (ErrorInCommand e) { throw new BadRequestException(e); } catch (YamcsException e) { // could be anything, consider as internal server error throw new InternalServerErrorException(e); } // Good, now send CommandQueue queue; try { if (dryRun) { CommandQueueManager mgr = processor.getCommandingManager().getCommandQueueManager(); queue = mgr.getQueue(req.getAuthToken(), preparedCommand); } else { queue = processor.getCommandingManager().sendCommand(req.getAuthToken(), preparedCommand); if(comment != null) { try { processor.getCommandingManager().addToCommandHistory(preparedCommand.getCommandId(), "Comment", comment, req.getAuthToken()); } catch (NoPermissionException e) { throw new ForbiddenException(e); } } } } catch (InvalidAuthenticationToken e) { throw new ForbiddenException(e); } Commanding.CommandQueueEntry cqe = ManagementGpbHelper.toCommandQueueEntry(queue, preparedCommand); IssueCommandResponse.Builder response = IssueCommandResponse.newBuilder(); response.setCommandQueueEntry(cqe); response.setSource(preparedCommand.getSource()); response.setBinary(ByteString.copyFrom(preparedCommand.getBinary())); response.setHex(StringConverter.arrayToHexString(preparedCommand.getBinary())); completeOK(req, response.build(), SchemaRest.IssueCommandResponse.WRITE); } @Route(path = "/api/processors/:instance/:processor/commandhistory/:name*", method = "POST") public void updateCommandHistory(RestRequest req) throws HttpException { Processor processor = verifyProcessor(req, req.getRouteParam("instance"), req.getRouteParam("processor")); if (!processor.hasCommanding()) { throw new BadRequestException("Commanding not activated for this processor"); } try { if (req.hasBody()) { Rest.UpdateCommandHistoryRequest request = req.bodyAsMessage(SchemaRest.UpdateCommandHistoryRequest.MERGE).build(); Commanding.CommandId cmdId = request.getCmdId(); for (Rest.UpdateCommandHistoryRequest.KeyValue historyEntry : request.getHistoryEntryList()) { processor.getCommandingManager().addToCommandHistory(cmdId, historyEntry.getKey(), historyEntry.getValue(), req.getAuthToken()); } } } catch (NoPermissionException e) { throw new ForbiddenException(e); } completeOK(req); } }