39b986c0a129f0dde62a7533be54027bd9bb3ea9
[fw/altos] / altosui / AltosLaunchUI.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
27 class FireButton extends JButton {
28         protected void processMouseEvent(MouseEvent e) {
29                 super.processMouseEvent(e);
30                 switch (e.getID()) {
31                 case MouseEvent.MOUSE_PRESSED:
32                         if (actionListener != null)
33                                 actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_down"));
34                         break;
35                 case MouseEvent.MOUSE_RELEASED:
36                         if (actionListener != null)
37                                 actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_up"));
38                         break;
39                 }
40         }
41
42         public FireButton(String s) {
43                 super(s);
44         }
45 }
46
47 public class AltosLaunchUI
48         extends AltosDialog
49         implements ActionListener
50 {
51         AltosDevice     device;
52         JFrame          owner;
53         JLabel          label;
54
55         int             radio_channel;
56         JLabel          radio_channel_label;
57         JTextField      radio_channel_text;
58
59         int             launcher_serial;
60         JLabel          launcher_serial_label;
61         JTextField      launcher_serial_text;
62
63         int             launcher_channel;
64         JLabel          launcher_channel_label;
65         JTextField      launcher_channel_text;
66
67         JLabel          armed_label;
68         JLabel          armed_status_label;
69         JLabel          igniter;
70         JLabel          igniter_status_label;
71         JToggleButton   arm;
72         FireButton      fire;
73         javax.swing.Timer       arm_timer;
74         javax.swing.Timer       fire_timer;
75
76         boolean         firing;
77         boolean         armed;
78         int             armed_status;
79         int             igniter_status;
80         int             rssi;
81
82         final static int        arm_timeout = 1 * 1000;
83         final static int        fire_timeout = 250;
84
85         int             armed_count;
86
87         LinkedBlockingQueue<String>     command_queue;
88
89         class LaunchHandler implements Runnable {
90                 AltosLaunch     launch;
91                 JFrame          owner;
92
93                 void send_exception(Exception e) {
94                         final Exception f_e = e;
95                         Runnable r = new Runnable() {
96                                         public void run() {
97                                                 launch_exception(f_e);
98                                         }
99                                 };
100                         SwingUtilities.invokeLater(r);
101                 }
102
103                 public void run () {
104                         try {
105                                 launch = new AltosLaunch(device);
106                         } catch (Exception e) {
107                                 send_exception(e);
108                                 return;
109                         }
110                         launch.set_frame(owner);
111                         launch.set_remote(launcher_serial, launcher_channel);
112
113                         for (;;) {
114                                 Runnable        r;
115
116                                 try {
117                                         String          command = command_queue.take();
118                                         String          reply = null;
119
120                                         if (command.equals("get_status")) {
121                                                 launch.status();
122                                                 reply = "status";
123                                                 armed_status = launch.armed;
124                                                 igniter_status = launch.igniter;
125                                                 rssi = launch.rssi;
126                                         } else if (command.equals("set_remote")) {
127                                                 launch.set_remote(launcher_serial, launcher_channel);
128                                                 reply = "remote set";
129                                         } else if (command.equals("arm")) {
130                                                 launch.arm();
131                                                 reply = "armed";
132                                         } else if (command.equals("fire")) {
133                                                 launch.fire();
134                                                 reply = "fired";
135                                         } else if (command.equals("quit")) {
136                                                 launch.close();
137                                                 break;
138                                         } else {
139                                                 throw new ParseException(String.format("invalid command %s", command), 0);
140                                         }
141                                         final String f_reply = reply;
142                                         r = new Runnable() {
143                                                         public void run() {
144                                                                 launch_reply(f_reply);
145                                                         }
146                                                 };
147                                         SwingUtilities.invokeLater(r);
148                                 } catch (Exception e) {
149                                         send_exception(e);
150                                 }
151                         }
152                 }
153
154                 public LaunchHandler(JFrame in_owner) {
155                         owner = in_owner;
156                 }
157         }
158
159         void launch_exception(Exception e) {
160                 if (e instanceof FileNotFoundException) {
161                         JOptionPane.showMessageDialog(owner,
162                                                       ((FileNotFoundException) e).getMessage(),
163                                                       "Cannot open target device",
164                                                       JOptionPane.ERROR_MESSAGE);
165                 } else if (e instanceof AltosSerialInUseException) {
166                         JOptionPane.showMessageDialog(owner,
167                                                       String.format("Device \"%s\" already in use",
168                                                                     device.toShortString()),
169                                                       "Device in use",
170                                                       JOptionPane.ERROR_MESSAGE);
171                 } else if (e instanceof IOException) {
172                         IOException ee = (IOException) e;
173                         JOptionPane.showMessageDialog(owner,
174                                                       device.toShortString(),
175                                                       ee.getLocalizedMessage(),
176                                                       JOptionPane.ERROR_MESSAGE);
177                 } else {
178                         JOptionPane.showMessageDialog(owner,
179                                                       String.format("Connection to \"%s\" failed",
180                                                                     device.toShortString()),
181                                                       "Connection Failed",
182                                                       JOptionPane.ERROR_MESSAGE);
183                 }
184                 close();
185         }
186
187         void launch_reply(String reply) {
188                 if (reply == null)
189                         return;
190                 if (reply.equals("remote set"))
191                         poll_launch_status();
192                 if (reply.equals("status")) {
193                         set_launch_status();
194                 }
195         }
196
197         void set_arm_text() {
198                 if (arm.isSelected())
199                         arm.setText(String.format("%d", armed_count));
200                 else
201                         arm.setText("Arm");
202         }
203
204         void start_arm_timer() {
205                 armed_count = 30;
206                 set_arm_text();
207         }
208
209         void stop_arm_timer() {
210                 armed_count = 0;
211                 armed = false;
212                 arm.setSelected(false);
213                 fire.setEnabled(false);
214                 set_arm_text();
215         }
216
217         void cancel () {
218                 fire.setEnabled(false);
219                 firing = false;
220                 stop_arm_timer();
221         }
222
223         void send_command(String command) {
224                 try {
225                         command_queue.put(command);
226                 } catch (Exception ex) {
227                         launch_exception(ex);
228                 }
229         }
230
231         boolean getting_status = false;
232
233         void set_launch_status() {
234                 getting_status = false;
235                 armed_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(armed_status)));
236                 igniter_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(igniter_status)));
237         }
238
239         void poll_launch_status() {
240                 if (!getting_status && !firing && !armed) {
241                         getting_status = true;
242                         send_command("get_status");
243                 }
244         }
245
246         void fired() {
247                 firing = false;
248                 cancel();
249         }
250
251         void close() {
252                 send_command("quit");
253                 arm_timer.stop();
254                 setVisible(false);
255                 dispose();
256         }
257
258         void tick_arm_timer() {
259                 if (armed_count > 0) {
260                         --armed_count;
261                         if (armed_count <= 0) {
262                                 armed_count = 0;
263                                 cancel();
264                         } else {
265                                 if (!firing) {
266                                         send_command("arm");
267                                         set_arm_text();
268                                 }
269                         }
270                 }
271                 poll_launch_status();
272         }
273
274         void arm() {
275                 if (arm.isSelected()) {
276                         fire.setEnabled(true);
277                         start_arm_timer();
278                         if (!firing)
279                                 send_command("arm");
280                         armed = true;
281                 } else
282                         cancel();
283         }
284
285         void fire_more() {
286                 if (firing)
287                         send_command("fire");
288         }
289
290         void fire_down() {
291                 if (arm.isEnabled() && arm.isSelected() && armed_count > 0) {
292                         firing = true;
293                         fire_more();
294                         fire_timer.restart();
295                 }
296         }
297
298         void fire_up() {
299                 firing = false;
300                 fire_timer.stop();
301         }
302
303         void set_radio() {
304                 try {
305                         radio_channel = Integer.parseInt(radio_channel_text.getText());
306                 } catch (NumberFormatException ne) {
307                         radio_channel_text.setText(String.format("%d", radio_channel));
308                 }
309         }
310
311         void set_serial() {
312                 try {
313                         launcher_serial = Integer.parseInt(launcher_serial_text.getText());
314                         AltosUIPreferences.set_launcher_serial(launcher_serial);
315                         send_command("set_remote");
316                 } catch (NumberFormatException ne) {
317                         launcher_serial_text.setText(String.format("%d", launcher_serial));
318                 }
319         }
320
321         void set_channel() {
322                 try {
323                         launcher_channel = Integer.parseInt(launcher_channel_text.getText());
324                         AltosUIPreferences.set_launcher_serial(launcher_channel);
325                         send_command("set_remote");
326                 } catch (NumberFormatException ne) {
327                         launcher_channel_text.setText(String.format("%d", launcher_channel));
328                 }
329         }
330
331         public void actionPerformed(ActionEvent e) {
332                 String cmd = e.getActionCommand();
333                 if (cmd.equals("armed") || cmd.equals("igniter")) {
334                         stop_arm_timer();
335                 }
336
337                 if (cmd.equals("arm"))
338                         arm();
339                 if (cmd.equals("tick_arm"))
340                         tick_arm_timer();
341                 if (cmd.equals("close"))
342                         close();
343                 if (cmd.equals("fire_down"))
344                         fire_down();
345                 if (cmd.equals("fire_up"))
346                         fire_up();
347                 if (cmd.equals("tick_fire"))
348                         fire_more();
349                 if (cmd.equals("new_serial"))
350                         set_serial();
351                 if (cmd.equals("new_channel"))
352                         set_channel();
353         }
354
355         /* A window listener to catch closing events and tell the config code */
356         class ConfigListener extends WindowAdapter {
357                 AltosLaunchUI   ui;
358
359                 public ConfigListener(AltosLaunchUI this_ui) {
360                         ui = this_ui;
361                 }
362
363                 public void windowClosing(WindowEvent e) {
364                         ui.actionPerformed(new ActionEvent(e.getSource(),
365                                                            ActionEvent.ACTION_PERFORMED,
366                                                            "close"));
367                 }
368         }
369
370         private boolean open() {
371                 command_queue = new LinkedBlockingQueue<String>();
372
373                 device = AltosDeviceDialog.show(owner, Altos.product_any);
374                 if (device != null) {
375                                 LaunchHandler   handler = new LaunchHandler(owner);
376                                 Thread          t = new Thread(handler);
377                                 t.start();
378                                 return true;
379                 }
380                 return false;
381         }
382
383         public AltosLaunchUI(JFrame in_owner) {
384
385                 launcher_channel = AltosUIPreferences.launcher_channel();
386                 launcher_serial = AltosUIPreferences.launcher_serial();
387                 owner = in_owner;
388                 armed_status = AltosLaunch.Unknown;
389                 igniter_status = AltosLaunch.Unknown;
390
391                 if (!open())
392                         return;
393
394                 Container               pane = getContentPane();
395                 GridBagConstraints      c = new GridBagConstraints();
396                 Insets                  i = new Insets(4,4,4,4);
397
398                 arm_timer = new javax.swing.Timer(arm_timeout, this);
399                 arm_timer.setActionCommand("tick_arm");
400                 arm_timer.restart();
401
402                 fire_timer = new javax.swing.Timer(fire_timeout, this);
403                 fire_timer.setActionCommand("tick_fire");
404
405                 owner = in_owner;
406
407                 pane.setLayout(new GridBagLayout());
408
409                 c.fill = GridBagConstraints.NONE;
410                 c.anchor = GridBagConstraints.CENTER;
411                 c.insets = i;
412                 c.weightx = 1;
413                 c.weighty = 1;
414
415                 c.gridx = 0;
416                 c.gridy = 0;
417                 c.gridwidth = 2;
418                 c.anchor = GridBagConstraints.CENTER;
419                 label = new JLabel ("Launch Controller");
420                 pane.add(label, c);
421
422                 c.gridx = 0;
423                 c.gridy = 1;
424                 c.gridwidth = 1;
425                 c.anchor = GridBagConstraints.WEST;
426                 launcher_serial_label = new JLabel("Launcher Serial");
427                 pane.add(launcher_serial_label, c);
428
429                 c.gridx = 1;
430                 c.gridy = 1;
431                 c.gridwidth = 1;
432                 c.anchor = GridBagConstraints.WEST;
433                 launcher_serial_text = new JTextField(7);
434                 launcher_serial_text.setText(String.format("%d", launcher_serial));
435                 launcher_serial_text.setActionCommand("new_serial");
436                 launcher_serial_text.addActionListener(this);
437                 pane.add(launcher_serial_text, c);
438
439                 c.gridx = 0;
440                 c.gridy = 2;
441                 c.gridwidth = 1;
442                 c.anchor = GridBagConstraints.WEST;
443                 launcher_channel_label = new JLabel("Launcher Channel");
444                 pane.add(launcher_channel_label, c);
445
446                 c.gridx = 1;
447                 c.gridy = 2;
448                 c.gridwidth = 1;
449                 c.anchor = GridBagConstraints.WEST;
450                 launcher_channel_text = new JTextField(7);
451                 launcher_channel_text.setText(String.format("%d", launcher_channel));
452                 launcher_channel_text.setActionCommand("new_channel");
453                 launcher_channel_text.addActionListener(this);
454                 pane.add(launcher_channel_text, c);
455
456                 c.gridx = 0;
457                 c.gridy = 3;
458                 c.gridwidth = 1;
459                 c.anchor = GridBagConstraints.WEST;
460                 armed_label = new JLabel ("Armed");
461                 pane.add(armed_label, c);
462
463                 c.gridx = 1;
464                 c.gridy = 3;
465                 c.gridwidth = 1;
466                 c.anchor = GridBagConstraints.WEST;
467                 armed_status_label = new JLabel();
468                 pane.add(armed_status_label, c);
469
470                 c.gridx = 0;
471                 c.gridy = 4;
472                 c.gridwidth = 1;
473                 c.anchor = GridBagConstraints.WEST;
474                 igniter = new JLabel ("Igniter");
475                 pane.add(igniter, c);
476
477                 c.gridx = 1;
478                 c.gridy = 4;
479                 c.gridwidth = 1;
480                 c.anchor = GridBagConstraints.WEST;
481                 igniter_status_label = new JLabel();
482                 pane.add(igniter_status_label, c);
483
484                 c.gridx = 0;
485                 c.gridy = 5;
486                 c.gridwidth = 1;
487                 c.anchor = GridBagConstraints.CENTER;
488                 arm = new JToggleButton ("Arm");
489                 pane.add(arm, c);
490                 arm.addActionListener(this);
491                 arm.setActionCommand("arm");
492                 arm.setEnabled(true);
493
494                 c.gridx = 1;
495                 c.gridy = 5;
496                 c.gridwidth = 1;
497                 c.anchor = GridBagConstraints.CENTER;
498                 fire = new FireButton ("Fire");
499                 fire.setEnabled(false);
500                 pane.add(fire, c);
501                 fire.addActionListener(this);
502                 fire.setActionCommand("fire");
503
504                 pack();
505                 setLocationRelativeTo(owner);
506
507                 addWindowListener(new ConfigListener(this));
508
509                 setVisible(true);
510         }
511 }