Move Summary to top of motor editor, make it show progress.
[sw/motorsim] / gui / com / billkuker / rocketry / motorsim / visual / workbench / MotorEditor.java
1 package com.billkuker.rocketry.motorsim.visual.workbench;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.Dimension;\r
5 import java.awt.event.ActionEvent;\r
6 import java.awt.event.ActionListener;\r
7 import java.awt.event.ComponentEvent;\r
8 import java.awt.event.ComponentListener;\r
9 import java.awt.event.FocusEvent;\r
10 import java.awt.event.FocusListener;\r
11 import java.beans.PropertyChangeEvent;\r
12 import java.beans.PropertyChangeListener;\r
13 import java.beans.PropertyVetoException;\r
14 import java.util.Collection;\r
15 import java.util.HashMap;\r
16 import java.util.List;\r
17 import java.util.Map;\r
18 import java.util.Vector;\r
19 \r
20 import javax.measure.quantity.Length;\r
21 import javax.measure.unit.SI;\r
22 import javax.swing.Box;\r
23 import javax.swing.BoxLayout;\r
24 import javax.swing.DefaultComboBoxModel;\r
25 import javax.swing.JButton;\r
26 import javax.swing.JComboBox;\r
27 import javax.swing.JFrame;\r
28 import javax.swing.JLabel;\r
29 import javax.swing.JPanel;\r
30 import javax.swing.JProgressBar;\r
31 import javax.swing.JSplitPane;\r
32 import javax.swing.JTabbedPane;\r
33 import javax.swing.JTextArea;\r
34 import javax.swing.JTextField;\r
35 import javax.swing.SwingUtilities;\r
36 import javax.swing.UIManager;\r
37 import javax.swing.WindowConstants;\r
38 \r
39 import org.apache.log4j.Logger;\r
40 import org.jscience.physics.amount.Amount;\r
41 \r
42 import com.billkuker.rocketry.motorsim.Burn;\r
43 import com.billkuker.rocketry.motorsim.Chamber;\r
44 import com.billkuker.rocketry.motorsim.ChangeListening;\r
45 import com.billkuker.rocketry.motorsim.ConvergentDivergentNozzle;\r
46 import com.billkuker.rocketry.motorsim.CylindricalChamber;\r
47 import com.billkuker.rocketry.motorsim.Fuel;\r
48 import com.billkuker.rocketry.motorsim.Grain;\r
49 import com.billkuker.rocketry.motorsim.Motor;\r
50 import com.billkuker.rocketry.motorsim.Nozzle;\r
51 import com.billkuker.rocketry.motorsim.RocketScience;\r
52 import com.billkuker.rocketry.motorsim.cases.Schedule40;\r
53 import com.billkuker.rocketry.motorsim.fuel.KNSU;\r
54 import com.billkuker.rocketry.motorsim.grain.CSlot;\r
55 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
56 import com.billkuker.rocketry.motorsim.grain.EndBurner;\r
57 import com.billkuker.rocketry.motorsim.grain.Finocyl;\r
58 import com.billkuker.rocketry.motorsim.grain.Moonburner;\r
59 import com.billkuker.rocketry.motorsim.grain.MultiGrain;\r
60 import com.billkuker.rocketry.motorsim.grain.RodAndTubeGrain;\r
61 import com.billkuker.rocketry.motorsim.grain.Star;\r
62 import com.billkuker.rocketry.motorsim.visual.BurnPanel;\r
63 import com.billkuker.rocketry.motorsim.visual.Editor;\r
64 import com.billkuker.rocketry.motorsim.visual.GrainPanel;\r
65 import com.billkuker.rocketry.motorsim.visual.HardwarePanel;\r
66 import com.billkuker.rocketry.motorsim.visual.SummaryPanel;\r
67 \r
68 public class MotorEditor extends JPanel implements PropertyChangeListener {\r
69         private static final long serialVersionUID = 1L;\r
70         private static Logger log = Logger.getLogger(MotorEditor.class);\r
71         Motor motor;\r
72         GrainEditor grainEditor;\r
73         BurnTab bt;\r
74         Burn burn;\r
75         SummaryPanel sp;\r
76         \r
77         JTabbedPane tabs;\r
78 \r
79         private Vector<BurnWatcher> burnWatchers = new Vector<BurnWatcher>();\r
80         private DefaultComboBoxModel availableFuels = new DefaultComboBoxModel();\r
81         \r
82         public void addFuel(Fuel f){\r
83                 availableFuels.addElement(f);\r
84         }\r
85 \r
86         //private static final int XML_TAB = 0;\r
87         private static final int CASING_TAB = 0;\r
88         private static final int GRAIN_TAB = 1;\r
89         private static final int BURN_TAB = 2;\r
90 \r
91         private List<Class<? extends Grain>> grainTypes = new Vector<Class<? extends Grain>>();\r
92         {\r
93                 grainTypes.add(CoredCylindricalGrain.class);\r
94                 grainTypes.add(Finocyl.class);\r
95                 grainTypes.add(Star.class);\r
96                 grainTypes.add(Moonburner.class);\r
97                 grainTypes.add(RodAndTubeGrain.class);\r
98                 grainTypes.add(CSlot.class);\r
99                 grainTypes.add(EndBurner.class);\r
100         }\r
101 \r
102         private abstract class Chooser<T> extends JPanel {\r
103                 private static final long serialVersionUID = 1L;\r
104                 private List<Class<? extends T>> types;\r
105                 private Map<Class<? extends T>, T> old = new HashMap<Class<? extends T>, T>();\r
106 \r
107                 @SuppressWarnings("unchecked")\r
108                 public Chooser(T initial, List<Class<? extends T>> ts) {\r
109                         types = ts;\r
110                         if ( initial != null )\r
111                                 old.put((Class<? extends T>)initial.getClass(), initial);\r
112                         setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
113                         for (final Class<? extends T> c : types) {\r
114                                 JButton b = new JButton(c.getSimpleName());\r
115                                 add(b);\r
116                                 b.addActionListener(new ActionListener() {\r
117                                         public void actionPerformed(ActionEvent e) {\r
118                                                 try {\r
119                                                         T val = old.get(c);\r
120                                                         if ( val == null ){\r
121                                                                 System.err.println("CREATED NEW =========================");\r
122                                                                 val = c.newInstance();\r
123                                                                 old.put(c, val);\r
124                                                         }\r
125                                                         choiceMade(val);\r
126                                                 } catch (InstantiationException e1) {\r
127                                                         e1.printStackTrace();\r
128                                                 } catch (IllegalAccessException e1) {\r
129                                                         e1.printStackTrace();\r
130                                                 }\r
131                                         }\r
132                                 });\r
133                         }\r
134                 }\r
135 \r
136                 protected abstract void choiceMade(T o);\r
137         }\r
138 \r
139         private class BurnTab extends JPanel {\r
140                 private static final long serialVersionUID = 1L;\r
141                 private Thread currentThread;\r
142                 \r
143                 public BurnTab() {\r
144                         setLayout(new BorderLayout());\r
145                         setName("Simulation Results");\r
146                         reBurn();\r
147                 }\r
148                 \r
149                 private class BurnCanceled extends RuntimeException{\r
150                         private static final long serialVersionUID = 1L;\r
151                 };\r
152 \r
153                 public void reBurn() {\r
154                         removeAll();\r
155                         currentThread = new Thread() {\r
156                                 public void run() {\r
157                                         final Thread me = this;\r
158                                         try {\r
159                                                 final Burn b = new Burn(motor);\r
160                                                 b.addBurnProgressListener(\r
161                                                                 new Burn.BurnProgressListener() {\r
162                                                                         @Override\r
163                                                                         public void burnComplete(){};\r
164                                                                         @Override\r
165                                                                         public void setProgress(float f) {\r
166                                                                                 if ( currentThread != me ){\r
167                                                                                         throw new BurnCanceled();\r
168                                                                                 }\r
169                                                                         }\r
170                                                                 });\r
171                                                 if ( sp != null )\r
172                                                         MotorEditor.this.remove(sp);\r
173                                                 MotorEditor.this.add(sp = new SummaryPanel(b), BorderLayout.NORTH);\r
174                                                 revalidate();\r
175                                                 b.burn();\r
176 \r
177                                                 final BurnPanel bp = new BurnPanel(b);\r
178                                                 SwingUtilities.invokeLater(new Thread() {\r
179                                                         public void run() {\r
180                                                                 add(bp, BorderLayout.CENTER);\r
181                                                                 for (BurnWatcher bw : burnWatchers)\r
182                                                                         bw.replace(burn, b);\r
183                                                                 burn = b;\r
184                                                                 revalidate();\r
185                                                         }\r
186                                                 });\r
187                                         } catch (BurnCanceled c){\r
188                                                 log.info("Burn Canceled!");\r
189                                         } catch (Exception e) {\r
190                                                 JTextArea t = new JTextArea(e.getMessage());\r
191                                                 t.setEditable(false);\r
192                                                 add(t);\r
193                                         }\r
194                                 }\r
195                         };\r
196                         currentThread.start();\r
197                 }\r
198         }\r
199 \r
200         private class GrainEditor extends JSplitPane {\r
201                 private static final long serialVersionUID = 1L;\r
202 \r
203                 public GrainEditor(final Grain g) {\r
204                         super(JSplitPane.HORIZONTAL_SPLIT);\r
205                         setName("Grain Geometry");\r
206                         setRightComponent(new GrainPanel(g));\r
207                         if (g instanceof Grain.Composite) {\r
208                                 final JPanel p = new JPanel();\r
209                                 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));\r
210                                 p.add(new Editor(g));\r
211                                 for (Grain gg : ((Grain.Composite) g).getGrains()) {\r
212                                         final int grainEditorIndex = p.getComponentCount() + 1;\r
213                                         p.add(new Chooser<Grain>(gg, grainTypes) {\r
214                                                 private static final long serialVersionUID = 1L;\r
215 \r
216                                                 @Override\r
217                                                 protected void choiceMade(Grain ng) {\r
218                                                         if (g instanceof MultiGrain) {\r
219                                                                 ((MultiGrain) g).setGrain(ng);\r
220                                                                 p.remove(grainEditorIndex);\r
221                                                                 p.add(new Editor(ng), grainEditorIndex);\r
222                                                                 p.remove(0);\r
223                                                                 p.add(new Editor(g), 0);\r
224                                                         }\r
225                                                 }\r
226                                         });\r
227                                         p.add(new Editor(gg));\r
228                                         if (gg instanceof ChangeListening.Subject) {\r
229                                                 ((ChangeListening.Subject) gg)\r
230                                                                 .addPropertyChangeListener(MotorEditor.this);\r
231                                         }\r
232                                 }\r
233                                 setLeftComponent(p);\r
234                         } else {\r
235                                 setLeftComponent(new Editor(g));\r
236                         }\r
237                         // setDividerLocation(.25);\r
238                         // setResizeWeight(.25);\r
239                         if (g instanceof ChangeListening.Subject) {\r
240                                 ((ChangeListening.Subject) g)\r
241                                                 .addPropertyChangeListener(MotorEditor.this);\r
242                         }\r
243                 }\r
244         }\r
245 \r
246         private class CaseEditor extends JSplitPane implements ComponentListener {\r
247                 private static final long serialVersionUID = 1L;\r
248 \r
249                 public CaseEditor(Nozzle n, Chamber c) {\r
250                         super(JSplitPane.VERTICAL_SPLIT);\r
251                         setName("General Parameters");\r
252                         this.addComponentListener(this);\r
253                         \r
254                         JPanel parts = new JPanel();\r
255                         parts.setLayout(new BoxLayout(parts, BoxLayout.X_AXIS));\r
256                         setTopComponent(parts);\r
257                         setBottomComponent(new HardwarePanel(n, c));\r
258                         \r
259                         JPanel nameAndFuel = new JPanel();\r
260                         nameAndFuel.setLayout(new BoxLayout(nameAndFuel, BoxLayout.Y_AXIS));\r
261 \r
262                         nameAndFuel.add(new JLabel("Name:"));\r
263                         nameAndFuel.add(new JTextField(motor.getName()) {\r
264                                 private static final long serialVersionUID = 1L;\r
265                                 {\r
266                                         setMinimumSize(new Dimension(200, 20));\r
267                                         setMaximumSize(new Dimension(Short.MAX_VALUE, 20));\r
268                                         final JTextField t = this;\r
269                                         addFocusListener(new FocusListener() {\r
270 \r
271                                                 @Override\r
272                                                 public void focusLost(FocusEvent e) {\r
273                                                         String n = t.getText();\r
274                                                         if (!"".equals(n) && !n.equals(motor.getName())) {\r
275                                                                 motor.setName(n);\r
276                                                         } else {\r
277                                                                 t.setText(motor.getName());\r
278                                                         }\r
279                                                 }\r
280 \r
281                                                 @Override\r
282                                                 public void focusGained(FocusEvent e) {\r
283 \r
284                                                 }\r
285                                         });\r
286 \r
287                                 }\r
288                         });\r
289                         nameAndFuel.add(new JLabel("Fuel:"));\r
290                         nameAndFuel.add( new JComboBox(availableFuels){\r
291                                 {\r
292                                         this.setSelectedItem(motor.getFuel());\r
293                                 }\r
294                                 private static final long serialVersionUID = 1L;\r
295                                 {\r
296                                         setMinimumSize(new Dimension(200, 20));\r
297                                         setMaximumSize(new Dimension(Short.MAX_VALUE, 20));\r
298                                         addActionListener(new ActionListener(){\r
299                                                 @Override\r
300                                                 public void actionPerformed(ActionEvent e) {\r
301                                                         motor.setFuel((Fuel)getSelectedItem());\r
302                                                         System.out.println("FUEL CHANGED");\r
303                                                 }});\r
304                                 }\r
305                         });\r
306                         nameAndFuel.add(Box.createVerticalGlue());\r
307                         parts.add(nameAndFuel);\r
308                         \r
309                         JPanel casing = new JPanel();\r
310                         casing.setLayout(new BoxLayout(casing, BoxLayout.Y_AXIS));\r
311                         casing.add(new JLabel("Casing:"));\r
312                         casing.add(new Editor(c));\r
313                         parts.add(casing);\r
314                         \r
315                         JPanel nozzle = new JPanel();\r
316                         nozzle.setLayout(new BoxLayout(nozzle, BoxLayout.Y_AXIS));\r
317                         nozzle.add(new JLabel("Nozzle:"));\r
318                         nozzle.add(new Editor(n));\r
319                         parts.add(nozzle);\r
320 \r
321                         if (n instanceof ChangeListening.Subject) {\r
322                                 ((ChangeListening.Subject) n)\r
323                                                 .addPropertyChangeListener(MotorEditor.this);\r
324                         }\r
325                         if (c instanceof ChangeListening.Subject) {\r
326                                 ((ChangeListening.Subject) c)\r
327                                                 .addPropertyChangeListener(MotorEditor.this);\r
328                         }\r
329                 }\r
330 \r
331                 @Override\r
332                 public void componentHidden(ComponentEvent arg0) {\r
333 \r
334                 }\r
335 \r
336                 @Override\r
337                 public void componentMoved(ComponentEvent arg0) {\r
338 \r
339                 }\r
340 \r
341                 @Override\r
342                 public void componentResized(ComponentEvent arg0) {\r
343                         setResizeWeight(.5);\r
344                         setDividerLocation(.5);\r
345                 }\r
346 \r
347                 @Override\r
348                 public void componentShown(ComponentEvent arg0) {\r
349 \r
350                 }\r
351         }\r
352 \r
353 \r
354         public MotorEditor(Motor m, Collection<Fuel> fuels) {\r
355                 \r
356                 setLayout( new BorderLayout());\r
357                 tabs = new JTabbedPane(JTabbedPane.BOTTOM);\r
358                 add(tabs, BorderLayout.CENTER);\r
359 \r
360                 for ( Fuel f : fuels )\r
361                         addFuel(f);\r
362                 setMotor(m);\r
363         }\r
364 \r
365         public Motor getMotor() {\r
366                 return motor;\r
367         }\r
368 \r
369 \r
370         private void setMotor(Motor m) {\r
371                 if (motor != null)\r
372                         motor.removePropertyChangeListener(this);\r
373                 motor = m;\r
374                 motor.addPropertyChangeListener(this);\r
375                 if (grainEditor != null)\r
376                         remove(grainEditor);\r
377                 while (tabs.getTabCount() > 1)\r
378                         tabs.removeTabAt(1);\r
379                 tabs.add(new CaseEditor(motor.getNozzle(), motor.getChamber()), CASING_TAB);\r
380                 tabs.add(new GrainEditor(motor.getGrain()), GRAIN_TAB);\r
381                 tabs.add(bt = new BurnTab(), BURN_TAB);\r
382         }\r
383 \r
384         public static Motor defaultMotor() {\r
385                 Motor m = new Motor();\r
386                 m.setName("Example Motor");\r
387                 m.setFuel(new KNSU());\r
388 \r
389                 CylindricalChamber c = new CylindricalChamber();\r
390                 c.setLength(Amount.valueOf(200, SI.MILLIMETER));\r
391                 c.setID(Amount.valueOf(30, SI.MILLIMETER));\r
392                 m.setChamber(c);\r
393 \r
394                 Schedule40 pvc = new Schedule40();\r
395                 pvc.setLength(Amount.valueOf(200, SI.MILLIMETER));\r
396                 m.setChamber(pvc);\r
397                 \r
398                 CoredCylindricalGrain g = new CoredCylindricalGrain();\r
399                 try {\r
400                         g.setLength(Amount.valueOf(70, SI.MILLIMETER));\r
401                         g.setOD(Amount.valueOf(30, SI.MILLIMETER));\r
402                         g.setID(Amount.valueOf(10, SI.MILLIMETER));\r
403                 } catch (PropertyVetoException v) {\r
404                         throw new Error(v);\r
405                 }\r
406                 \r
407 \r
408                 m.setGrain(new MultiGrain(g, 2));\r
409 \r
410                 ConvergentDivergentNozzle n = new ConvergentDivergentNozzle();\r
411                 n.setThroatDiameter(Amount.valueOf(7.962, SI.MILLIMETER));\r
412                 n.setExitDiameter(Amount.valueOf(13.79, SI.MILLIMETER));\r
413                 n.setEfficiency(.85);\r
414                 m.setNozzle(n);\r
415 \r
416                 return m;\r
417         }\r
418 \r
419         public void focusOnObject(Object o) {\r
420                 if (o instanceof Grain)\r
421                         tabs.setSelectedIndex(GRAIN_TAB);\r
422                 if (o instanceof Chamber || o instanceof Nozzle)\r
423                         tabs.setSelectedIndex(CASING_TAB);\r
424         }\r
425 \r
426         public void addBurnWatcher(BurnWatcher bw) {\r
427                 burnWatchers.add(bw);\r
428         }\r
429 \r
430         @Deprecated\r
431         public void showAsWindow() {\r
432                 JFrame f = new JFrame();\r
433                 f.setSize(1024, 768);\r
434                 f.setContentPane(this);\r
435                 f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\r
436                 f.setVisible(true);\r
437         }\r
438 \r
439         public static void main(String args[]) throws Exception {\r
440                 try {\r
441                         UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());\r
442                 } catch (Exception e1) {\r
443                         e1.printStackTrace();\r
444                 }\r
445                 Vector<Fuel> ff = new Vector<Fuel>();\r
446                 ff.add(new KNSU());\r
447                 //new MotorEditor(defaultMotor(), ff).showAsWindow();\r
448         }\r
449 \r
450         public void propertyChange(PropertyChangeEvent evt) {\r
451                 // Dont re-burn for a name change!\r
452                 if (!evt.getPropertyName().equals("Name")){\r
453                         bt.reBurn();\r
454                 } else {\r
455                         for (BurnWatcher bw : burnWatchers)\r
456                                 bw.replace(burn, burn);\r
457                 }\r
458         }\r
459 \r
460 }\r