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