package org.jactr.tools.loop; /* * default logging */ import java.util.Collection; import java.util.Set; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.concurrent.ExecutorServices; import org.jactr.core.model.IModel; import org.jactr.core.model.ModelTerminatedException; import org.jactr.core.module.procedural.IProceduralModule; import org.jactr.core.production.IInstantiation; import org.jactr.core.production.IProduction; import org.jactr.core.utils.parameter.BooleanParameterProcessor; import org.jactr.core.utils.parameter.IParameterized; import org.jactr.core.utils.parameter.ParameterHelper; import org.jactr.instrument.IInstrument; public class ProductionLoopDetector implements IInstrument, IParameterized { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(ProductionLoopDetector.class); private IProduction _lastProductionFired; private double _lastFiringTime; private double _defaultProductionFiringTime; private boolean _terminateOnDetection = true; private FiringSequenceListener _sequenceListener; private ParameterHelper _parameters = new ParameterHelper(); public ProductionLoopDetector() { _sequenceListener = new FiringSequenceListener(this); _parameters.addProcessor(new BooleanParameterProcessor( "TerminateOnDetection", this::setTerminateOnDetectionEnabled, this::isTerminateOnDetectionEnabled)); } @Override public void install(IModel model) { IProceduralModule pM = model.getProceduralModule(); pM.addListener(_sequenceListener, ExecutorServices.INLINE_EXECUTOR); } @Override public void uninstall(IModel model) { IProceduralModule pM = model.getProceduralModule(); pM.removeListener(_sequenceListener); } @Override public void initialize() { } public void setTerminateOnDetectionEnabled(boolean onOff) { _terminateOnDetection = onOff; } public boolean isTerminateOnDetectionEnabled() { return _terminateOnDetection; } public void productionFired(double simulationTime, IInstantiation instantiation) { IProduction production = instantiation.getProduction(); if (production.equals(_lastProductionFired)) { // check the time difference, or the cycle difference. double delta = simulationTime - _lastFiringTime; if (Math.abs(_defaultProductionFiringTime - delta) < 0.001) loopDetected(simulationTime, instantiation); } _lastFiringTime = simulationTime; _lastProductionFired = production; } /** * since this is executed inline with the model, throwing a * ModelTerminatedException will do what we expect it to. * * @param simulationTime * @param instantiation */ protected void loopDetected(double simulationTime, IInstantiation instantiation) { String message = String.format( "Loop detected: %s fired @ %0.3f and again @ %0.3f", _lastProductionFired, _lastFiringTime, simulationTime); LOGGER.error(message, new RuntimeException("trace info")); if (isTerminateOnDetectionEnabled()) throw new ModelTerminatedException(); // throw new ModelTerminatedException(message); } @Override public void setParameter(String key, String value) { _parameters.setParameter(key, value); } @Override public String getParameter(String key) { return _parameters.getParameter(key); } @Override public Collection<String> getPossibleParameters() { Set<String> rtn = new TreeSet<String>(); _parameters.getParameterNames(rtn); return rtn; } @Override public Collection<String> getSetableParameters() { Set<String> rtn = new TreeSet<String>(); _parameters.getSetableParameterNames(rtn); return rtn; } }