altosui: Split out flight monitoring to separate window
[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         public AltosUI() {
87
88                 load_library(null);
89
90                 java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg");
91                 if (imgURL != null)
92                         setIconImage(new ImageIcon(imgURL).getImage());
93
94                 AltosPreferences.init(this);
95
96                 setTitle("AltOS");
97
98                 createMenu();
99
100                 int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
101                 this.setSize(new Dimension (300, 100));
102                 this.validate();
103                 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
104                 addWindowListener(new WindowAdapter() {
105                         @Override
106                         public void windowClosing(WindowEvent e) {
107                                 System.exit(0);
108                         }
109                 });
110         }
111
112         private void ConnectToDevice() {
113                 AltosDevice     device = AltosDeviceDialog.show(AltosUI.this,
114                                                                 AltosDevice.product_basestation);
115
116                 if (device != null)
117                         telemetry_window(device);
118         }
119
120         void ConfigureCallsign() {
121                 String  result;
122                 result = JOptionPane.showInputDialog(AltosUI.this,
123                                                      "Configure Callsign",
124                                                      AltosPreferences.callsign());
125                 if (result != null)
126                         AltosPreferences.set_callsign(result);
127         }
128
129         void ConfigureTeleMetrum() {
130                 new AltosConfig(AltosUI.this);
131         }
132
133         void FlashImage() {
134                 new AltosFlashUI(AltosUI.this);
135         }
136
137         /*
138          * Replay a flight from telemetry data
139          */
140         private void Replay() {
141                 AltosLogfileChooser chooser = new AltosLogfileChooser(
142                         AltosUI.this);
143                 AltosRecordIterable iterable = chooser.runDialog();
144                 if (iterable != null) {
145                         AltosFlightReader reader = new AltosReplayReader(iterable.iterator(),
146                                                                          chooser.filename());
147                         new AltosFlightUI(voice, reader);
148                 }
149         }
150
151         /* Connect to TeleMetrum, either directly or through
152          * a TeleDongle over the packet link
153          */
154         private void SaveFlightData() {
155                 new AltosEepromDownload(AltosUI.this);
156         }
157
158         /* Load a flight log file and write out a CSV file containing
159          * all of the data in standard units
160          */
161
162         private void ExportData() {
163                 new AltosCSVUI(AltosUI.this);
164         }
165
166         /* Load a flight log CSV file and display a pretty graph.
167          */
168
169         private void GraphData() {
170                 new AltosGraphUI(AltosUI.this);
171         }
172
173         /* Create the AltosUI menus
174          */
175         private void createMenu() {
176                 JMenuBar menubar = new JMenuBar();
177                 JMenu menu;
178                 JMenuItem item;
179                 JRadioButtonMenuItem radioitem;
180
181                 // File menu
182                 {
183                         menu = new JMenu("File");
184                         menu.setMnemonic(KeyEvent.VK_F);
185                         menubar.add(menu);
186
187                         item = new JMenuItem("Replay File",KeyEvent.VK_R);
188                         item.addActionListener(new ActionListener() {
189                                         public void actionPerformed(ActionEvent e) {
190                                                 Replay();
191                                         }
192                                 });
193                         menu.add(item);
194
195                         item = new JMenuItem("Save Flight Data",KeyEvent.VK_S);
196                         item.addActionListener(new ActionListener() {
197                                         public void actionPerformed(ActionEvent e) {
198                                                 SaveFlightData();
199                                         }
200                                 });
201                         menu.add(item);
202
203                         item = new JMenuItem("Flash Image",KeyEvent.VK_I);
204                         item.addActionListener(new ActionListener() {
205                                         public void actionPerformed(ActionEvent e) {
206                                                 FlashImage();
207                                         }
208                                 });
209                         menu.add(item);
210
211                         item = new JMenuItem("Export Data",KeyEvent.VK_E);
212                         item.addActionListener(new ActionListener() {
213                                         public void actionPerformed(ActionEvent e) {
214                                                 ExportData();
215                                         }
216                                 });
217                         menu.add(item);
218
219                         item = new JMenuItem("Graph Data",KeyEvent.VK_G);
220                         item.addActionListener(new ActionListener() {
221                                         public void actionPerformed(ActionEvent e) {
222                                                 GraphData();
223                                         }
224                                 });
225                         menu.add(item);
226
227                         item = new JMenuItem("Quit",KeyEvent.VK_Q);
228                         item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
229                                                                    ActionEvent.CTRL_MASK));
230                         item.addActionListener(new ActionListener() {
231                                         public void actionPerformed(ActionEvent e) {
232                                                 System.out.printf("exiting\n");
233                                                 System.exit(0);
234                                         }
235                                 });
236                         menu.add(item);
237                 }
238
239                 // Device menu
240                 if (false) {
241                         menu = new JMenu("Device");
242                         menu.setMnemonic(KeyEvent.VK_D);
243                         menubar.add(menu);
244
245                         item = new JMenuItem("Connect to Device",KeyEvent.VK_C);
246                         item.addActionListener(new ActionListener() {
247                                         public void actionPerformed(ActionEvent e) {
248                                                 ConnectToDevice();
249                                         }
250                                 });
251                         menu.add(item);
252
253                         menu.addSeparator();
254
255                         item = new JMenuItem("Set Callsign",KeyEvent.VK_S);
256                         item.addActionListener(new ActionListener() {
257                                         public void actionPerformed(ActionEvent e) {
258                                                 ConfigureCallsign();
259                                         }
260                                 });
261
262                         menu.add(item);
263
264                         item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T);
265                         item.addActionListener(new ActionListener() {
266                                         public void actionPerformed(ActionEvent e) {
267                                                 ConfigureTeleMetrum();
268                                         }
269                                 });
270
271                         menu.add(item);
272                 }
273                 // Log menu
274                 {
275                         menu = new JMenu("Log");
276                         menu.setMnemonic(KeyEvent.VK_L);
277                         menubar.add(menu);
278
279                         item = new JMenuItem("New Log",KeyEvent.VK_N);
280                         item.addActionListener(new ActionListener() {
281                                         public void actionPerformed(ActionEvent e) {
282                                         }
283                                 });
284                         menu.add(item);
285
286                         item = new JMenuItem("Configure Log",KeyEvent.VK_C);
287                         item.addActionListener(new ActionListener() {
288                                         public void actionPerformed(ActionEvent e) {
289                                                 AltosPreferences.ConfigureLog();
290                                         }
291                                 });
292                         menu.add(item);
293                 }
294                 // Voice menu
295                 {
296                         menu = new JMenu("Voice", true);
297                         menu.setMnemonic(KeyEvent.VK_V);
298                         menubar.add(menu);
299
300                         radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice());
301                         radioitem.addActionListener(new ActionListener() {
302                                         public void actionPerformed(ActionEvent e) {
303                                                 JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource();
304                                                 boolean enabled = item.isSelected();
305                                                 AltosPreferences.set_voice(enabled);
306                                                 if (enabled)
307                                                         voice.speak_always("Enable voice.");
308                                                 else
309                                                         voice.speak_always("Disable voice.");
310                                         }
311                                 });
312                         menu.add(radioitem);
313                         item = new JMenuItem("Test Voice",KeyEvent.VK_T);
314                         item.addActionListener(new ActionListener() {
315                                         public void actionPerformed(ActionEvent e) {
316                                                 voice.speak("That's one small step for man; one giant leap for mankind.");
317                                         }
318                                 });
319                         menu.add(item);
320                 }
321                 this.setJMenuBar(menubar);
322         }
323
324         static AltosRecordIterable open_logfile(String filename) {
325                 File file = new File (filename);
326                 try {
327                         FileInputStream in;
328
329                         in = new FileInputStream(file);
330                         if (filename.endsWith("eeprom"))
331                                 return new AltosEepromIterable(in);
332                         else
333                                 return new AltosTelemetryIterable(in);
334                 } catch (FileNotFoundException fe) {
335                         System.out.printf("Cannot open '%s'\n", filename);
336                         return null;
337                 }
338         }
339
340         static AltosWriter open_csv(String filename) {
341                 File file = new File (filename);
342                 try {
343                         return new AltosCSV(file);
344                 } catch (FileNotFoundException fe) {
345                         System.out.printf("Cannot open '%s'\n", filename);
346                         return null;
347                 }
348         }
349
350         static AltosWriter open_kml(String filename) {
351                 File file = new File (filename);
352                 try {
353                         return new AltosKML(file);
354                 } catch (FileNotFoundException fe) {
355                         System.out.printf("Cannot open '%s'\n", filename);
356                         return null;
357                 }
358         }
359
360         static final int process_csv = 1;
361         static final int process_kml = 2;
362
363         static void process_file(String input, int process) {
364                 AltosRecordIterable iterable = open_logfile(input);
365                 if (iterable == null)
366                         return;
367                 if (process == 0)
368                         process = process_csv;
369                 if ((process & process_csv) != 0) {
370                         String output = Altos.replace_extension(input,".csv");
371                         System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
372                         if (input.equals(output)) {
373                                 System.out.printf("Not processing '%s'\n", input);
374                         } else {
375                                 AltosWriter writer = open_csv(output);
376                                 if (writer != null) {
377                                         writer.write(iterable);
378                                         writer.close();
379                                 }
380                         }
381                 }
382                 if ((process & process_kml) != 0) {
383                         String output = Altos.replace_extension(input,".kml");
384                         System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
385                         if (input.equals(output)) {
386                                 System.out.printf("Not processing '%s'\n", input);
387                         } else {
388                                 AltosWriter writer = open_kml(output);
389                                 if (writer == null)
390                                         return;
391                                 writer.write(iterable);
392                                 writer.close();
393                         }
394                 }
395         }
396
397         public static void main(final String[] args) {
398                 int     process = 0;
399                 /* Handle batch-mode */
400                 if (args.length > 0) {
401                         for (int i = 0; i < args.length; i++) {
402                                 if (args[i].equals("--kml"))
403                                         process |= process_kml;
404                                 else if (args[i].equals("--csv"))
405                                         process |= process_csv;
406                                 else
407                                         process_file(args[i], process);
408                         }
409                 } else {
410                         AltosUI altosui = new AltosUI();
411                         altosui.setVisible(true);
412
413                         AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation);
414                         for (int i = 0; i < devices.length; i++)
415                                 altosui.telemetry_window(devices[i]);
416                 }
417         }
418 }