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