package org.yamcs.commanding; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.Processor; import org.yamcs.algorithms.AlgorithmExecutionContext; import org.yamcs.algorithms.AlgorithmManager; import org.yamcs.cmdhistory.CommandHistoryPublisher; import org.yamcs.xtce.CheckWindow; import org.yamcs.xtce.CheckWindow.TimeWindowIsRelativeToType; import org.yamcs.xtce.CommandVerifier; import org.yamcs.xtce.CommandVerifier.TerminationAction; import org.yamcs.xtce.MetaCommand; import org.yamcs.xtce.SequenceContainer; /** * This class implements the (post transmission) command verification. * * There is one handler for all the verifiers of a command. * TODO: They share a common pool of data for parameter checks and for algorithms (i.e. one algorithm is run once inside this pool for all verifiers). * * @author nm * */ public class CommandVerificationHandler { final Processor yproc; final PreparedCommand preparedCommand; final ScheduledThreadPoolExecutor timer; private List<Verifier> pendingVerifiers = new ArrayList<>(); private final Logger log = LoggerFactory.getLogger(this.getClass().getName()); enum VerifResult {OK, NOK, TIMEOUT} AlgorithmExecutionContext algorithmCtx; public CommandVerificationHandler(Processor yproc, PreparedCommand pc) { this.yproc = yproc; this.preparedCommand = pc; this.timer = yproc.getTimer(); } public void start() { MetaCommand cmd = preparedCommand.getMetaCommand(); List<CommandVerifier> verifiers = new ArrayList<CommandVerifier>(); collectVerifiers(cmd, verifiers); Verifier prevVerifier = null; for(CommandVerifier cv: verifiers) { SequenceContainer c = cv.getContainerRef(); Verifier verifier; if(c!=null) { verifier = new ContainerVerifier(this, cv, c); } else { if(algorithmCtx==null) { createAlgorithmContext(); } verifier = new AlgorithmVerifier(this, cv); } CheckWindow checkWindow = cv.getCheckWindow(); boolean scheduleNow = true; if(checkWindow.getTimeWindowIsRelativeTo()==TimeWindowIsRelativeToType.timeLastVerifierPassed) { if(prevVerifier!=null) { prevVerifier.nextVerifier = verifier; scheduleNow = false; } } if(scheduleNow) { scheduleVerifier(verifier, checkWindow.getTimeToStartChecking(), checkWindow.getTimeToStopChecking()); } } } private void collectVerifiers(MetaCommand cmd, List<CommandVerifier> verifiers) { for(CommandVerifier cv: cmd.getCommandVerifiers()) { boolean found = false; for(CommandVerifier existingv: verifiers) { if(existingv.getStage().equals(cv.getStage())) { found = true; break; } } if(!found) { verifiers.add(cv); } } MetaCommand basecmd = cmd.getBaseMetaCommand(); if(basecmd!=null) { collectVerifiers(basecmd, verifiers); } } private void createAlgorithmContext() { AlgorithmManager algMgr = yproc.getParameterRequestManager().getParameterProvider(AlgorithmManager.class); if(algMgr==null) { String msg = "Algorithm manager not configured for this processor, cannot run command verification based on algorithms"; log.error(msg); throw new RuntimeException(msg); } algorithmCtx = algMgr.createContext(preparedCommand.getCmdName()); } private void scheduleVerifier(final Verifier verifier, long windowStart, long windowStop) { if(windowStart>0) { timer.schedule(new Runnable() { @Override public void run() { verifier.start(); } }, windowStart, TimeUnit.MILLISECONDS); } else { verifier.start(); } pendingVerifiers.add(verifier); if(windowStop<=0) { throw new IllegalArgumentException("The window stop has to be greater than 0"); } timer.schedule(() -> onVerifierFinished(verifier, VerifResult.TIMEOUT) ,windowStop, TimeUnit.MILLISECONDS); } void onVerifierFinished(Verifier v, VerifResult result) { if(!pendingVerifiers.remove(v)) { if(result!=VerifResult.TIMEOUT) { log.warn("Got verifier finished for a verifier not in the pending list. cmd: {} verifier:", preparedCommand.getCmdName(), v.cv); } return; } log.debug("Command {} verifier finished: {} result: {}", preparedCommand.getCmdName(), v.cv, result); CommandVerifier cv = v.cv; CommandHistoryPublisher cmdHistPublisher = yproc.getCommandHistoryPublisher(); String histKey= CommandHistoryPublisher.Verifier_KEY_PREFIX+"_"+cv.getStage(); cmdHistPublisher.updateStringKey(preparedCommand.getCommandId(), histKey, result.toString()); TerminationAction ta = null; switch(result) { case OK: ta = cv.getOnSuccess(); break; case NOK: ta = cv.getOnFail(); break; case TIMEOUT: ta = cv.getOnTimeout(); break; } if(ta==TerminationAction.SUCCESS) { cmdHistPublisher.updateStringKey(preparedCommand.getCommandId(), CommandHistoryPublisher.CommandComplete_KEY, "OK"); } else if(ta==TerminationAction.FAIL) { cmdHistPublisher.updateStringKey(preparedCommand.getCommandId(), CommandHistoryPublisher.CommandFailed_KEY, "NOK"); } if(v.nextVerifier!=null && (result==VerifResult.OK)) { CheckWindow cw = v.nextVerifier.cv.getCheckWindow(); scheduleVerifier(v.nextVerifier, cw.getTimeToStartChecking(), cw.getTimeToStopChecking()); } } public Processor getProcessor() { return yproc; } public AlgorithmExecutionContext getAlgorithmExecutionContext() { return algorithmCtx; } public PreparedCommand getPreparedCommand() { return preparedCommand; } public AlgorithmManager getAlgorithmManager() { return yproc.getParameterRequestManager().getParameterProvider(AlgorithmManager.class); } }