/* * Created on Oct 21, 2006 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu * (jactr.org) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. This library is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. You should have * received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.core.runtime; import java.io.PrintWriter; import java.io.StringWriter; import java.text.NumberFormat; import java.util.concurrent.ExecutorService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.logging.Logger; import org.jactr.core.model.ICycleProcessor; import org.jactr.core.model.IModel; import org.jactr.core.model.ModelTerminatedException; import org.jactr.core.model.basic.BasicModel; import org.jactr.core.model.event.ModelEvent; import org.jactr.core.queue.TimedEventQueue; /** * basic model runner, handles all events except disconnected which will be * handled by the controller * * @author developer */ public class DefaultModelRunner implements Runnable { /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(DefaultModelRunner.class); protected ExecutorService _service; protected BasicModel _model; protected ICycleProcessor _cycleRunner; private NumberFormat _format; public DefaultModelRunner(ExecutorService service, IModel model, ICycleProcessor cycleRunner) { if (!(model instanceof BasicModel)) throw new RuntimeException(getClass().getSimpleName() + " only supports subclasses of " + BasicModel.class.getName()); _model = (BasicModel) model; _cycleRunner = cycleRunner; _service = service; _format = NumberFormat.getNumberInstance(); _format.setMaximumFractionDigits(3); _format.setMinimumFractionDigits(3); } protected void startUp() { /** * this will connect, and once it has gotten the go ahead to initialize from * common reality, it will call model.initialize(). we then block until it * gets the start command */ ACTRRuntime.getRuntime().getConnector().connect(_model); ACTRRuntime.getRuntime().getClock(_model).setTimeShift(_model.getAge()); if (LOGGER.isDebugEnabled()) LOGGER.debug("Shifted clock to " + _model.getAge()); /** * let anyone who depends on commonreality know that we have connected */ _model.dispatch(new ModelEvent(_model, ModelEvent.Type.CONNECTED)); } protected void shutDown(Exception deferred) { try { _model .dispatch(new ModelEvent(_model, ModelEvent.Type.STOPPED, deferred)); double age = ACTRRuntime.getRuntime().getClock(_model).getTime(); if (LOGGER.isDebugEnabled()) LOGGER.debug("Set model age to " + age); _model.setAge(age); ACTRRuntime.getRuntime().getConnector().disconnect(_model); Thread.currentThread().setName("dead-" + _model.getName()); } catch (Exception e) { deferred = e; } finally { _model.dispatch(new ModelEvent(_model, ModelEvent.Type.DISCONNECTED, deferred)); } } protected void preEventFiring() { } protected boolean firePendingEvents(double now) { /* * fire all the events that may have transpired by now */ TimedEventQueue queue = _model.getTimedEventQueue(); boolean anyFired = false; while (queue.fireExpiredEvents(now)) anyFired = true; return anyFired; } protected void postEventFiring() { } /** * run a single cycle of the model * * @return */ protected double cycle(boolean eventsHaveFired) { return _cycleRunner.cycle(_model, eventsHaveFired); } /** * wait for the clock to reach this time * * @param waitForTime * @throws InterruptedException */ protected double waitForClock(double waitForTime) throws InterruptedException { if (Double.isNaN(waitForTime)) return 0; double rtn = ACTRRuntime.getRuntime().getClock(_model).waitForTime( waitForTime); return rtn; } /** * called before blocking on waitForClock */ protected void preClock() { } /** * called after waitForClock returns */ protected void postClock(double currentSimulatedTime) { } /** * called before each cycle starts */ protected void preCycle(double currentSimulatedTime) { } /** * called after each cycle */ protected void postCycle(double nextTime) { } /** * run the model in loop * * @param clock */ protected void runModelLoop() { try { double startTime = waitForClock(_model.getAge()); if (LOGGER.isDebugEnabled()) LOGGER.debug("Starting @ " + startTime + " age:" + _model.getAge()); /* * signal the start to the runtime and the model */ _model.dispatch(new ModelEvent(_model, ModelEvent.Type.STARTED)); double nextTime = startTime; while (!_service.isShutdown() && !Double.isNaN(nextTime)) { /* * fire the time event */ if (Logger.hasLoggers(_model)) Logger.log(_model, Logger.Stream.TIME, _format.format(nextTime)); preEventFiring(); boolean eventsHaveFired = firePendingEvents(nextTime); postEventFiring(); preCycle(nextTime); if (LOGGER.isDebugEnabled()) LOGGER.debug("set age to " + nextTime); _model.setAge(nextTime); nextTime = cycle(eventsHaveFired); postCycle(nextTime); if (!Double.isNaN(nextTime)) { preClock(); nextTime = waitForClock(nextTime); postClock(nextTime); } else postClock(Double.NaN); } } catch (ModelTerminatedException mte) { // perfectly normal } catch (InterruptedException ie) { // perfectly normal } finally { /* * pop all the buffer contents */ for (IActivationBuffer buffer : _model.getActivationBuffers()) buffer.clear(); } } public void run() { Exception deferred = null; try { startUp(); runModelLoop(); } catch (Exception e) { deferred = e; if (LOGGER.isErrorEnabled()) LOGGER.error("Model:" + _model + " has thrown an exception", e); if (Logger.hasLoggers(_model)) { /* * build the message.. */ StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); Logger.log(_model, Logger.Stream.EXCEPTION, sw.toString()); } try { _model.dispatch(new ModelEvent(_model, e)); } catch (Exception e2) { // just in case } } finally { shutDown(deferred); } } }