add site map tab, at least for QRS launches
[fw/altos] / ao-tools / altosui / AltosUI.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 java.io.*;
26 import java.util.*;
27 import java.text.*;
28 import java.util.prefs.*;
29 import java.util.concurrent.LinkedBlockingQueue;
30
31 import altosui.Altos;
32 import altosui.AltosSerial;
33 import altosui.AltosSerialMonitor;
34 import altosui.AltosRecord;
35 import altosui.AltosTelemetry;
36 import altosui.AltosState;
37 import altosui.AltosDeviceDialog;
38 import altosui.AltosPreferences;
39 import altosui.AltosLog;
40 import altosui.AltosVoice;
41 import altosui.AltosFlightInfoTableModel;
42 import altosui.AltosFlashUI;
43 import altosui.AltosLogfileChooser;
44 import altosui.AltosCSVUI;
45 import altosui.AltosLine;
46 import altosui.AltosStatusTable;
47 import altosui.AltosInfoTable;
48 import altosui.AltosDisplayThread;
49
50 import libaltosJNI.*;
51
52 public class AltosUI extends JFrame {
53         public AltosVoice voice = new AltosVoice();
54
55         public static boolean load_library(Frame frame) {
56                 if (!AltosDevice.load_library()) {
57                         JOptionPane.showMessageDialog(frame,
58                                                       String.format("No AltOS library in \"%s\"",
59                                                                     System.getProperty("java.library.path","<undefined>")),
60                                                       "Cannot load device access library",
61                                                       JOptionPane.ERROR_MESSAGE);
62                         return false;
63                 }
64                 return true;
65         }
66
67         void telemetry_window(AltosDevice device) {
68                 try {
69                         AltosFlightReader reader = new AltosTelemetryReader(device);
70                         if (reader != null)
71                                 new AltosFlightUI(voice, reader, device.getSerial());
72                 } catch (FileNotFoundException ee) {
73                         JOptionPane.showMessageDialog(AltosUI.this,
74                                                       String.format("Cannot open device \"%s\"",
75                                                                     device.getPath()),
76                                                       "Cannot open target device",
77                                                       JOptionPane.ERROR_MESSAGE);
78                 } catch (IOException ee) {
79                         JOptionPane.showMessageDialog(AltosUI.this,
80                                                       device.getPath(),
81                                                       "Unkonwn I/O error",
82                                                       JOptionPane.ERROR_MESSAGE);
83                 }
84         }
85
86         Container       pane;
87         GridBagLayout   gridbag;
88
89         JButton addButton(int x, int y, String label) {
90                 GridBagConstraints      c;
91                 JButton                 b;
92
93                 c = new GridBagConstraints();
94                 c.gridx = x; c.gridy = y;
95                 c.fill = GridBagConstraints.BOTH;
96                 c.weightx = 1;
97                 c.weighty = 1;
98                 b = new JButton(label);
99
100                 Dimension ps = b.getPreferredSize();
101
102                 gridbag.setConstraints(b, c);
103                 add(b, c);
104                 return b;
105         }
106
107         public AltosUI() {
108
109                 load_library(null);
110
111                 java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg");
112                 if (imgURL != null)
113                         setIconImage(new ImageIcon(imgURL).getImage());
114
115                 AltosPreferences.init(this);
116
117                 pane = getContentPane();
118                 gridbag = new GridBagLayout();
119                 pane.setLayout(gridbag);
120
121                 JButton b;
122
123                 b = addButton(0, 0, "Monitor Flight");
124                 b.addActionListener(new ActionListener() {
125                                         public void actionPerformed(ActionEvent e) {
126                                                 ConnectToDevice();
127                                         }
128                                 });
129                 b = addButton(1, 0, "Save Flight Data");
130                 b.addActionListener(new ActionListener() {
131                                         public void actionPerformed(ActionEvent e) {
132                                                 SaveFlightData();
133                                         }
134                                 });
135                 b = addButton(2, 0, "Replay Flight");
136                 b.addActionListener(new ActionListener() {
137                                         public void actionPerformed(ActionEvent e) {
138                                                 Replay();
139                                         }
140                                 });
141                 b = addButton(0, 1, "Graph Data");
142                 b.addActionListener(new ActionListener() {
143                                         public void actionPerformed(ActionEvent e) {
144                                                 GraphData();
145                                         }
146                                 });
147                 b = addButton(1, 1, "Export Data");
148                 b.addActionListener(new ActionListener() {
149                                         public void actionPerformed(ActionEvent e) {
150                                                 ExportData();
151                                         }
152                                 });
153                 b = addButton(2, 1, "Configure TeleMetrum");
154                 b.addActionListener(new ActionListener() {
155                                         public void actionPerformed(ActionEvent e) {
156                                                 ConfigureTeleMetrum();
157                                         }
158                                 });
159
160                 setTitle("AltOS");
161
162                 createMenu();
163
164                 pane.doLayout();
165                 pane.validate();
166
167                 doLayout();
168                 validate();
169
170                 setVisible(true);
171
172                 Insets i = getInsets();
173                 Dimension ps = rootPane.getPreferredSize();
174                 ps.width += i.left + i.right;
175                 ps.height += i.top + i.bottom;
176                 setPreferredSize(ps);
177                 setSize(ps);
178                 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
179                 addWindowListener(new WindowAdapter() {
180                         @Override
181                         public void windowClosing(WindowEvent e) {
182                                 System.exit(0);
183                         }
184                 });
185         }
186
187         private void ConnectToDevice() {
188                 AltosDevice     device = AltosDeviceDialog.show(AltosUI.this,
189                                                                 AltosDevice.product_basestation);
190
191                 if (device != null)
192                         telemetry_window(device);
193         }
194
195         void ConfigureCallsign() {
196                 String  result;
197                 result = JOptionPane.showInputDialog(AltosUI.this,
198                                                      "Configure Callsign",
199                                                      AltosPreferences.callsign());
200                 if (result != null)
201                         AltosPreferences.set_callsign(result);
202         }
203
204         void ConfigureTeleMetrum() {
205                 new AltosConfig(AltosUI.this);
206         }
207
208         void FlashImage() {
209                 new AltosFlashUI(AltosUI.this);
210         }
211
212         /*
213          * Replay a flight from telemetry data
214          */
215         private void Replay() {
216                 AltosLogfileChooser chooser = new AltosLogfileChooser(
217                         AltosUI.this);
218                 AltosRecordIterable iterable = chooser.runDialog();
219                 if (iterable != null) {
220                         AltosFlightReader reader = new AltosReplayReader(iterable.iterator(),
221                                                                          chooser.filename());
222                         new AltosFlightUI(voice, reader);
223                 }
224         }
225
226         /* Connect to TeleMetrum, either directly or through
227          * a TeleDongle over the packet link
228          */
229         private void SaveFlightData() {
230                 new AltosEepromDownload(AltosUI.this);
231         }
232
233         /* Load a flight log file and write out a CSV file containing
234          * all of the data in standard units
235          */
236
237         private void ExportData() {
238                 new AltosCSVUI(AltosUI.this);
239         }
240
241         /* Load a flight log CSV file and display a pretty graph.
242          */
243
244         private void GraphData() {
245                 new AltosGraphUI(AltosUI.this);
246         }
247
248         /* Create the AltosUI menus
249          */
250         private void createMenu() {
251                 JMenuBar menubar = new JMenuBar();
252                 JMenu menu;
253                 JMenuItem item;
254                 JRadioButtonMenuItem radioitem;
255
256                 // File menu
257                 {
258                         menu = new JMenu("File");
259                         menu.setMnemonic(KeyEvent.VK_F);
260                         menubar.add(menu);
261
262                         item = new JMenuItem("Flash Image",KeyEvent.VK_I);
263                         item.addActionListener(new ActionListener() {
264                                         public void actionPerformed(ActionEvent e) {
265                                                 FlashImage();
266                                         }
267                                 });
268                         menu.add(item);
269
270                         item = new JMenuItem("Export Data",KeyEvent.VK_E);
271                         item.addActionListener(new ActionListener() {
272                                         public void actionPerformed(ActionEvent e) {
273                                                 ExportData();
274                                         }
275                                 });
276                         menu.add(item);
277
278                         item = new JMenuItem("Graph Data",KeyEvent.VK_G);
279                         item.addActionListener(new ActionListener() {
280                                         public void actionPerformed(ActionEvent e) {
281                                                 GraphData();
282                                         }
283                                 });
284                         menu.add(item);
285
286                         item = new JMenuItem("Quit",KeyEvent.VK_Q);
287                         item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
288                                                                    ActionEvent.CTRL_MASK));
289                         item.addActionListener(new ActionListener() {
290                                         public void actionPerformed(ActionEvent e) {
291                                                 System.out.printf("exiting\n");
292                                                 System.exit(0);
293                                         }
294                                 });
295                         menu.add(item);
296                 }
297
298                 // Device menu
299                 if (false) {
300                         menu = new JMenu("Device");
301                         menu.setMnemonic(KeyEvent.VK_D);
302                         menubar.add(menu);
303
304                         item = new JMenuItem("Connect to Device",KeyEvent.VK_C);
305                         item.addActionListener(new ActionListener() {
306                                         public void actionPerformed(ActionEvent e) {
307                                                 ConnectToDevice();
308                                         }
309                                 });
310                         menu.add(item);
311
312                         menu.addSeparator();
313
314                         item = new JMenuItem("Set Callsign",KeyEvent.VK_S);
315                         item.addActionListener(new ActionListener() {
316                                         public void actionPerformed(ActionEvent e) {
317                                                 ConfigureCallsign();
318                                         }
319                                 });
320
321                         menu.add(item);
322
323                         item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T);
324                         item.addActionListener(new ActionListener() {
325                                         public void actionPerformed(ActionEvent e) {
326                                                 ConfigureTeleMetrum();
327                                         }
328                                 });
329
330                         menu.add(item);
331                 }
332                 // Log menu
333                 {
334                         menu = new JMenu("Log");
335                         menu.setMnemonic(KeyEvent.VK_L);
336                         menubar.add(menu);
337
338                         item = new JMenuItem("New Log",KeyEvent.VK_N);
339                         item.addActionListener(new ActionListener() {
340                                         public void actionPerformed(ActionEvent e) {
341                                         }
342                                 });
343                         menu.add(item);
344
345                         item = new JMenuItem("Configure Log",KeyEvent.VK_C);
346                         item.addActionListener(new ActionListener() {
347                                         public void actionPerformed(ActionEvent e) {
348                                                 AltosPreferences.ConfigureLog();
349                                         }
350                                 });
351                         menu.add(item);
352                 }
353                 // Voice menu
354                 {
355                         menu = new JMenu("Voice", true);
356                         menu.setMnemonic(KeyEvent.VK_V);
357                         menubar.add(menu);
358
359                         radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice());
360                         radioitem.addActionListener(new ActionListener() {
361                                         public void actionPerformed(ActionEvent e) {
362                                                 JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource();
363                                                 boolean enabled = item.isSelected();
364                                                 AltosPreferences.set_voice(enabled);
365                                                 if (enabled)
366                                                         voice.speak_always("Enable voice.");
367                                                 else
368                                                         voice.speak_always("Disable voice.");
369                                         }
370                                 });
371                         menu.add(radioitem);
372                         item = new JMenuItem("Test Voice",KeyEvent.VK_T);
373                         item.addActionListener(new ActionListener() {
374                                         public void actionPerformed(ActionEvent e) {
375                                                 voice.speak("That's one small step for man; one giant leap for mankind.");
376                                         }
377                                 });
378                         menu.add(item);
379                 }
380                 this.setJMenuBar(menubar);
381         }
382
383         static AltosRecordIterable open_logfile(String filename) {
384                 File file = new File (filename);
385                 try {
386                         FileInputStream in;
387
388                         in = new FileInputStream(file);
389                         if (filename.endsWith("eeprom"))
390                                 return new AltosEepromIterable(in);
391                         else
392                                 return new AltosTelemetryIterable(in);
393                 } catch (FileNotFoundException fe) {
394                         System.out.printf("Cannot open '%s'\n", filename);
395                         return null;
396                 }
397         }
398
399         static AltosWriter open_csv(String filename) {
400                 File file = new File (filename);
401                 try {
402                         return new AltosCSV(file);
403                 } catch (FileNotFoundException fe) {
404                         System.out.printf("Cannot open '%s'\n", filename);
405                         return null;
406                 }
407         }
408
409         static AltosWriter open_kml(String filename) {
410                 File file = new File (filename);
411                 try {
412                         return new AltosKML(file);
413                 } catch (FileNotFoundException fe) {
414                         System.out.printf("Cannot open '%s'\n", filename);
415                         return null;
416                 }
417         }
418
419         static final int process_csv = 1;
420         static final int process_kml = 2;
421
422         static void process_file(String input, int process) {
423                 AltosRecordIterable iterable = open_logfile(input);
424                 if (iterable == null)
425                         return;
426                 if (process == 0)
427                         process = process_csv;
428                 if ((process & process_csv) != 0) {
429                         String output = Altos.replace_extension(input,".csv");
430                         System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
431                         if (input.equals(output)) {
432                                 System.out.printf("Not processing '%s'\n", input);
433                         } else {
434                                 AltosWriter writer = open_csv(output);
435                                 if (writer != null) {
436                                         writer.write(iterable);
437                                         writer.close();
438                                 }
439                         }
440                 }
441                 if ((process & process_kml) != 0) {
442                         String output = Altos.replace_extension(input,".kml");
443                         System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
444                         if (input.equals(output)) {
445                                 System.out.printf("Not processing '%s'\n", input);
446                         } else {
447                                 AltosWriter writer = open_kml(output);
448                                 if (writer == null)
449                                         return;
450                                 writer.write(iterable);
451                                 writer.close();
452                         }
453                 }
454         }
455
456         public static void main(final String[] args) {
457                 int     process = 0;
458                 /* Handle batch-mode */
459         if (args.length == 2 && args[0].equals("--replay")) {
460             String filename = args[1];
461             FileInputStream in;
462             try {
463                 in = new FileInputStream(filename);
464             } catch (Exception e) {
465                 System.out.printf("Failed to open file '%s'\n", filename);
466                 return;
467             }
468             AltosRecordIterable recs;
469             AltosReplayReader reader;
470             if (filename.endsWith("eeprom")) {
471               recs = new AltosEepromIterable(in);
472             } else {
473               recs = new AltosTelemetryIterable(in); 
474             }
475             reader = new AltosReplayReader(recs.iterator(), filename);
476             new AltosFlightUI(new AltosVoice(), reader);
477             return;
478         } else if (args.length > 0) {
479                         for (int i = 0; i < args.length; i++) {
480                                 if (args[i].equals("--kml"))
481                                         process |= process_kml;
482                                 else if (args[i].equals("--csv"))
483                                         process |= process_csv;
484                                 else
485                                         process_file(args[i], process);
486                         }
487                 } else {
488                         AltosUI altosui = new AltosUI();
489                         altosui.setVisible(true);
490
491                         AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation);
492                         for (int i = 0; i < devices.length; i++)
493                                 altosui.telemetry_window(devices[i]);
494                 }
495         }
496 }