2 * Copyright © 2010 Keith Packard <keithp@keithp.com>
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.
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.
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.
22 import java.awt.event.*;
26 import java.util.concurrent.*;
27 import org.altusmetrum.altosuilib_12.*;
29 class FireButton extends JButton {
30 protected void processMouseEvent(MouseEvent e) {
31 super.processMouseEvent(e);
33 case MouseEvent.MOUSE_PRESSED:
34 if (actionListener != null)
35 actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_down"));
37 case MouseEvent.MOUSE_RELEASED:
38 if (actionListener != null)
39 actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_up"));
44 public FireButton(String s) {
49 public class AltosLaunchUI
51 implements ActionListener
58 JLabel radio_channel_label;
59 JTextField radio_channel_text;
62 JLabel launcher_serial_label;
63 JTextField launcher_serial_text;
66 JLabel launcher_channel_label;
67 JTextField launcher_channel_text;
70 JLabel armed_status_label;
72 JLabel igniter_status_label;
75 javax.swing.Timer arm_timer;
76 javax.swing.Timer fire_timer;
84 final static int arm_timeout = 1 * 1000;
85 final static int fire_timeout = 250;
89 LinkedBlockingQueue<String> command_queue;
91 class LaunchHandler implements Runnable {
95 void send_exception(Exception e) {
96 final Exception f_e = e;
97 Runnable r = new Runnable() {
99 launch_exception(f_e);
102 SwingUtilities.invokeLater(r);
107 launch = new AltosLaunch(device);
108 } catch (Exception e) {
112 launch.set_frame(owner);
113 launch.set_remote(launcher_serial, launcher_channel);
119 String command = command_queue.take();
122 if (command.equals("get_status")) {
125 armed_status = launch.armed;
126 igniter_status = launch.igniter;
128 } else if (command.equals("set_remote")) {
129 launch.set_remote(launcher_serial, launcher_channel);
130 reply = "remote set";
131 } else if (command.equals("arm")) {
134 } else if (command.equals("fire")) {
137 } else if (command.equals("quit")) {
141 throw new ParseException(String.format("invalid command %s", command), 0);
143 final String f_reply = reply;
146 launch_reply(f_reply);
149 SwingUtilities.invokeLater(r);
150 } catch (Exception e) {
156 public LaunchHandler(JFrame in_owner) {
161 void launch_exception(Exception e) {
162 if (e instanceof FileNotFoundException) {
163 JOptionPane.showMessageDialog(owner,
164 ((FileNotFoundException) e).getMessage(),
165 "Cannot open target device",
166 JOptionPane.ERROR_MESSAGE);
167 } else if (e instanceof AltosSerialInUseException) {
168 JOptionPane.showMessageDialog(owner,
169 String.format("Device \"%s\" already in use",
170 device.toShortString()),
172 JOptionPane.ERROR_MESSAGE);
173 } else if (e instanceof IOException) {
174 IOException ee = (IOException) e;
175 JOptionPane.showMessageDialog(owner,
176 device.toShortString(),
177 ee.getLocalizedMessage(),
178 JOptionPane.ERROR_MESSAGE);
180 JOptionPane.showMessageDialog(owner,
181 String.format("Connection to \"%s\" failed",
182 device.toShortString()),
184 JOptionPane.ERROR_MESSAGE);
189 void launch_reply(String reply) {
192 if (reply.equals("remote set"))
193 poll_launch_status();
194 if (reply.equals("status")) {
199 void set_arm_text() {
200 if (arm.isSelected())
201 arm.setText(String.format("%d", armed_count));
206 void start_arm_timer() {
211 void stop_arm_timer() {
214 arm.setSelected(false);
215 fire.setEnabled(false);
220 fire.setEnabled(false);
225 void send_command(String command) {
227 command_queue.put(command);
228 } catch (Exception ex) {
229 launch_exception(ex);
233 boolean getting_status = false;
235 void set_launch_status() {
236 getting_status = false;
237 armed_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(armed_status)));
238 igniter_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(igniter_status)));
241 void poll_launch_status() {
242 if (!getting_status && !firing && !armed) {
243 getting_status = true;
244 send_command("get_status");
254 send_command("quit");
260 void tick_arm_timer() {
261 if (armed_count > 0) {
263 if (armed_count <= 0) {
273 poll_launch_status();
277 if (arm.isSelected()) {
278 fire.setEnabled(true);
289 send_command("fire");
293 if (arm.isEnabled() && arm.isSelected() && armed_count > 0) {
296 fire_timer.restart();
307 radio_channel = Integer.parseInt(radio_channel_text.getText());
308 } catch (NumberFormatException ne) {
309 radio_channel_text.setText(String.format("%d", radio_channel));
315 launcher_serial = Integer.parseInt(launcher_serial_text.getText());
316 AltosUIPreferences.set_launcher_serial(launcher_serial);
317 send_command("set_remote");
318 } catch (NumberFormatException ne) {
319 launcher_serial_text.setText(String.format("%d", launcher_serial));
325 launcher_channel = Integer.parseInt(launcher_channel_text.getText());
326 AltosUIPreferences.set_launcher_serial(launcher_channel);
327 send_command("set_remote");
328 } catch (NumberFormatException ne) {
329 launcher_channel_text.setText(String.format("%d", launcher_channel));
333 public void actionPerformed(ActionEvent e) {
334 String cmd = e.getActionCommand();
335 if (cmd.equals("armed") || cmd.equals("igniter")) {
339 if (cmd.equals("arm"))
341 if (cmd.equals("tick_arm"))
343 if (cmd.equals("close"))
345 if (cmd.equals("fire_down"))
347 if (cmd.equals("fire_up"))
349 if (cmd.equals("tick_fire"))
351 if (cmd.equals("new_serial"))
353 if (cmd.equals("new_channel"))
357 /* A window listener to catch closing events and tell the config code */
358 class ConfigListener extends WindowAdapter {
361 public ConfigListener(AltosLaunchUI this_ui) {
365 public void windowClosing(WindowEvent e) {
366 ui.actionPerformed(new ActionEvent(e.getSource(),
367 ActionEvent.ACTION_PERFORMED,
372 private boolean open() {
373 command_queue = new LinkedBlockingQueue<String>();
375 device = AltosDeviceUIDialog.show(owner, Altos.product_any);
376 if (device != null) {
377 LaunchHandler handler = new LaunchHandler(owner);
378 Thread t = new Thread(handler);
385 public AltosLaunchUI(JFrame in_owner) {
387 launcher_channel = AltosUIPreferences.launcher_channel();
388 launcher_serial = AltosUIPreferences.launcher_serial();
390 armed_status = AltosLaunch.Unknown;
391 igniter_status = AltosLaunch.Unknown;
396 Container pane = getContentPane();
397 GridBagConstraints c = new GridBagConstraints();
398 Insets i = new Insets(4,4,4,4);
400 arm_timer = new javax.swing.Timer(arm_timeout, this);
401 arm_timer.setActionCommand("tick_arm");
404 fire_timer = new javax.swing.Timer(fire_timeout, this);
405 fire_timer.setActionCommand("tick_fire");
409 pane.setLayout(new GridBagLayout());
411 c.fill = GridBagConstraints.NONE;
412 c.anchor = GridBagConstraints.CENTER;
420 c.anchor = GridBagConstraints.CENTER;
421 label = new JLabel ("Launch Controller");
427 c.anchor = GridBagConstraints.WEST;
428 launcher_serial_label = new JLabel("Launcher Serial");
429 pane.add(launcher_serial_label, c);
434 c.anchor = GridBagConstraints.WEST;
435 launcher_serial_text = new JTextField(7);
436 launcher_serial_text.setText(String.format("%d", launcher_serial));
437 launcher_serial_text.setActionCommand("new_serial");
438 launcher_serial_text.addActionListener(this);
439 pane.add(launcher_serial_text, c);
444 c.anchor = GridBagConstraints.WEST;
445 launcher_channel_label = new JLabel("Launcher Channel");
446 pane.add(launcher_channel_label, c);
451 c.anchor = GridBagConstraints.WEST;
452 launcher_channel_text = new JTextField(7);
453 launcher_channel_text.setText(String.format("%d", launcher_channel));
454 launcher_channel_text.setActionCommand("new_channel");
455 launcher_channel_text.addActionListener(this);
456 pane.add(launcher_channel_text, c);
461 c.anchor = GridBagConstraints.WEST;
462 armed_label = new JLabel ("Armed");
463 pane.add(armed_label, c);
468 c.anchor = GridBagConstraints.WEST;
469 armed_status_label = new JLabel();
470 pane.add(armed_status_label, c);
475 c.anchor = GridBagConstraints.WEST;
476 igniter = new JLabel ("Igniter");
477 pane.add(igniter, c);
482 c.anchor = GridBagConstraints.WEST;
483 igniter_status_label = new JLabel();
484 pane.add(igniter_status_label, c);
489 c.anchor = GridBagConstraints.CENTER;
490 arm = new JToggleButton ("Arm");
492 arm.addActionListener(this);
493 arm.setActionCommand("arm");
494 arm.setEnabled(true);
499 c.anchor = GridBagConstraints.CENTER;
500 fire = new FireButton ("Fire");
501 fire.setEnabled(false);
503 fire.addActionListener(this);
504 fire.setActionCommand("fire");
507 setLocationRelativeTo(owner);
509 addWindowListener(new ConfigListener(this));