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