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