package org.yamcs.algorithms; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.parameter.ParameterValue; import org.yamcs.xtce.Algorithm; import org.yamcs.xtce.InputParameter; import org.yamcs.xtce.OnParameterUpdateTrigger; import org.yamcs.xtce.Parameter; import org.yamcs.xtce.ParameterInstanceRef; public abstract class AbstractAlgorithmExecutor implements AlgorithmExecutor { final protected AlgorithmExecutionContext execCtx; final protected Algorithm algorithmDef; final protected CopyOnWriteArrayList<AlgorithmExecListener> execListeners = new CopyOnWriteArrayList<AlgorithmExecListener>(); // Keep only unique arguments (for subscription purposes) protected Set<Parameter> requiredParameters = new HashSet<Parameter>(); protected Set<InputParameter>mandatoryToRun = new HashSet<InputParameter>(); private Map<InputParameter,ParameterValue> inputValues = new HashMap<InputParameter,ParameterValue>(); static final Logger log = LoggerFactory.getLogger(AbstractAlgorithmExecutor.class); public AbstractAlgorithmExecutor(Algorithm algorithmDef, AlgorithmExecutionContext execCtx) { this.algorithmDef = algorithmDef; this.execCtx = execCtx; for(InputParameter inputParameter:algorithmDef.getInputSet()) { requiredParameters.add(inputParameter.getParameterInstance().getParameter()); // Default-define all input values to null to prevent ugly runtime errors String scriptName = inputParameter.getInputName(); if(scriptName==null) { scriptName = inputParameter.getParameterInstance().getParameter().getName(); } if(inputParameter.isMandatory()) mandatoryToRun.add(inputParameter); } } /** * update the parameters and return true if the algorithm should run * @param items * @return true if the algorithm should run */ public synchronized boolean updateParameters(ArrayList<ParameterValue> items) { ArrayList<ParameterValue> allItems=new ArrayList<ParameterValue>(items); boolean skipRun=false; // Set algorithm arguments based on incoming values for(InputParameter inputParameter:algorithmDef.getInputSet()) { ParameterInstanceRef pInstance = inputParameter.getParameterInstance(); for(ParameterValue pval:allItems) { if(pInstance.getParameter().equals(pval.getParameter())) { if(getLookbackSize(pInstance.getParameter())==0) { updateInput(inputParameter, pval); inputValues.put(inputParameter, pval); } else { ParameterValue historicValue=execCtx.getHistoricValue(pInstance); if(historicValue!=null) { updateInput(inputParameter, historicValue); inputValues.put(inputParameter, historicValue); } } } } if(!skipRun && inputParameter.isMandatory() && !inputValues.containsKey(inputParameter)) { log.trace("Not running algorithm {} because mandatory input {} is not present", algorithmDef.getName(), inputParameter.getInputName()); skipRun = true; } } // But run it only, if this satisfies an onParameterUpdate trigger boolean triggered=false; for(OnParameterUpdateTrigger trigger:algorithmDef.getTriggerSet().getOnParameterUpdateTriggers()) { if(triggered) break; for(ParameterValue pval:allItems) { if(pval.getParameter().equals(trigger.getParameter())) { triggered=true; break; } } } boolean shouldRun =(!skipRun && triggered); return shouldRun; } abstract protected void updateInput(InputParameter inputParameter, ParameterValue newValue); protected void propagateToListeners(Object returnValue, List<ParameterValue> outputValues){ for(AlgorithmExecListener listener: execListeners) { listener.algorithmRun(returnValue, outputValues); } } @Override public void addExecListener(AlgorithmExecListener listener) { execListeners.add(listener); } @Override public AlgorithmExecutionContext getExecutionContext() { return execCtx; } @Override public int getLookbackSize(Parameter parameter) { // e.g. [ -3, -2, -1, 0 ] int min=0; for(InputParameter p:algorithmDef.getInputSet()) { ParameterInstanceRef pInstance=p.getParameterInstance(); if(pInstance.getParameter().equals(parameter) && pInstance.getInstance()<min) { min=p.getParameterInstance().getInstance(); } } return -min; } @Override public Set<Parameter> getRequiredParameters() { return requiredParameters; } @Override public Algorithm getAlgorithm() { return algorithmDef; } }