add run-time dependency on libjfreechart-java
[fw/altos] / altosui / AltosIgniteUI.java
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package altosui;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import javax.swing.*;
23 import javax.swing.filechooser.FileNameExtensionFilter;
24 import javax.swing.table.*;
25 import javax.swing.event.*;
26 import java.io.*;
27 import java.util.*;
28 import java.text.*;
29 import java.util.prefs.*;
30 import java.util.concurrent.*;
31
32 public class AltosIgniteUI
33         extends JDialog
34         implements ActionListener
35 {
36         AltosDevice     device;
37         JFrame          owner;
38         JLabel          label;
39         JRadioButton    apogee;
40         JLabel          apogee_status_label;
41         JRadioButton    main;
42         JLabel          main_status_label;
43         JToggleButton   arm;
44         JButton         fire;
45         javax.swing.Timer       timer;
46         JButton         close;
47
48         int             apogee_status;
49         int             main_status;
50
51         final static int        timeout = 1 * 1000;
52
53         int             time_remaining;
54         boolean         timer_running;
55
56         LinkedBlockingQueue<String>     command_queue;
57
58         class IgniteHandler implements Runnable {
59                 AltosIgnite     ignite;
60                 JFrame          owner;
61
62                 void send_exception(Exception e) {
63                         final Exception f_e = e;
64                         Runnable r = new Runnable() {
65                                         public void run() {
66                                                 ignite_exception(f_e);
67                                         }
68                                 };
69                         SwingUtilities.invokeLater(r);
70                 }
71
72                 public void run () {
73                         try {
74                                 ignite = new AltosIgnite(device);
75                         } catch (Exception e) {
76                                 send_exception(e);
77                                 return;
78                         }
79                         ignite.set_frame(owner);
80
81                         for (;;) {
82                                 Runnable        r;
83
84                                 try {
85                                         String          command = command_queue.take();
86                                         String          reply = null;
87
88                                         if (command.equals("get_status")) {
89                                                 apogee_status = ignite.status(AltosIgnite.Apogee);
90                                                 main_status = ignite.status(AltosIgnite.Main);
91                                                 reply = "status";
92                                         } else if (command.equals("main")) {
93                                                 ignite.fire(AltosIgnite.Main);
94                                                 reply = "fired";
95                                         } else if (command.equals("apogee")) {
96                                                 ignite.fire(AltosIgnite.Apogee);
97                                                 reply = "fired";
98                                         } else if (command.equals("quit")) {
99                                                 ignite.close();
100                                                 break;
101                                         } else {
102                                                 throw new ParseException(String.format("invalid command %s", command), 0);
103                                         }
104                                         final String f_reply = reply;
105                                         r = new Runnable() {
106                                                         public void run() {
107                                                                 ignite_reply(f_reply);
108                                                         }
109                                                 };
110                                         SwingUtilities.invokeLater(r);
111                                 } catch (Exception e) {
112                                         send_exception(e);
113                                 }
114                         }
115                 }
116
117                 public IgniteHandler(JFrame in_owner) {
118                         owner = in_owner;
119                 }
120         }
121
122         void ignite_exception(Exception e) {
123                 if (e instanceof FileNotFoundException) {
124                         JOptionPane.showMessageDialog(owner,
125                                                       String.format("Cannot open device \"%s\"",
126                                                                     device.toShortString()),
127                                                       "Cannot open target device",
128                                                       JOptionPane.ERROR_MESSAGE);
129                 } else if (e instanceof AltosSerialInUseException) {
130                         JOptionPane.showMessageDialog(owner,
131                                                       String.format("Device \"%s\" already in use",
132                                                                     device.toShortString()),
133                                                       "Device in use",
134                                                       JOptionPane.ERROR_MESSAGE);
135                 } else if (e instanceof IOException) {
136                         IOException ee = (IOException) e;
137                         JOptionPane.showMessageDialog(owner,
138                                                       device.toShortString(),
139                                                       ee.getLocalizedMessage(),
140                                                       JOptionPane.ERROR_MESSAGE);
141                 } else {
142                         JOptionPane.showMessageDialog(owner,
143                                                       String.format("Connection to \"%s\" failed",
144                                                                     device.toShortString()),
145                                                       "Connection Failed",
146                                                       JOptionPane.ERROR_MESSAGE);
147                 }
148                 close();
149         }
150
151         void ignite_reply(String reply) {
152                 if (reply.equals("status")) {
153                         set_ignite_status();
154                 } else if (reply.equals("fired")) {
155                         fired();
156                 }
157         }
158
159         void set_arm_text() {
160                 if (arm.isSelected())
161                         arm.setText(String.format("%d", time_remaining));
162                 else
163                         arm.setText("Arm");
164         }
165
166         void start_timer() {
167                 time_remaining = 10;
168                 set_arm_text();
169                 timer_running = true;
170         }
171
172         void stop_timer() {
173                 time_remaining = 0;
174                 arm.setSelected(false);
175                 arm.setEnabled(false);
176                 fire.setEnabled(false);
177                 timer_running = false;
178                 set_arm_text();
179         }
180
181         void cancel () {
182                 apogee.setSelected(false);
183                 main.setSelected(false);
184                 fire.setEnabled(false);
185                 stop_timer();
186         }
187
188         void send_command(String command) {
189                 try {
190                         command_queue.put(command);
191                 } catch (Exception ex) {
192                         ignite_exception(ex);
193                 }
194         }
195
196         boolean getting_status = false;
197
198         boolean visible = false;
199         void set_ignite_status() {
200                 getting_status = false;
201                 apogee_status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(apogee_status)));
202                 main_status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(main_status)));
203                 if (!visible) {
204                         visible = true;
205                         setVisible(true);
206                 }
207         }
208
209         void poll_ignite_status() {
210                 if (!getting_status) {
211                         getting_status = true;
212                         send_command("get_status");
213                 }
214         }
215
216         boolean firing = false;
217
218         void start_fire(String which) {
219                 if (!firing) {
220                         firing = true;
221                         send_command(which);
222                 }
223         }
224
225         void fired() {
226                 firing = false;
227                 cancel();
228         }
229
230         void close() {
231                 send_command("quit");
232                 timer.stop();
233                 setVisible(false);
234                 dispose();
235         }
236
237         void tick_timer() {
238                 if (timer_running) {
239                         --time_remaining;
240                         if (time_remaining <= 0)
241                                 cancel();
242                         else
243                                 set_arm_text();
244                 }
245                 poll_ignite_status();
246         }
247
248         void fire() {
249                 if (arm.isEnabled() && arm.isSelected() && time_remaining > 0) {
250                         String  igniter = "none";
251                         if (apogee.isSelected() && !main.isSelected())
252                                 igniter = "apogee";
253                         else if (main.isSelected() && !apogee.isSelected())
254                                 igniter = "main";
255                         send_command(igniter);
256                         cancel();
257                 }
258         }
259
260         public void actionPerformed(ActionEvent e) {
261                 String cmd = e.getActionCommand();
262                 if (cmd.equals("apogee") || cmd.equals("main")) {
263                         stop_timer();
264                 }
265
266                 if (cmd.equals("apogee") && apogee.isSelected()) {
267                         main.setSelected(false);
268                         arm.setEnabled(true);
269                 }
270                 if (cmd.equals("main") && main.isSelected()) {
271                         apogee.setSelected(false);
272                         arm.setEnabled(true);
273                 }
274
275                 if (cmd.equals("arm")) {
276                         if (arm.isSelected()) {
277                                 fire.setEnabled(true);
278                                 start_timer();
279                         } else
280                                 cancel();
281                 }
282                 if (cmd.equals("fire"))
283                         fire();
284                 if (cmd.equals("tick"))
285                         tick_timer();
286                 if (cmd.equals("close")) {
287                         close();
288                 }
289         }
290
291         /* A window listener to catch closing events and tell the config code */
292         class ConfigListener extends WindowAdapter {
293                 AltosIgniteUI   ui;
294
295                 public ConfigListener(AltosIgniteUI this_ui) {
296                         ui = this_ui;
297                 }
298
299                 public void windowClosing(WindowEvent e) {
300                         ui.actionPerformed(new ActionEvent(e.getSource(),
301                                                            ActionEvent.ACTION_PERFORMED,
302                                                            "close"));
303                 }
304         }
305
306         private boolean open() {
307                 command_queue = new LinkedBlockingQueue<String>();
308
309                 device = AltosDeviceDialog.show(owner, Altos.product_any);
310                 if (device != null) {
311                                 IgniteHandler   handler = new IgniteHandler(owner);
312                                 Thread          t = new Thread(handler);
313                                 t.start();
314                                 return true;
315                 }
316                 return false;
317         }
318
319         public AltosIgniteUI(JFrame in_owner) {
320
321                 owner = in_owner;
322                 apogee_status = AltosIgnite.Unknown;
323                 main_status = AltosIgnite.Unknown;
324
325                 if (!open())
326                         return;
327
328                 Container               pane = getContentPane();
329                 GridBagConstraints      c = new GridBagConstraints();
330                 Insets                  i = new Insets(4,4,4,4);
331
332                 timer = new javax.swing.Timer(timeout, this);
333                 timer.setActionCommand("tick");
334                 timer_running = false;
335                 timer.restart();
336
337                 owner = in_owner;
338
339                 pane.setLayout(new GridBagLayout());
340
341                 c.fill = GridBagConstraints.NONE;
342                 c.anchor = GridBagConstraints.CENTER;
343                 c.insets = i;
344                 c.weightx = 0;
345                 c.weighty = 0;
346
347                 c.gridx = 0;
348                 c.gridy = 0;
349                 c.gridwidth = 2;
350                 c.anchor = GridBagConstraints.CENTER;
351                 label = new JLabel ("Fire Igniter");
352                 pane.add(label, c);
353
354                 c.gridx = 0;
355                 c.gridy = 1;
356                 c.gridwidth = 1;
357                 c.anchor = GridBagConstraints.WEST;
358                 apogee = new JRadioButton ("Apogee");
359                 pane.add(apogee, c);
360                 apogee.addActionListener(this);
361                 apogee.setActionCommand("apogee");
362
363                 c.gridx = 1;
364                 c.gridy = 1;
365                 c.gridwidth = 1;
366                 c.anchor = GridBagConstraints.WEST;
367                 apogee_status_label = new JLabel();
368                 pane.add(apogee_status_label, c);
369
370                 c.gridx = 0;
371                 c.gridy = 2;
372                 c.gridwidth = 1;
373                 c.anchor = GridBagConstraints.WEST;
374                 main = new JRadioButton ("Main");
375                 pane.add(main, c);
376                 main.addActionListener(this);
377                 main.setActionCommand("main");
378
379                 c.gridx = 1;
380                 c.gridy = 2;
381                 c.gridwidth = 1;
382                 c.anchor = GridBagConstraints.WEST;
383                 main_status_label = new JLabel();
384                 pane.add(main_status_label, c);
385
386                 c.gridx = 0;
387                 c.gridy = 3;
388                 c.gridwidth = 1;
389                 c.anchor = GridBagConstraints.CENTER;
390                 arm = new JToggleButton ("Arm");
391                 pane.add(arm, c);
392                 arm.addActionListener(this);
393                 arm.setActionCommand("arm");
394                 arm.setEnabled(false);
395
396                 c.gridx = 1;
397                 c.gridy = 3;
398                 c.gridwidth = 1;
399                 c.anchor = GridBagConstraints.CENTER;
400                 fire = new JButton ("Fire");
401                 fire.setEnabled(false);
402                 pane.add(fire, c);
403                 fire.addActionListener(this);
404                 fire.setActionCommand("fire");
405
406                 c.gridx = 0;
407                 c.gridy = 4;
408                 c.gridwidth = 2;
409                 c.anchor = GridBagConstraints.CENTER;
410                 close = new JButton ("Close");
411                 pane.add(close, c);
412                 close.addActionListener(this);
413                 close.setActionCommand("close");
414                         
415                 pack();
416                 setLocationRelativeTo(owner);
417
418                 addWindowListener(new ConfigListener(this));
419         }
420 }