altos/test: Adjust CRC error rate after FEC fix
[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_14.*;
29 import org.altusmetrum.altosuilib_14.*;
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         boolean         has_standard;
46         int             npyro;
47
48         final static int        timeout = 1 * 1000;
49
50         int             time_remaining;
51         boolean         timer_running;
52
53         int             poll_remaining;
54
55         LinkedBlockingQueue<String>     command_queue;
56
57         class Igniter {
58                 JRadioButton    button;
59                 JLabel          status_label;
60                 String          name;
61                 int             status;
62
63                 void set_status (int status) {
64                         this.status = status;
65                         status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(status)));
66                 }
67
68                 Igniter(AltosIgniteUI ui, String label, String name, int y) {
69                         Container               pane = getScrollablePane();
70                         GridBagConstraints      c = new GridBagConstraints();
71                         Insets                  i = new Insets(4,4,4,4);
72
73                         this.name = name;
74                         this.status = AltosIgnite.Unknown;
75
76                         c.gridx = 0;
77                         c.gridy = y;
78                         c.gridwidth = 1;
79                         c.anchor = GridBagConstraints.WEST;
80                         button = new JRadioButton (label);
81                         pane.add(button, c);
82                         button.addActionListener(ui);
83                         button.setActionCommand(name);
84                         group.add(button);
85
86                         c.gridx = 1;
87                         c.gridy = y;
88                         c.gridwidth = 1;
89                         c.anchor = GridBagConstraints.WEST;
90                         status_label = new JLabel("plenty of text");
91                         pane.add(status_label, c);
92
93                         status = AltosIgnite.Unknown;
94                 }
95         }
96
97         Igniter igniters[];
98
99         void set_status(String _name, int _status) {
100
101                 final String name = _name;
102                 final int status = _status;
103                 Runnable r = new Runnable() {
104                                 public void run() {
105                                         for (int p = 0; p < igniters.length; p++)
106                                                 if (name.equals(igniters[p].name))
107                                                         igniters[p].set_status(status);
108                                 }
109                         };
110                 SwingUtilities.invokeLater(r);
111         }
112
113         class IgniteHandler implements Runnable {
114                 AltosIgnite     ignite;
115                 JFrame          owner;
116                 AltosLink       link;
117
118                 void send_exception(Exception e) {
119                         final Exception f_e = e;
120                         Runnable r = new Runnable() {
121                                         public void run() {
122                                                 ignite_exception(f_e);
123                                         }
124                                 };
125                         SwingUtilities.invokeLater(r);
126                 }
127
128                 public void run () {
129                         try {
130                                 ignite = new AltosIgnite(link,
131                                                          device.matchProduct(Altos.product_basestation));
132
133                         } catch (Exception e) {
134                                 send_exception(e);
135                                 return;
136                         }
137
138                         for (;;) {
139                                 Runnable        r;
140
141                                 try {
142                                         String          command = command_queue.take();
143                                         String          reply = null;
144
145                                         if (command.equals("get_status")) {
146                                                 HashMap<String,Integer> status_map = ignite.status();
147
148                                                 for (int p = 0; p < igniters.length; p++) {
149                                                         Integer i = status_map.get(igniters[p].name);
150                                                         if (i != null)
151                                                                 set_status(igniters[p].name, i);
152                                                 }
153                                                 reply = "status";
154                                         } else if (command.equals("get_npyro")) {
155                                                 reply = String.format("npyro %d %d", ignite.npyro(), ignite.has_standard() ? 1 : 0);
156                                         } else if (command.equals("quit")) {
157                                                 ignite.close();
158                                                 break;
159                                         } else {
160                                                 ignite.fire(command);
161                                                 reply = "fired";
162                                         }
163                                         final String f_reply = reply;
164                                         r = new Runnable() {
165                                                         public void run() {
166                                                                 ignite_reply(f_reply);
167                                                         }
168                                                 };
169                                         SwingUtilities.invokeLater(r);
170                                 } catch (Exception e) {
171                                         send_exception(e);
172                                 }
173                         }
174                 }
175
176                 public IgniteHandler(JFrame in_owner, AltosLink in_link) {
177                         owner = in_owner;
178                         link = in_link;
179                 }
180         }
181
182         void ignite_exception(Exception e) {
183                 if (e instanceof FileNotFoundException) {
184                         JOptionPane.showMessageDialog(owner,
185                                                       ((FileNotFoundException) e).getMessage(),
186                                                       "Cannot open target device",
187                                                       JOptionPane.ERROR_MESSAGE);
188                 } else if (e instanceof AltosSerialInUseException) {
189                         JOptionPane.showMessageDialog(owner,
190                                                       String.format("Device \"%s\" already in use",
191                                                                     device.toShortString()),
192                                                       "Device in use",
193                                                       JOptionPane.ERROR_MESSAGE);
194                 } else if (e instanceof IOException) {
195                         IOException ee = (IOException) e;
196                         JOptionPane.showMessageDialog(owner,
197                                                       device.toShortString(),
198                                                       ee.getLocalizedMessage(),
199                                                       JOptionPane.ERROR_MESSAGE);
200                 } else {
201                         JOptionPane.showMessageDialog(owner,
202                                                       String.format("Connection to \"%s\" failed",
203                                                                     device.toShortString()),
204                                                       "Connection Failed",
205                                                       JOptionPane.ERROR_MESSAGE);
206                 }
207                 close();
208         }
209
210         void ignite_reply(String reply) {
211                 if (reply.equals("status")) {
212                         set_ignite_status();
213                 } else if (reply.equals("fired")) {
214                         fired();
215                 } else if (reply.startsWith("npyro")) {
216                         String items[] = reply.split("\\s+");
217                         npyro = Integer.parseInt(items[1]);
218                         if (npyro == AltosLib.MISSING)
219                                 npyro = 0;
220                         has_standard = Integer.parseInt(items[2]) != 0;
221                         make_ui();
222                 }
223         }
224
225         void set_arm_text() {
226                 if (arm.isSelected())
227                         arm.setText(String.format("%d", time_remaining));
228                 else
229                         arm.setText("Arm");
230         }
231
232         void start_timer() {
233                 time_remaining = 10;
234                 set_arm_text();
235                 timer_running = true;
236         }
237
238         void stop_timer() {
239                 time_remaining = 0;
240                 fire.setEnabled(false);
241                 timer_running = false;
242                 arm.setSelected(false);
243                 arm.setEnabled(false);
244                 set_arm_text();
245         }
246
247         void cancel () {
248                 group.clearSelection();
249                 fire.setEnabled(false);
250                 stop_timer();
251         }
252
253         void send_command(String command) {
254                 try {
255                         command_queue.put(command);
256                 } catch (Exception ex) {
257                         ignite_exception(ex);
258                 }
259         }
260
261         boolean getting_status = false;
262
263         void set_ignite_status() {
264                 getting_status = false;
265                 poll_remaining = 2;
266                 if (!isVisible())
267                         setVisible(true);
268         }
269
270         void poll_ignite_status() {
271                 if (poll_remaining > 0) {
272                         --poll_remaining;
273                         return;
274                 }
275                 if (!getting_status) {
276                         getting_status = true;
277                         send_command("get_status");
278                 }
279         }
280
281         boolean firing = false;
282
283         void start_fire(String which) {
284                 if (!firing) {
285                         firing = true;
286                         send_command(which);
287                 }
288         }
289
290         void fired() {
291                 firing = false;
292                 cancel();
293         }
294
295         void close() {
296                 if (opened) {
297                         send_command("quit");
298                 }
299                 if (timer != null)
300                         timer.stop();
301                 setVisible(false);
302                 dispose();
303         }
304
305         void tick_timer() {
306                 if (timer_running) {
307                         --time_remaining;
308                         if (time_remaining <= 0)
309                                 cancel();
310                         else
311                                 set_arm_text();
312                 }
313                 poll_ignite_status();
314         }
315
316         void fire() {
317                 if (arm.isEnabled() && arm.isSelected() && time_remaining > 0) {
318                         String  igniter = "none";
319
320                         for (int p = 0; p < igniters.length; p++)
321                                 if (igniters[p].button.isSelected()) {
322                                         igniter = igniters[p].name;
323                                         break;
324                                 }
325                         send_command(igniter);
326                         cancel();
327                 }
328         }
329
330         public void actionPerformed(ActionEvent e) {
331                 String cmd = e.getActionCommand();
332
333                 for (int p = 0; p < igniters.length; p++)
334                         if (cmd.equals(igniters[p].name)) {
335                                 stop_timer();
336                                 arm.setEnabled(true);
337                                 break;
338                         }
339
340                 if (cmd.equals("arm")) {
341                         if (arm.isSelected()) {
342                                 fire.setEnabled(true);
343                                 start_timer();
344                         } else
345                                 cancel();
346                 }
347                 if (cmd.equals("fire"))
348                         fire();
349                 if (cmd.equals("tick"))
350                         tick_timer();
351                 if (cmd.equals("close"))
352                         close();
353         }
354
355         /* A window listener to catch closing events and tell the config code */
356         class ConfigListener extends WindowAdapter {
357                 AltosIgniteUI   ui;
358
359                 public ConfigListener(AltosIgniteUI 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                 opened = false;
374                 device = AltosDeviceUIDialog.show(owner, Altos.product_any);
375                 if (device != null) {
376                         try {
377                                 AltosSerial     serial = new AltosSerial(device);
378                                 serial.set_frame(owner);
379                                 IgniteHandler   handler = new IgniteHandler(owner, serial);
380                                 Thread          t = new Thread(handler);
381                                 t.start();
382                                 opened = true;
383                                 return true;
384                         } catch (Exception ex) {
385                                 ignite_exception(ex);
386                         }
387                 }
388                 return false;
389         }
390
391         private void make_ui() {
392                 group = new ButtonGroup();
393
394                 Container               pane = getScrollablePane();
395
396                 GridBagConstraints      c = new GridBagConstraints();
397                 Insets                  i = new Insets(4,4,4,4);
398
399                 timer = new javax.swing.Timer(timeout, this);
400                 timer.setActionCommand("tick");
401                 timer_running = false;
402                 timer.restart();
403
404                 pane.setLayout(new GridBagLayout());
405
406                 c.fill = GridBagConstraints.NONE;
407                 c.anchor = GridBagConstraints.CENTER;
408                 c.insets = i;
409                 c.weightx = 0;
410                 c.weighty = 0;
411
412                 int y = 0;
413
414                 c.gridx = 0;
415                 c.gridy = y;
416                 c.gridwidth = 2;
417                 c.anchor = GridBagConstraints.CENTER;
418                 label = new JLabel ("Fire Igniter");
419                 pane.add(label, c);
420
421                 y++;
422
423                 int nstandard = 0;
424                 if (has_standard)
425                         nstandard = 2;
426
427                 igniters = new Igniter[nstandard + npyro];
428
429                 if (has_standard) {
430                         igniters[0] = new Igniter(this, "Apogee", AltosIgnite.Apogee, y++);
431                         igniters[1] = new Igniter(this, "Main", AltosIgnite.Main, y++);
432                 }
433
434                 for (int p = 0; p < npyro; p++) {
435                         String  name = String.format("%d", p);
436                         String  label = String.format("%c", 'A' + p);
437                         igniters[nstandard+p] = new Igniter(this, label, name, y++);
438                 }
439
440                 c.gridx = 0;
441                 c.gridy = y;
442                 c.gridwidth = 1;
443                 c.anchor = GridBagConstraints.CENTER;
444                 arm = new JToggleButton ("Arm");
445                 pane.add(arm, c);
446                 arm.addActionListener(this);
447                 arm.setActionCommand("arm");
448                 arm.setEnabled(false);
449
450                 c.gridx = 1;
451                 c.gridy = y;
452                 c.gridwidth = 1;
453                 c.anchor = GridBagConstraints.CENTER;
454                 fire = new JButton ("Fire");
455                 fire.setEnabled(false);
456                 pane.add(fire, c);
457                 fire.addActionListener(this);
458                 fire.setActionCommand("fire");
459
460                 y++;
461
462                 c.gridx = 0;
463                 c.gridy = y;
464                 c.gridwidth = 2;
465                 c.anchor = GridBagConstraints.CENTER;
466                 close = new JButton ("Close");
467                 pane.add(close, c);
468                 close.addActionListener(this);
469                 close.setActionCommand("close");
470
471                 pack();
472                 setLocationRelativeTo(owner);
473
474                 addWindowListener(new ConfigListener(this));
475         }
476
477         public AltosIgniteUI(JFrame in_owner) {
478
479                 owner = in_owner;
480
481                 if (!open())
482                         return;
483
484                 send_command("get_npyro");
485         }
486 }