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