]> git.gag.com Git - debian/openrocket/blob - src/net/sf/openrocket/document/Simulation.java
version 1.1.3
[debian/openrocket] / src / net / sf / openrocket / document / Simulation.java
1 package net.sf.openrocket.document;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import javax.swing.event.ChangeEvent;
7 import javax.swing.event.ChangeListener;
8
9 import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
10 import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
11 import net.sf.openrocket.aerodynamics.WarningSet;
12 import net.sf.openrocket.logging.LogHelper;
13 import net.sf.openrocket.masscalc.BasicMassCalculator;
14 import net.sf.openrocket.masscalc.MassCalculator;
15 import net.sf.openrocket.rocketcomponent.Configuration;
16 import net.sf.openrocket.rocketcomponent.Rocket;
17 import net.sf.openrocket.simulation.BasicEventSimulationEngine;
18 import net.sf.openrocket.simulation.FlightData;
19 import net.sf.openrocket.simulation.GUISimulationConditions;
20 import net.sf.openrocket.simulation.RK4SimulationStepper;
21 import net.sf.openrocket.simulation.SimulationConditions;
22 import net.sf.openrocket.simulation.SimulationEngine;
23 import net.sf.openrocket.simulation.SimulationStepper;
24 import net.sf.openrocket.simulation.exception.SimulationException;
25 import net.sf.openrocket.simulation.exception.SimulationListenerException;
26 import net.sf.openrocket.simulation.listeners.SimulationListener;
27 import net.sf.openrocket.startup.Application;
28 import net.sf.openrocket.util.BugException;
29 import net.sf.openrocket.util.ChangeSource;
30
31
32 public class Simulation implements ChangeSource, Cloneable {
33         private static final LogHelper log = Application.getLogger();
34         
35         public static enum Status {
36                 /** Up-to-date */
37                 UPTODATE,
38
39                 /** Loaded from file, status probably up-to-date */
40                 LOADED,
41
42                 /** Data outdated */
43                 OUTDATED,
44
45                 /** Imported external data */
46                 EXTERNAL,
47
48                 /** Not yet simulated */
49                 NOT_SIMULATED
50         }
51         
52         
53         private final Rocket rocket;
54         
55         private String name = "";
56         
57         private Status status = Status.NOT_SIMULATED;
58         
59         /** The conditions to use */
60         // TODO: HIGH: Change to use actual conditions class??
61         private GUISimulationConditions conditions;
62         
63         private ArrayList<String> simulationListeners = new ArrayList<String>();
64         
65         private final Class<? extends SimulationEngine> simulationEngineClass = BasicEventSimulationEngine.class;
66         private Class<? extends SimulationStepper> simulationStepperClass = RK4SimulationStepper.class;
67         private Class<? extends AerodynamicCalculator> aerodynamicCalculatorClass = BarrowmanCalculator.class;
68         private Class<? extends MassCalculator> massCalculatorClass = BasicMassCalculator.class;
69         
70
71
72         /** Listeners for this object */
73         private List<ChangeListener> listeners = new ArrayList<ChangeListener>();
74         
75
76         /** The conditions actually used in the previous simulation, or null */
77         private GUISimulationConditions simulatedConditions = null;
78         private String simulatedMotors = null;
79         private FlightData simulatedData = null;
80         private int simulatedRocketID = -1;
81         
82         
83         /**
84          * Create a new simulation for the rocket.  The initial motor configuration is
85          * taken from the default rocket configuration.
86          * 
87          * @param rocket        the rocket associated with the simulation.
88          */
89         public Simulation(Rocket rocket) {
90                 this.rocket = rocket;
91                 this.status = Status.NOT_SIMULATED;
92                 
93                 conditions = new GUISimulationConditions(rocket);
94                 conditions.setMotorConfigurationID(
95                                 rocket.getDefaultConfiguration().getMotorConfigurationID());
96                 conditions.addChangeListener(new ConditionListener());
97         }
98         
99         
100         public Simulation(Rocket rocket, Status status, String name, GUISimulationConditions conditions,
101                         List<String> listeners, FlightData data) {
102                 
103                 if (rocket == null)
104                         throw new IllegalArgumentException("rocket cannot be null");
105                 if (status == null)
106                         throw new IllegalArgumentException("status cannot be null");
107                 if (name == null)
108                         throw new IllegalArgumentException("name cannot be null");
109                 if (conditions == null)
110                         throw new IllegalArgumentException("conditions cannot be null");
111                 
112                 this.rocket = rocket;
113                 
114                 if (status == Status.UPTODATE) {
115                         this.status = Status.LOADED;
116                 } else if (data == null) {
117                         this.status = Status.NOT_SIMULATED;
118                 } else {
119                         this.status = status;
120                 }
121                 
122                 this.name = name;
123                 
124                 this.conditions = conditions;
125                 conditions.addChangeListener(new ConditionListener());
126                 
127                 if (listeners != null) {
128                         this.simulationListeners.addAll(listeners);
129                 }
130                 
131
132                 if (data != null && this.status != Status.NOT_SIMULATED) {
133                         simulatedData = data;
134                         if (this.status == Status.LOADED) {
135                                 simulatedConditions = conditions.clone();
136                                 simulatedRocketID = rocket.getModID();
137                         }
138                 }
139                 
140         }
141         
142         
143         /**
144          * Return the rocket associated with this simulation.
145          * 
146          * @return      the rocket.
147          */
148         public Rocket getRocket() {
149                 return rocket;
150         }
151         
152         
153         /**
154          * Return a newly created Configuration for this simulation.  The configuration
155          * has the motor ID set and all stages active.
156          * 
157          * @return      a newly created Configuration of the launch conditions.
158          */
159         public Configuration getConfiguration() {
160                 Configuration c = new Configuration(rocket);
161                 c.setMotorConfigurationID(conditions.getMotorConfigurationID());
162                 c.setAllStages();
163                 return c;
164         }
165         
166         /**
167          * Returns the simulation conditions attached to this simulation.  The conditions
168          * may be modified freely, and the status of the simulation will change to reflect
169          * the changes.
170          * 
171          * @return the simulation conditions.
172          */
173         public GUISimulationConditions getConditions() {
174                 return conditions;
175         }
176         
177         
178         /**
179          * Get the list of simulation listeners.  The returned list is the one used by
180          * this object; changes to it will reflect changes in the simulation.
181          * 
182          * @return      the actual list of simulation listeners.
183          */
184         public List<String> getSimulationListeners() {
185                 return simulationListeners;
186         }
187         
188         
189         /**
190          * Return the user-defined name of the simulation.
191          * 
192          * @return      the name for the simulation.
193          */
194         public String getName() {
195                 return name;
196         }
197         
198         /**
199          * Set the user-defined name of the simulation.  Setting the name to
200          * null yields an empty name.
201          * 
202          * @param name  the name of the simulation.
203          */
204         public void setName(String name) {
205                 if (this.name.equals(name))
206                         return;
207                 
208                 if (name == null)
209                         this.name = "";
210                 else
211                         this.name = name;
212                 
213                 fireChangeEvent();
214         }
215         
216         
217         /**
218          * Returns the status of this simulation.  This method examines whether the
219          * simulation has been outdated and returns {@link Status#OUTDATED} accordingly.
220          * 
221          * @return the status
222          * @see Status
223          */
224         public Status getStatus() {
225                 if (status == Status.UPTODATE || status == Status.LOADED) {
226                         if (rocket.getFunctionalModID() != simulatedRocketID ||
227                                         !conditions.equals(simulatedConditions))
228                                 return Status.OUTDATED;
229                 }
230                 
231                 return status;
232         }
233         
234         
235
236
237         public void simulate(SimulationListener... additionalListeners)
238                                                 throws SimulationException {
239                 
240                 if (this.status == Status.EXTERNAL) {
241                         throw new SimulationException("Cannot simulate imported simulation.");
242                 }
243                 
244                 SimulationEngine simulator;
245                 
246                 try {
247                         simulator = simulationEngineClass.newInstance();
248                 } catch (InstantiationException e) {
249                         throw new IllegalStateException("Cannot instantiate simulator.", e);
250                 } catch (IllegalAccessException e) {
251                         throw new IllegalStateException("Cannot access simulator instance?! BUG!", e);
252                 } catch (NullPointerException e) {
253                         throw new IllegalStateException("Simulator null", e);
254                 }
255                 
256                 SimulationConditions simulationConditions = conditions.toSimulationConditions();
257                 for (SimulationListener l : additionalListeners) {
258                         simulationConditions.getSimulationListenerList().add(l);
259                 }
260                 
261                 for (String className : simulationListeners) {
262                         SimulationListener l = null;
263                         try {
264                                 Class<?> c = Class.forName(className);
265                                 l = (SimulationListener) c.newInstance();
266                         } catch (Exception e) {
267                                 throw new SimulationListenerException("Could not instantiate listener of " +
268                                                 "class: " + className, e);
269                         }
270                         simulationConditions.getSimulationListenerList().add(l);
271                 }
272                 
273                 long t1, t2;
274                 log.debug("Simulation: calling simulator");
275                 t1 = System.currentTimeMillis();
276                 simulatedData = simulator.simulate(simulationConditions);
277                 t2 = System.currentTimeMillis();
278                 log.debug("Simulation: returning from simulator, simulation took " + (t2 - t1) + "ms");
279                 
280                 // Set simulated info after simulation, will not be set in case of exception
281                 simulatedConditions = conditions.clone();
282                 simulatedMotors = getConfiguration().getMotorConfigurationDescription();
283                 simulatedRocketID = rocket.getFunctionalModID();
284                 
285                 status = Status.UPTODATE;
286                 fireChangeEvent();
287         }
288         
289         
290         /**
291          * Return the conditions used in the previous simulation, or <code>null</code>
292          * if this simulation has not been run.
293          * 
294          * @return      the conditions used in the previous simulation, or <code>null</code>.
295          */
296         public GUISimulationConditions getSimulatedConditions() {
297                 return simulatedConditions;
298         }
299         
300         /**
301          * Return the warnings generated in the previous simulation, or
302          * <code>null</code> if this simulation has not been run.  This is the same
303          * warning set as contained in the <code>FlightData</code> object.
304          * 
305          * @return      the warnings during the previous simulation, or <code>null</code>.
306          * @see         FlightData#getWarningSet()
307          */
308         public WarningSet getSimulatedWarnings() {
309                 if (simulatedData == null)
310                         return null;
311                 return simulatedData.getWarningSet();
312         }
313         
314         
315         /**
316          * Return a string describing the motor configuration of the previous simulation,
317          * or <code>null</code> if this simulation has not been run.
318          * 
319          * @return      a description of the motor configuration of the previous simulation, or
320          *                      <code>null</code>.
321          * @see         Rocket#getMotorConfigurationNameOrDescription(String)
322          */
323         public String getSimulatedMotorDescription() {
324                 return simulatedMotors;
325         }
326         
327         /**
328          * Return the flight data of the previous simulation, or <code>null</code> if
329          * this simulation has not been run.
330          * 
331          * @return      the flight data of the previous simulation, or <code>null</code>.
332          */
333         public FlightData getSimulatedData() {
334                 return simulatedData;
335         }
336         
337         
338
339         /**
340          * Returns a copy of this simulation suitable for cut/copy/paste operations.  
341          * This excludes any simulated data.
342          *  
343          * @return      a copy of this simulation and its conditions.
344          */
345         @SuppressWarnings("unchecked")
346         public Simulation copy() {
347                 try {
348                         
349                         Simulation copy = (Simulation) super.clone();
350                         
351                         copy.status = Status.NOT_SIMULATED;
352                         copy.conditions = this.conditions.clone();
353                         copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
354                         copy.listeners = new ArrayList<ChangeListener>();
355                         copy.simulatedConditions = null;
356                         copy.simulatedMotors = null;
357                         copy.simulatedData = null;
358                         copy.simulatedRocketID = -1;
359                         
360                         return copy;
361                         
362
363                 } catch (CloneNotSupportedException e) {
364                         throw new BugException("Clone not supported, BUG", e);
365                 }
366         }
367         
368         
369         /**
370          * Create a duplicate of this simulation with the specified rocket.  The new
371          * simulation is in non-simulated state.
372          * 
373          * @param newRocket             the rocket for the new simulation.
374          * @return                              a new simulation with the same conditions and properties.
375          */
376         @SuppressWarnings("unchecked")
377         public Simulation duplicateSimulation(Rocket newRocket) {
378                 Simulation copy = new Simulation(newRocket);
379                 
380                 copy.name = this.name;
381                 copy.conditions.copyFrom(this.conditions);
382                 copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
383                 copy.simulationStepperClass = this.simulationStepperClass;
384                 copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
385                 
386                 return copy;
387         }
388         
389         
390
391         @Override
392         public void addChangeListener(ChangeListener listener) {
393                 listeners.add(listener);
394         }
395         
396         @Override
397         public void removeChangeListener(ChangeListener listener) {
398                 listeners.remove(listener);
399         }
400         
401         protected void fireChangeEvent() {
402                 ChangeListener[] ls = listeners.toArray(new ChangeListener[0]);
403                 ChangeEvent e = new ChangeEvent(this);
404                 for (ChangeListener l : ls) {
405                         l.stateChanged(e);
406                 }
407         }
408         
409         
410
411
412         private class ConditionListener implements ChangeListener {
413                 
414                 private Status oldStatus = null;
415                 
416                 @Override
417                 public void stateChanged(ChangeEvent e) {
418                         if (getStatus() != oldStatus) {
419                                 oldStatus = getStatus();
420                                 fireChangeEvent();
421                         }
422                 }
423         }
424 }