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;
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;
this.simulationListeners.addAll(listeners);
}
-
+
if (data != null && this.status != Status.NOT_SIMULATED) {
simulatedData = data;
if (this.status == Status.LOADED) {
}
+ /**
+ * 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.
* @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();
*
* @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
* @return the actual list of simulation listeners.
*/
public List<String> getSimulationListeners() {
+ mutex.verify();
return simulationListeners;
}
* @return the name for the simulation.
*/
public String getName() {
+ mutex.verify();
return name;
}
* @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.
* @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>
*
* @return the conditions used in the previous simulation, or <code>null</code>.
*/
- public SimulationConditions getSimulatedConditions() {
+ public GUISimulationConditions getSimulatedConditions() {
+ mutex.verify();
return simulatedConditions;
}
* @see FlightData#getWarningSet()
*/
public WarningSet getSimulatedWarnings() {
+ mutex.verify();
if (simulatedData == null)
return null;
return simulatedData.getWarningSet();
* @see Rocket#getMotorConfigurationNameOrDescription(String)
*/
public String getSimulatedMotorDescription() {
+ mutex.verify();
return simulatedMotors;
}
* @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.
*/
@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();
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");
}
}
*/
@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