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