DGP - merged printing support from branch
[debian/openrocket] / src / net / sf / openrocket / document / Simulation.java
index 5e8d8e9f960d5dff70c094116d7d759b6d0f324d..9d5d0cd35169c62b6ce1fcbeab492e0987943ead 100644 (file)
@@ -9,60 +9,81 @@ import javax.swing.event.ChangeListener;
 import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
 import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
 import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.masscalc.BasicMassCalculator;
+import net.sf.openrocket.masscalc.MassCalculator;
 import net.sf.openrocket.rocketcomponent.Configuration;
 import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.simulation.BasicEventSimulationEngine;
 import net.sf.openrocket.simulation.FlightData;
-import net.sf.openrocket.simulation.FlightSimulator;
-import net.sf.openrocket.simulation.RK4Simulator;
+import net.sf.openrocket.simulation.GUISimulationConditions;
+import net.sf.openrocket.simulation.RK4SimulationStepper;
 import net.sf.openrocket.simulation.SimulationConditions;
-import net.sf.openrocket.simulation.SimulationListener;
+import net.sf.openrocket.simulation.SimulationEngine;
+import net.sf.openrocket.simulation.SimulationStepper;
 import net.sf.openrocket.simulation.exception.SimulationException;
 import net.sf.openrocket.simulation.exception.SimulationListenerException;
+import net.sf.openrocket.simulation.listeners.SimulationListener;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.BugException;
 import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.SafetyMutex;
 
-
+/**
+ * A class defining a simulation, its conditions and simulated data.
+ * <p>
+ * This class is not thread-safe and enforces single-threaded access with a
+ * SafetyMutex.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
 public class Simulation implements ChangeSource, Cloneable {
+       private static final LogHelper log = Application.getLogger();
        
        public static enum Status {
                /** Up-to-date */
                UPTODATE,
-               
+
                /** Loaded from file, status probably up-to-date */
                LOADED,
-               
+
                /** Data outdated */
                OUTDATED,
-               
+
                /** Imported external data */
                EXTERNAL,
-               
+
                /** Not yet simulated */
                NOT_SIMULATED
        }
        
-
+       private final SafetyMutex mutex = new SafetyMutex();
+       
        private final Rocket rocket;
        
        private String name = "";
-
+       
        private Status status = Status.NOT_SIMULATED;
        
        /** The conditions to use */
-       private SimulationConditions conditions;
+       // TODO: HIGH: Change to use actual conditions class??
+       private GUISimulationConditions conditions;
        
        private ArrayList<String> simulationListeners = new ArrayList<String>();
        
-       private Class<? extends FlightSimulator> simulatorClass = RK4Simulator.class;
-       private Class<? extends AerodynamicCalculator> calculatorClass = BarrowmanCalculator.class;
-
-       
+       private final Class<? extends SimulationEngine> simulationEngineClass = BasicEventSimulationEngine.class;
+       private Class<? extends SimulationStepper> simulationStepperClass = RK4SimulationStepper.class;
+       private Class<? extends AerodynamicCalculator> aerodynamicCalculatorClass = BarrowmanCalculator.class;
+       private Class<? extends MassCalculator> massCalculatorClass = BasicMassCalculator.class;
        
+
+
        /** Listeners for this object */
        private List<ChangeListener> listeners = new ArrayList<ChangeListener>();
        
-       
+
        /** The conditions actually used in the previous simulation, or null */
-       private SimulationConditions simulatedConditions = null;
+       private GUISimulationConditions simulatedConditions = null;
        private String simulatedMotors = null;
        private FlightData simulatedData = null;
        private int simulatedRocketID = -1;
@@ -78,23 +99,23 @@ public class Simulation implements ChangeSource, Cloneable {
                this.rocket = rocket;
                this.status = Status.NOT_SIMULATED;
                
-               conditions = new SimulationConditions(rocket);
+               conditions = new GUISimulationConditions(rocket);
                conditions.setMotorConfigurationID(
                                rocket.getDefaultConfiguration().getMotorConfigurationID());
                conditions.addChangeListener(new ConditionListener());
        }
        
        
-       public Simulation(Rocket rocket, Status status, String name, SimulationConditions conditions,
+       public Simulation(Rocket rocket, Status status, String name, GUISimulationConditions conditions,
                        List<String> listeners, FlightData data) {
                
-               if (rocket == null) 
+               if (rocket == null)
                        throw new IllegalArgumentException("rocket cannot be null");
-               if (status == null) 
+               if (status == null)
                        throw new IllegalArgumentException("status cannot be null");
-               if (name == null) 
+               if (name == null)
                        throw new IllegalArgumentException("name cannot be null");
-               if (conditions == null) 
+               if (conditions == null)
                        throw new IllegalArgumentException("conditions cannot be null");
                
                this.rocket = rocket;
@@ -116,7 +137,7 @@ public class Simulation implements ChangeSource, Cloneable {
                        this.simulationListeners.addAll(listeners);
                }
                
-               
+
                if (data != null && this.status != Status.NOT_SIMULATED) {
                        simulatedData = data;
                        if (this.status == Status.LOADED) {
@@ -128,8 +149,17 @@ public class Simulation implements ChangeSource, Cloneable {
        }
        
        
+       /**
+        * Return the rocket associated with this simulation.
+        * 
+        * @return      the rocket.
+        */
+       public Rocket getRocket() {
+               mutex.verify();
+               return rocket;
+       }
+       
        
-
        /**
         * Return a newly created Configuration for this simulation.  The configuration
         * has the motor ID set and all stages active.
@@ -137,6 +167,7 @@ public class Simulation implements ChangeSource, Cloneable {
         * @return      a newly created Configuration of the launch conditions.
         */
        public Configuration getConfiguration() {
+               mutex.verify();
                Configuration c = new Configuration(rocket);
                c.setMotorConfigurationID(conditions.getMotorConfigurationID());
                c.setAllStages();
@@ -150,10 +181,11 @@ public class Simulation implements ChangeSource, Cloneable {
         * 
         * @return the simulation conditions.
         */
-       public SimulationConditions getConditions() {
+       public GUISimulationConditions getConditions() {
+               mutex.verify();
                return conditions;
        }
-
+       
        
        /**
         * Get the list of simulation listeners.  The returned list is the one used by
@@ -162,6 +194,7 @@ public class Simulation implements ChangeSource, Cloneable {
         * @return      the actual list of simulation listeners.
         */
        public List<String> getSimulationListeners() {
+               mutex.verify();
                return simulationListeners;
        }
        
@@ -172,6 +205,7 @@ public class Simulation implements ChangeSource, Cloneable {
         * @return      the name for the simulation.
         */
        public String getName() {
+               mutex.verify();
                return name;
        }
        
@@ -182,18 +216,23 @@ public class Simulation implements ChangeSource, Cloneable {
         * @param name  the name of the simulation.
         */
        public void setName(String name) {
-               if (this.name.equals(name))
-                       return;
-               
-               if (name == null)
-                       this.name = "";
-               else
-                       this.name = name;
-               
-               fireChangeEvent();
+               mutex.lock("setName");
+               try {
+                       if (this.name.equals(name))
+                               return;
+                       
+                       if (name == null)
+                               this.name = "";
+                       else
+                               this.name = name;
+                       
+                       fireChangeEvent();
+               } finally {
+                       mutex.unlock("setName");
+               }
        }
-
-
+       
+       
        /**
         * Returns the status of this simulation.  This method examines whether the
         * simulation has been outdated and returns {@link Status#OUTDATED} accordingly.
@@ -202,76 +241,77 @@ public class Simulation implements ChangeSource, Cloneable {
         * @see Status
         */
        public Status getStatus() {
+               mutex.verify();
+               
                if (status == Status.UPTODATE || status == Status.LOADED) {
-                       if (rocket.getFunctionalModID() != simulatedRocketID || 
+                       if (rocket.getFunctionalModID() != simulatedRocketID ||
                                        !conditions.equals(simulatedConditions))
                                return Status.OUTDATED;
                }
                
                return status;
        }
-
-       
        
        
-       public void simulate(SimulationListener ... additionalListeners) 
+
+
+       public void simulate(SimulationListener... additionalListeners)
                                                throws SimulationException {
-               
-               if (this.status == Status.EXTERNAL) {
-                       throw new SimulationException("Cannot simulate imported simulation.");
-               }
-               Configuration configuration;
-               AerodynamicCalculator calculator;
-               FlightSimulator simulator;
-       
+               mutex.lock("simulate");
                try {
-                       calculator = calculatorClass.newInstance();
-                       simulator = simulatorClass.newInstance();
-               } catch (InstantiationException e) {
-                       throw new IllegalStateException("Cannot instantiate calculator/simulator.",e);
-               } catch (IllegalAccessException e) {
-                       throw new IllegalStateException("Cannot access calc/sim instance?! BUG!",e);
-               } catch (NullPointerException e) {
-                       throw new IllegalStateException("Calculator or simulator null",e);
-               }
-
-               configuration = this.getConfiguration();
-               calculator.setConfiguration(configuration);
-               simulator.setCalculator(calculator);
-               
-               for (SimulationListener l: additionalListeners) {
-                       simulator.addSimulationListener(l);
-               }
-               
-               for (String className: simulationListeners) {
-                       SimulationListener l = null;
+                       
+                       if (this.status == Status.EXTERNAL) {
+                               throw new SimulationException("Cannot simulate imported simulation.");
+                       }
+                       
+                       SimulationEngine simulator;
+                       
                        try {
-                               Class<?> c = Class.forName(className);
-                               l = (SimulationListener)c.newInstance();
-                       } catch (Exception e) {
-                               throw new SimulationListenerException("Could not instantiate listener of " +
-                                               "class: " + className);
+                               simulator = simulationEngineClass.newInstance();
+                       } catch (InstantiationException e) {
+                               throw new IllegalStateException("Cannot instantiate simulator.", e);
+                       } catch (IllegalAccessException e) {
+                               throw new IllegalStateException("Cannot access simulator instance?! BUG!", e);
+                       } catch (NullPointerException e) {
+                               throw new IllegalStateException("Simulator null", e);
+                       }
+                       
+                       SimulationConditions simulationConditions = conditions.toSimulationConditions();
+                       for (SimulationListener l : additionalListeners) {
+                               simulationConditions.getSimulationListenerList().add(l);
+                       }
+                       
+                       for (String className : simulationListeners) {
+                               SimulationListener l = null;
+                               try {
+                                       Class<?> c = Class.forName(className);
+                                       l = (SimulationListener) c.newInstance();
+                               } catch (Exception e) {
+                                       throw new SimulationListenerException("Could not instantiate listener of " +
+                                                       "class: " + className, e);
+                               }
+                               simulationConditions.getSimulationListenerList().add(l);
                        }
-                       simulator.addSimulationListener(l);
+                       
+                       long t1, t2;
+                       log.debug("Simulation: calling simulator");
+                       t1 = System.currentTimeMillis();
+                       simulatedData = simulator.simulate(simulationConditions);
+                       t2 = System.currentTimeMillis();
+                       log.debug("Simulation: returning from simulator, simulation took " + (t2 - t1) + "ms");
+                       
+                       // Set simulated info after simulation, will not be set in case of exception
+                       simulatedConditions = conditions.clone();
+                       simulatedMotors = getConfiguration().getMotorConfigurationDescription();
+                       simulatedRocketID = rocket.getFunctionalModID();
+                       
+                       status = Status.UPTODATE;
+                       fireChangeEvent();
+               } finally {
+                       mutex.unlock("simulate");
                }
-               
-               long t1, t2;
-               System.out.println("Simulation: calling simulator");
-               t1 = System.currentTimeMillis();
-               simulatedData = simulator.simulate(conditions);
-               t2 = System.currentTimeMillis();
-               System.out.println("Simulation: returning from simulator, " +
-                               "simulation took "+(t2-t1)+"ms");
-               
-               // Set simulated info after simulation, will not be set in case of exception
-               simulatedConditions = conditions.clone();
-               simulatedMotors = configuration.getMotorConfigurationDescription();
-               simulatedRocketID = rocket.getFunctionalModID();
-
-               status = Status.UPTODATE;
-               fireChangeEvent();
        }
-
+       
        
        /**
         * Return the conditions used in the previous simulation, or <code>null</code>
@@ -279,7 +319,8 @@ public class Simulation implements ChangeSource, Cloneable {
         * 
         * @return      the conditions used in the previous simulation, or <code>null</code>.
         */
-       public SimulationConditions getSimulatedConditions() {
+       public GUISimulationConditions getSimulatedConditions() {
+               mutex.verify();
                return simulatedConditions;
        }
        
@@ -292,6 +333,7 @@ public class Simulation implements ChangeSource, Cloneable {
         * @see         FlightData#getWarningSet()
         */
        public WarningSet getSimulatedWarnings() {
+               mutex.verify();
                if (simulatedData == null)
                        return null;
                return simulatedData.getWarningSet();
@@ -307,6 +349,7 @@ public class Simulation implements ChangeSource, Cloneable {
         * @see         Rocket#getMotorConfigurationNameOrDescription(String)
         */
        public String getSimulatedMotorDescription() {
+               mutex.verify();
                return simulatedMotors;
        }
        
@@ -317,11 +360,12 @@ public class Simulation implements ChangeSource, Cloneable {
         * @return      the flight data of the previous simulation, or <code>null</code>.
         */
        public FlightData getSimulatedData() {
+               mutex.verify();
                return simulatedData;
        }
        
        
-       
+
        /**
         * Returns a copy of this simulation suitable for cut/copy/paste operations.  
         * This excludes any simulated data.
@@ -330,10 +374,11 @@ public class Simulation implements ChangeSource, Cloneable {
         */
        @SuppressWarnings("unchecked")
        public Simulation copy() {
+               mutex.lock("copy");
                try {
-
-                       Simulation copy = (Simulation)super.clone();
-
+                       
+                       Simulation copy = (Simulation) super.clone();
+                       
                        copy.status = Status.NOT_SIMULATED;
                        copy.conditions = this.conditions.clone();
                        copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
@@ -342,12 +387,13 @@ public class Simulation implements ChangeSource, Cloneable {
                        copy.simulatedMotors = null;
                        copy.simulatedData = null;
                        copy.simulatedRocketID = -1;
-
+                       
                        return copy;
-
-               
+                       
                } catch (CloneNotSupportedException e) {
-                       throw new RuntimeException("Clone not supported, BUG", e);
+                       throw new BugException("Clone not supported, BUG", e);
+               } finally {
+                       mutex.unlock("copy");
                }
        }
        
@@ -361,42 +407,49 @@ public class Simulation implements ChangeSource, Cloneable {
         */
        @SuppressWarnings("unchecked")
        public Simulation duplicateSimulation(Rocket newRocket) {
-               Simulation copy = new Simulation(newRocket);
-               
-               copy.name = this.name;
-               copy.conditions.copyFrom(this.conditions);
-               copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
-               copy.simulatorClass = this.simulatorClass;
-               copy.calculatorClass = this.calculatorClass;
-
-               return copy;
+               mutex.lock("duplicateSimulation");
+               try {
+                       Simulation copy = new Simulation(newRocket);
+                       
+                       copy.name = this.name;
+                       copy.conditions.copyFrom(this.conditions);
+                       copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
+                       copy.simulationStepperClass = this.simulationStepperClass;
+                       copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
+                       
+                       return copy;
+               } finally {
+                       mutex.unlock("duplicateSimulation");
+               }
        }
        
        
 
        @Override
        public void addChangeListener(ChangeListener listener) {
+               mutex.verify();
                listeners.add(listener);
        }
-
+       
        @Override
        public void removeChangeListener(ChangeListener listener) {
+               mutex.verify();
                listeners.remove(listener);
        }
        
        protected void fireChangeEvent() {
                ChangeListener[] ls = listeners.toArray(new ChangeListener[0]);
                ChangeEvent e = new ChangeEvent(this);
-               for (ChangeListener l: ls) {
+               for (ChangeListener l : ls) {
                        l.stateChanged(e);
                }
        }
        
+       
 
 
-       
        private class ConditionListener implements ChangeListener {
-
+               
                private Status oldStatus = null;
                
                @Override