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