altosui: Make deployment testing handle Connecting... dialog
[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
47         int             apogee_status;
48         int             main_status;
49
50         final static int        timeout = 1 * 1000;
51
52         int             time_remaining;
53         boolean         timer_running;
54
55         LinkedBlockingQueue<String>     command_queue;
56
57         class IgniteHandler implements Runnable {
58                 AltosIgnite     ignite;
59
60                 public void run () {
61                         for (;;) {
62                                 Runnable        r;
63
64                                 try {
65                                         String          command = command_queue.take();
66                                         String          reply = null;
67
68                                         if (command.equals("get_status")) {
69                                                 apogee_status = ignite.status(AltosIgnite.Apogee);
70                                                 main_status = ignite.status(AltosIgnite.Main);
71                                                 reply = "status";
72                                         } else if (command.equals("main")) {
73                                                 ignite.fire(AltosIgnite.Main);
74                                                 reply = "fired";
75                                         } else if (command.equals("apogee")) {
76                                                 ignite.fire(AltosIgnite.Apogee);
77                                                 reply = "fired";
78                                         } else if (command.equals("quit")) {
79                                                 ignite.close();
80                                                 break;
81                                         } else {
82                                                 throw new ParseException(String.format("invalid command %s", command), 0);
83                                         }
84                                         final String f_reply = reply;
85                                         r = new Runnable() {
86                                                         public void run() {
87                                                                 ignite_reply(f_reply);
88                                                         }
89                                                 };
90                                 } catch (Exception e) {
91                                         final Exception f_e = e;
92                                         r = new Runnable() {
93                                                         public void run() {
94                                                                 ignite_exception(f_e);
95                                                         }
96                                                 };
97                                 }
98                                 SwingUtilities.invokeLater(r);
99                         }
100                 }
101
102                 public IgniteHandler(AltosIgnite in_ignite) {
103                         ignite = in_ignite;
104                 }
105         }
106
107         void ignite_exception(Exception e) {
108                 abort();
109         }
110
111         void ignite_reply(String reply) {
112                 if (reply.equals("status")) {
113                         set_ignite_status();
114                 } else if (reply.equals("fired")) {
115                         fired();
116                 }
117         }
118
119         void set_arm_text() {
120                 if (arm.isSelected())
121                         arm.setText(String.format("%d", time_remaining));
122                 else
123                         arm.setText("Arm");
124         }
125
126         void start_timer() {
127                 time_remaining = 10;
128                 set_arm_text();
129                 timer_running = true;
130         }
131
132         void stop_timer() {
133                 time_remaining = 0;
134                 arm.setSelected(false);
135                 arm.setEnabled(false);
136                 fire.setEnabled(false);
137                 timer_running = false;
138                 set_arm_text();
139         }
140
141         void cancel () {
142                 apogee.setSelected(false);
143                 main.setSelected(false);
144                 fire.setEnabled(false);
145                 stop_timer();
146         }
147
148         void send_command(String command) {
149                 try {
150                         command_queue.put(command);
151                 } catch (Exception ex) {
152                         abort();
153                 }
154         }
155
156         boolean getting_status = false;
157
158         boolean visible = false;
159         void set_ignite_status() {
160                 getting_status = false;
161                 apogee_status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(apogee_status)));
162                 main_status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(main_status)));
163                 if (!visible) {
164                         visible = true;
165                         setVisible(true);
166                 }
167         }
168
169         void poll_ignite_status() {
170                 if (!getting_status) {
171                         getting_status = true;
172                         send_command("get_status");
173                 }
174         }
175
176         boolean firing = false;
177
178         void start_fire(String which) {
179                 if (!firing) {
180                         firing = true;
181                         send_command(which);
182                 }
183         }
184
185         void fired() {
186                 firing = false;
187                 cancel();
188         }
189
190         void close() {
191                 send_command("quit");
192                 timer.stop();
193                 setVisible(false);
194                 dispose();
195         }
196
197         void abort() {
198                 close();
199                 JOptionPane.showMessageDialog(owner,
200                                               String.format("Connection to \"%s\" failed",
201                                                             device.toShortString()),
202                                               "Connection Failed",
203                                               JOptionPane.ERROR_MESSAGE);
204         }
205
206         void tick_timer() {
207                 if (timer_running) {
208                         --time_remaining;
209                         if (time_remaining <= 0)
210                                 cancel();
211                         else
212                                 set_arm_text();
213                 }
214                 poll_ignite_status();
215         }
216
217         void fire() {
218                 if (arm.isEnabled() && arm.isSelected() && time_remaining > 0) {
219                         String  igniter = "none";
220                         if (apogee.isSelected() && !main.isSelected())
221                                 igniter = "apogee";
222                         else if (main.isSelected() && !apogee.isSelected())
223                                 igniter = "main";
224                         send_command(igniter);
225                         cancel();
226                 }
227         }
228
229         public void actionPerformed(ActionEvent e) {
230                 String cmd = e.getActionCommand();
231                 if (cmd.equals("apogee") || cmd.equals("main")) {
232                         stop_timer();
233                 }
234
235                 if (cmd.equals("apogee") && apogee.isSelected()) {
236                         main.setSelected(false);
237                         arm.setEnabled(true);
238                 }
239                 if (cmd.equals("main") && main.isSelected()) {
240                         apogee.setSelected(false);
241                         arm.setEnabled(true);
242                 }
243
244                 if (cmd.equals("arm")) {
245                         if (arm.isSelected()) {
246                                 fire.setEnabled(true);
247                                 start_timer();
248                         } else
249                                 cancel();
250                 }
251                 if (cmd.equals("fire"))
252                         fire();
253                 if (cmd.equals("tick"))
254                         tick_timer();
255                 if (cmd.equals("close")) {
256                         close();
257                 }
258         }
259
260         /* A window listener to catch closing events and tell the config code */
261         class ConfigListener extends WindowAdapter {
262                 AltosIgniteUI   ui;
263
264                 public ConfigListener(AltosIgniteUI this_ui) {
265                         ui = this_ui;
266                 }
267
268                 public void windowClosing(WindowEvent e) {
269                         ui.actionPerformed(new ActionEvent(e.getSource(),
270                                                            ActionEvent.ACTION_PERFORMED,
271                                                            "close"));
272                 }
273         }
274
275         private boolean open() {
276                 command_queue = new LinkedBlockingQueue<String>();
277
278                 device = AltosDeviceDialog.show(owner, AltosDevice.product_any);
279                 if (device != null) {
280                         try {
281                                 AltosIgnite     ignite = new AltosIgnite(device);
282                                 IgniteHandler   handler = new IgniteHandler(ignite);
283                                 Thread          t = new Thread(handler);
284                                 ignite.set_frame(owner);
285                                 t.start();
286                                 return true;
287                         } catch (FileNotFoundException ee) {
288                                 JOptionPane.showMessageDialog(owner,
289                                                               String.format("Cannot open device \"%s\"",
290                                                                             device.toShortString()),
291                                                               "Cannot open target device",
292                                                               JOptionPane.ERROR_MESSAGE);
293                         } catch (AltosSerialInUseException si) {
294                                 JOptionPane.showMessageDialog(owner,
295                                                               String.format("Device \"%s\" already in use",
296                                                                             device.toShortString()),
297                                                               "Device in use",
298                                                               JOptionPane.ERROR_MESSAGE);
299                         } catch (IOException ee) {
300                                 JOptionPane.showMessageDialog(owner,
301                                                               device.toShortString(),
302                                                               ee.getLocalizedMessage(),
303                                                               JOptionPane.ERROR_MESSAGE);
304                         }
305                 }
306                 return false;
307         }
308
309         public AltosIgniteUI(JFrame in_owner) {
310
311                 owner = in_owner;
312                 apogee_status = AltosIgnite.Unknown;
313                 main_status = AltosIgnite.Unknown;
314
315                 if (!open())
316                         return;
317
318                 Container               pane = getContentPane();
319                 GridBagConstraints      c = new GridBagConstraints();
320                 Insets                  i = new Insets(4,4,4,4);
321
322                 timer = new javax.swing.Timer(timeout, this);
323                 timer.setActionCommand("tick");
324                 timer_running = false;
325                 timer.restart();
326
327                 owner = in_owner;
328
329                 pane.setLayout(new GridBagLayout());
330
331                 c.fill = GridBagConstraints.NONE;
332                 c.anchor = GridBagConstraints.CENTER;
333                 c.insets = i;
334                 c.weightx = 1;
335                 c.weighty = 1;
336
337                 c.gridx = 0;
338                 c.gridy = 0;
339                 c.gridwidth = 2;
340                 c.anchor = GridBagConstraints.CENTER;
341                 label = new JLabel ("Fire Igniter");
342                 pane.add(label, c);
343
344                 c.gridx = 0;
345                 c.gridy = 1;
346                 c.gridwidth = 1;
347                 c.anchor = GridBagConstraints.WEST;
348                 apogee = new JRadioButton ("Apogee");
349                 pane.add(apogee, c);
350                 apogee.addActionListener(this);
351                 apogee.setActionCommand("apogee");
352
353                 c.gridx = 1;
354                 c.gridy = 1;
355                 c.gridwidth = 1;
356                 c.anchor = GridBagConstraints.WEST;
357                 apogee_status_label = new JLabel();
358                 pane.add(apogee_status_label, c);
359
360                 c.gridx = 0;
361                 c.gridy = 2;
362                 c.gridwidth = 1;
363                 c.anchor = GridBagConstraints.WEST;
364                 main = new JRadioButton ("Main");
365                 pane.add(main, c);
366                 main.addActionListener(this);
367                 main.setActionCommand("main");
368
369                 c.gridx = 1;
370                 c.gridy = 2;
371                 c.gridwidth = 1;
372                 c.anchor = GridBagConstraints.WEST;
373                 main_status_label = new JLabel();
374                 pane.add(main_status_label, c);
375
376                 c.gridx = 0;
377                 c.gridy = 3;
378                 c.gridwidth = 1;
379                 c.anchor = GridBagConstraints.CENTER;
380                 arm = new JToggleButton ("Arm");
381                 pane.add(arm, c);
382                 arm.addActionListener(this);
383                 arm.setActionCommand("arm");
384                 arm.setEnabled(false);
385
386                 c.gridx = 1;
387                 c.gridy = 3;
388                 c.gridwidth = 1;
389                 c.anchor = GridBagConstraints.CENTER;
390                 fire = new JButton ("Fire");
391                 fire.setEnabled(false);
392                 pane.add(fire, c);
393                 fire.addActionListener(this);
394                 fire.setActionCommand("fire");
395
396                 pack();
397                 setLocationRelativeTo(owner);
398
399                 addWindowListener(new ConfigListener(this));
400         }
401 }