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