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