package jadex.base.service.simulation; import jadex.commons.ChangeEvent; import jadex.commons.Future; import jadex.commons.IChangeListener; import jadex.commons.ICommand; import jadex.commons.IFuture; import jadex.commons.collection.SCollection; import jadex.commons.concurrent.DefaultResultListener; import jadex.commons.concurrent.IResultListener; import jadex.commons.concurrent.IThreadPool; import jadex.commons.service.BasicService; import jadex.commons.service.IServiceProvider; import jadex.commons.service.SServiceProvider; import jadex.commons.service.clock.ClockService; import jadex.commons.service.clock.IClock; import jadex.commons.service.clock.IClockService; import jadex.commons.service.clock.ITimer; import jadex.commons.service.execution.IExecutionService; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * The execution control is the access point for controlling the * execution of one application. It provides basic features for * starting, stopping and stepwise execution. */ public class SimulationService extends BasicService implements ISimulationService { //-------- attributes -------- /** The platform. */ protected IServiceProvider provider; /** The execution mode. */ protected String mode; /** The executing flag. */ protected boolean executing; /** The listeners. */ protected List listeners; /** The simcommand for a simulation clock. */ protected ICommand simcommand; /** The time of a time step. */ protected long timesteptime; /** The clock service. */ protected IClockService clockservice; /** The execution service. */ protected IExecutionService exeservice; /** The outstanding listener notifications (change events). */ protected List notifications; /** Change indicator to coordinate separate threads. */ protected int change; //-------- constructors -------- /** * Create a new execution control. */ public SimulationService(IServiceProvider provider) { this(provider, null); } /** * Create a new execution control. */ public SimulationService(IServiceProvider provider, Map properties) { super(provider.getId(), ISimulationService.class, properties); this.provider = provider; // this.mode = mode; this.mode = MODE_NORMAL; this.listeners = SCollection.createArrayList(); this.simcommand = new ICommand() { public void execute(Object args) { IClockService simclock = getClockService(); int choice = 0; final int advance = 1; final int step_time = 2; synchronized(SimulationService.this) { if(isExecuting() && (MODE_TIME_STEP.equals(getMode()) || MODE_ACTION_STEP.equals(getMode()))) { setExecuting(false); } if(MODE_NORMAL.equals(getMode()) && executing) { choice = advance; } else if(MODE_TIME_STEP.equals(getMode())) { ITimer t = simclock.getNextTimer(); if(t!=null && t.getNotificationTime()<=timesteptime) { // System.out.println("continuing time step: "+timesteptime); choice = step_time; } // else // { // System.out.println("time step finished"); // } } } notifyListeners(); // delayed notifications of setExecuting() // Perform action out of synchronized block. switch(choice) { case advance: simclock.advanceEvent(); break; case step_time: stepTime(); break; default: } } }; } /** * Shutdown the service. * @param listener The listener. */ public IFuture shutdownService() { IFuture ret = super.shutdownService(); pause(); return ret; } //-------- methods -------- /** * Start (and run) the execution. */ public synchronized IFuture startService() { final Future ret = new Future(); super.startService().addResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { final boolean[] services = new boolean[2]; SServiceProvider.getService(provider, IExecutionService.class).addResultListener(new DefaultResultListener() { public void resultAvailable(Object source, Object result) { exeservice = (IExecutionService)result; boolean setresult; synchronized(services) { services[0] = true; setresult = services[0] && services[1]; } if(setresult) { init(); ret.setResult(SimulationService.this); } } }); SServiceProvider.getService(provider, IClockService.class).addResultListener(new DefaultResultListener() { public void resultAvailable(Object source, Object result) { clockservice = (IClockService)result; boolean setresult; synchronized(services) { services[1] = true; setresult = services[0] && services[1]; } if(setresult) { init(); ret.setResult(SimulationService.this); } } }); } public void exceptionOccurred(Object source, Exception exception) { ret.setException(exception); } }); return ret; } /** * Called after all required services have been found. */ protected void init() { String type = getClockService().getClockType(); if(IClock.TYPE_EVENT_DRIVEN.equals(type) || IClock.TYPE_TIME_DRIVEN.equals(type)) { getExecutorService().addIdleCommand(simcommand); } start(); } /** * Pause the execution (can be resumed via start or step). */ public void pause() { boolean dostop = false; synchronized(this) { if(isExecuting()) { dostop = true; setExecuting(false); } } notifyListeners(); // delayed notifications of setExecuting() if(dostop) getClockService().stop(); } /** * Restart the execution after pause. */ public void start() { boolean dostart = false; String type = null; synchronized(this) { if(!isExecuting()) { type = getClockService().getClockType(); setMode(MODE_NORMAL); setExecuting(true); dostart = true; } } notifyListeners(); // delayed notifications of setExecuting() if(dostart) { getClockService().start(); if(IClock.TYPE_EVENT_DRIVEN.equals(type) || IClock.TYPE_TIME_DRIVEN.equals(type)) { getClockService().advanceEvent(); } } } /** * Perform one event. */ public void stepEvent() { boolean dostart = false; int oldchange; synchronized(this) { if(!isExecuting()) { if(IClock.TYPE_CONTINUOUS.equals(getClockService().getClockType()) || IClock.TYPE_SYSTEM.equals(getClockService().getClockType())) { throw new RuntimeException("Step only possible in simulation mode."); } setMode(MODE_ACTION_STEP); setExecuting(true); dostart = true; } oldchange = change; } notifyListeners(); // delayed notifications of setExecuting() if(dostart) { getClockService().start(); boolean advanced = getClockService().advanceEvent(); synchronized(this) { // Have to make sure that executing is set back to false, // even if there is no time point or timing entry does not cause any execution. if(oldchange==change && (!advanced || getExecutorService().isIdle())) { //System.out.println("No further timepoint."); setExecuting(false); } } notifyListeners(); // delayed notifications of setExecuting() } } /** * Perform all actions belonging to one time point. */ public void stepTime() { boolean dostart = false; int oldchange; synchronized(this) { if(!isExecuting()) { if(IClock.TYPE_CONTINUOUS.equals(getClockService().getClockType()) || IClock.TYPE_SYSTEM.equals(getClockService().getClockType())) { throw new RuntimeException("Step only possible in simulation mode."); } setMode(MODE_TIME_STEP); setExecuting(true); dostart = true; } oldchange = change; } notifyListeners(); // delayed notifications of setExecuting() if(dostart) { ITimer next = getClockService().getNextTimer(); if(next!=null) { timesteptime = next.getNotificationTime(); // System.out.println("time step: "+timesteptime); getClockService().start(); boolean advanced = getClockService().advanceEvent(); synchronized(this) { // Have to make sure that executing is set back to false, // even if there is no time point or timing entry does not cause any execution. if(oldchange==change && (!advanced || getExecutorService().isIdle())) { // System.out.println("No further timepoint."); setExecuting(false); } } notifyListeners(); // delayed notifications of setExecuting() } } } /** * Get the execution mode. * @return The mode. */ public String getMode() { return mode; } /** * Get the execution mode. * @param mode The mode. */ public void setMode(String mode) { this.mode = mode; } /** * Set the clock. * @param clock The new clock. */ public void setClockType(String type, IThreadPool tp) { if(isExecuting()) throw new RuntimeException("Change clock not allowed during execution."); // IClockService cs = (IClockService)container.getService(IClockService.class); String oldtype = clockservice.getClockType(); if(!type.equals(oldtype)) { //System.out.println("Exchanged clock!!! "+clock); if(IClock.TYPE_EVENT_DRIVEN.equals(oldtype) || IClock.TYPE_TIME_DRIVEN.equals(oldtype)) { getExecutorService().removeIdleCommand(simcommand); } ((ClockService)clockservice).setClock(type, tp); if(IClock.TYPE_EVENT_DRIVEN.equals(type) || IClock.TYPE_TIME_DRIVEN.equals(type)) { getExecutorService().addIdleCommand(simcommand); } if(notifications==null) notifications = new ArrayList(); notifications.add(new ChangeEvent(this, "clock_type", type)); notifyListeners(); } } /** * Test if context is executing. */ public boolean isExecuting() { return executing; } /** * Add a change listener. * @param listener The change listener. */ public void addChangeListener(IChangeListener listener) { synchronized(this) { listeners.add(listener); } } /** * Remove a change listener. * @param listener The change listener. */ public void removeChangeListener(IChangeListener listener) { synchronized(this) { listeners.remove(listener); } } /** * Set the executing state. */ public void setExecuting(boolean executing) { synchronized(this) { if(executing!=this.executing) { change++; this.executing = executing; if(notifications==null) notifications = new ArrayList(); notifications.add(new ChangeEvent(this, "executing", executing? Boolean.TRUE: Boolean.FALSE)); } else { Thread.dumpStack(); } } } /** * Notify the listeners. */ protected void notifyListeners() { ChangeEvent[] events; IChangeListener[] alisteners; synchronized(this) { events = notifications!=null ? (ChangeEvent[])notifications.toArray(new ChangeEvent[notifications.size()]) : null; alisteners = listeners.isEmpty() ? null : (IChangeListener[])listeners.toArray(new IChangeListener[listeners.size()]); notifications = null; } if(alisteners!=null && events!=null) { for(int i=0; i<alisteners.length; i++) { for(int j=0; j<events.length; j++) { alisteners[i].changeOccurred(events[j]); } } } } /** * Get the platform clock. * @return The clock. */ protected IClockService getClockService() { return clockservice; // return (IClockService)container.getService(IClockService.class); } /** * Get the executor service. * @return The executor service. */ public IExecutionService getExecutorService() { return exeservice; // return (IExecutionService)container.getService(IExecutionService.class); } }