/* * Simbad - Robot Simulator * Copyright (C) 2004 Louis Hugues * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ----------------------------------------------------------------------------- * $Author: sioulseuguh $ * $Date: 2005/08/07 12:25:03 $ * $Revision: 1.13 $ * $Source: /cvsroot/simbad/src/simbad/sim/Simulator.java,v $ */ package simbad.sim; import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; import javax.media.j3d.VirtualUniverse; import javax.swing.JComponent; import javax.swing.JInternalFrame; /** * The Simulator class. It manages the list of agents and performs the simulation steps. * For each agent a simulation step is is as follow: * <li>update sensors </li> * <li>call agent performBehavior </li> * <li>update position </li> * In normal operation the steps are triggered by a timer event. * <br> * The Simulator class also provides a Background Mode with limited rendering. * This mode is mainly useful for batch simulation ( ie genetic algorithms). * See Also Simbatch class. * * Cooperates with: World, PhysicalEngine * */ public class Simulator { /** Used for mutual exclusion. all methods accessing agents shoud use this lock. */ private Lock lock; /** Keeps track of application's main component -- can be null*/ private JComponent applicationComponent; World world; // link to associated world FrameMeter fps; // for fps measurement private Timer timer; /** The list of all objects in the world. (static objects and agents). */ private ArrayList<Object> objects; /** The list of all agents. */ private ArrayList<SimpleAgent> agents; /** number of frames per virtual seconds - 20 is a good value */ private int framesPerSecond; private float virtualTimeFactor; /** Thread for the background mode */ private SimulatorThread simulatorThread; /** Control the usage of physical law in the simulation */ private boolean usePhysics; /** Handles all algorithms and resources related to agent interactions. */ PhysicalEngine physicalEngine; /** Count simulation steps. */ private long counter; /////////////////////////////////////////////////////// // CREATION / DESTRUCTION /** Constructs the simulator object * * @param applicationComponent - A reference to the main Application container. * @param world - The 3d world object. * @param ed - the Environment description. */ public Simulator(JComponent applicationComponent,World world, EnvironmentDescription ed) { this.world = world; simulatorThread = null; this.applicationComponent = applicationComponent; lock = new Lock(); initialize(ed); } /** Initialize the simulator - only called once.*/ private void initialize(EnvironmentDescription ed) { counter = 0; timer = null; setFramesPerSecond(20); setVirtualTimeFactor(1); fps = new FrameMeter(); agents = new ArrayList<SimpleAgent>(); objects = new ArrayList<Object>(); usePhysics =ed.usePhysics; physicalEngine = new PhysicalEngine(); addMobileAndStaticObjects(ed); createAgentsUI(); } /** Dispose all ressources. only called once.*/ public synchronized void dispose(){ stopSimulation(); // dirty - wait rendering end. try {Thread.sleep(500); } catch(Exception e){} for (int i = 0; i < agents.size(); i++) ((SimpleAgent) agents.get(i)).dispose(); } /** Add all agents and objects. Only called once.*/ private void addMobileAndStaticObjects(EnvironmentDescription ed) { // add all agents requested by the user for (int i = 0 ; i < ed.objects.size();i++){ Object o = ed.objects.get(i); objects.add(o); if (o instanceof StaticObject){ // The object is of type static, we add it to the world // and precompute anything possible. StaticObject so = (StaticObject) o; so.setWorld(world); world.addObjectToPickableSceneBranch(so); // pre compute necessary stuff. so.createLocalToVworld(); so.createTransformedBounds(); }else if (o instanceof SimpleAgent){ // The object is of type agent, we add it to the // simulator and call create. SimpleAgent agent = (SimpleAgent)o; agent.setSimulator(this); agent.setWorld(world); agent.create(); agent.reset(); agents.add(agent); world.addObjectToPickableSceneBranch(agent); } } } /** Creates the UI that may be associated to each agent. * If the agent has set a Panel with setUIPanel a window is created containing the panel. * Only called once. **/ private void createAgentsUI() { for (int i = 0; i < agents.size(); i++) { SimpleAgent agent = ((SimpleAgent) agents.get(i)); // Only Agents have ui - not all SimpleAgents. if (agent instanceof Agent) { JInternalFrame window = ((Agent) agent).createUIWindow(); if ((window != null) && (applicationComponent != null)){ applicationComponent.add(window); window.show(); window.toFront(); } } } } ///////////////////////////////////////////////////////////////////////////////////// // SIMULATION LOOP /** * The main simulator method. It is called cyclicaly or step by step. (see startSimulation). */ public synchronized void simulateOneStep() { // start critical section. only one thread. lock(); int nagents = agents.size(); // Print memory info (rarely) if (counter % 100000==0){ Runtime.getRuntime().gc(); System.out.println("Memory heap total: "+Runtime.getRuntime().totalMemory()/1024+ "k max: "+Runtime.getRuntime().maxMemory()/1024+ "k free: "+Runtime.getRuntime().freeMemory()/1024+"k"); } counter++; // seconds are elapsed in virtual agent time. double dt = 1.0 / framesPerSecond; // update sensors and actuators for (int i = 0; i < nagents; i++) { SimpleAgent a = ((SimpleAgent) agents.get(i)); a.updateSensors(dt, world.getPickableSceneBranch()); a.updateActuators(dt); } // perform behavior for (int i = 0; i < nagents; i++) { SimpleAgent a = ((SimpleAgent) agents.get(i)); a.performPreBehavior(); a.performBehavior(); } // Compute forces for (int i = 0; i < nagents; i++) { SimpleAgent a = ((SimpleAgent) agents.get(i)); a.clearVeryNear(); a.setMotorsAcceleration(dt); if (usePhysics) physicalEngine.computeForces(dt,a); a.integratesVelocities(dt); a.integratesPositionChange(dt); } // Check physical interactions. if (usePhysics){ physicalEngine.checkAgentAgentPairs(dt,agents,true,false); physicalEngine.checkAgentObjectPairs(agents,objects,true,false); // integrate position again cause velocity may have changed due to impact. for (int i = 0; i < nagents; i++) { SimpleAgent a = ((SimpleAgent) agents.get(i)); a.integratesPositionChange(dt); } } // Check collision physicalEngine.checkAgentAgentPairs(dt,agents,false,true); physicalEngine.checkAgentObjectPairs(agents,objects,false,true); // Update position and all counters for (int i = 0; i < nagents; i++) { SimpleAgent a = ((SimpleAgent) agents.get(i)); a.updatePosition(); a.updateCounters(dt); } fps.measurePoint(1); // end of critical section unlock(); } /** initialize the behavior of all agents. */ public synchronized void initBehaviors() { lock();// start of critical section // init behaviors for (int i = 0; i < agents.size(); i++) { SimpleAgent agent = ((SimpleAgent) agents.get(i)); agent.initPreBehavior(); agent.initBehavior(); } unlock();// end of critical section } /** Starts the simulator loop. */ public synchronized void startSimulation() { stopSimulation(); initBehaviors(); timer = new Timer(); System.out.println("[SIM] start ..."); timer.scheduleAtFixedRate(new TimerTask() { public void run() { simulateOneStep(); } }, 0, (long) (1000 / (framesPerSecond * virtualTimeFactor))); } /** Stop (or pause) the simulator loop. */ public synchronized void stopSimulation() { if (timer != null) timer.cancel(); System.out.println("[SIM] stop ..."); } /** Simulator control. */ public synchronized void restartSimulation() { stopSimulation(); resetSimulation(); startSimulation(); System.out.println("[SIM] restart ..."); } /** Reset the simulation. Resets any living agents. */ public synchronized void resetSimulation() { stopSimulation(); lock(); for (int i = 0; i < agents.size(); i++) { SimpleAgent agent = ((SimpleAgent) agents.get(i)); agent.reset(); } unlock(); System.out.println("[SIM] reset ..."); } /** Perform a single step of simulation */ public synchronized void performSimulationStep() { stopSimulation(); System.out.println("[SIM] Step ..."); simulateOneStep(); } public ArrayList<SimpleAgent> getAgentList() { return agents; } /** Sets use physics indicator.*/ protected void setUsePhysics(boolean usePhysics){ this.usePhysics = usePhysics; } /** Gets use physics indicator.*/ protected boolean getUsePhysics(){ return usePhysics; } protected void setFramesPerSecond(int fps){ framesPerSecond = fps; } protected int getFramesPerSecond(){ return framesPerSecond ; } /** Set the time factor. Used to increase or decrease the simulation rate. * * @param fact : typical value 1.0 (default) , 2.0 or 0.5 */ public void setVirtualTimeFactor(float fact){ System.out.println("[SIM] virtualTimeFactor = " + fact); virtualTimeFactor = fact; } public float getVirtualTimeFactor(){ return virtualTimeFactor ; } public void setApplicationComponent(JComponent component){ applicationComponent= component;} /** This class runs the simulator in a background process. * It is only used for simulator background mode. */ /** Obtain simulator critical resources.*/ public void lock(){lock.lock();} /** Release simulator critical resources.*/ public void unlock(){lock.unlock();} ///////////////////////////////////////////////////////////////////////////////////// // BACKGROUND MODE THREAD /** Starts special background mode */ public synchronized void startBackgroundMode(){ simulatorThread = new SimulatorThread(); // j3d threads must have priority simulatorThread.setPriority(Thread.MIN_PRIORITY); simulatorThread.start(); } /** Stops special background mode */ public synchronized void stopBackgroundMode(){ if (simulatorThread != null) simulatorThread.requestStop(); } private class SimulatorThread extends Thread { private boolean stopped; SimulatorThread() { stopped = false; } public void requestStop() { stopped = true; } public void run() { setPriority(Thread.MAX_PRIORITY); VirtualUniverse.setJ3DThreadPriority(Thread.MIN_PRIORITY); int count = 0; int rendererRate = 100000; System.out.println("[SIM] Starting Background mode"); try { // First wait a bit so J3d is settled ? sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } world.stopRendering(); world.renderOnce(); // adjust fps update because we're running too fast to have precise // measure fps.setUpdateRate(rendererRate); for (int i = 0; i < agents.size(); i++) { SimpleAgent agent = (SimpleAgent) agents.get(i); agent.reset(); if (agent instanceof Agent) ((Agent) agent).setFrameMeterRate(rendererRate); } initBehaviors(); // loop until flag change while (!stopped) { simulateOneStep(); count++; if ((count % rendererRate) == 0) { world.renderOnce(); // inspector update seems to cause memory leak (priority ?) for (int i = 0; i < agents.size(); i++) { Object o = agents.get(i); if (o instanceof Agent) ((Agent) o).getAgentInspector().update(); } } } System.out.println("[SIM] Stopping Background mode"); world.startRendering(); } } }