telegps: Hook up data download dialog
[fw/altos] / telegps / TeleGPS.java
1 /*
2  * Copyright © 2014 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 org.altusmetrum.telegps;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import javax.swing.*;
23 import java.io.*;
24 import java.util.concurrent.*;
25 import java.util.*;
26 import org.altusmetrum.altoslib_4.*;
27 import org.altusmetrum.altosuilib_2.*;
28
29 public class TeleGPS extends AltosUIFrame implements AltosFlightDisplay, AltosFontListener, ActionListener {
30
31         static String[] telegps_icon_names = {
32                 "/telegps-16.png",
33                 "/telegps-32.png",
34                 "/telegps-48.png",
35                 "/telegps-64.png",
36                 "/telegps-128.png",
37                 "/telegps-256.png"
38         };
39
40         static { set_icon_names(telegps_icon_names); }
41
42         static AltosVoice       voice;
43
44         static AltosVoice voice() {
45                 if (voice == null)
46                         voice = new AltosVoice();
47                 return voice;
48         }
49
50         AltosFlightReader       reader;
51         AltosDisplayThread      thread;
52
53         JTabbedPane     pane;
54
55         AltosSiteMap    sitemap;
56         boolean         has_map;
57
58         JMenuBar        menu_bar;
59         JMenu           file_menu;
60         JMenu           monitor_menu;
61         JMenu           device_menu;
62
63         /* File menu */
64         final static String     new_command = "new";
65         final static String     preferences_command = "preferences";
66         final static String     load_maps_command = "loadmaps";
67         final static String     close_command = "close";
68         final static String     exit_command = "exit";
69
70         static final String[][] file_menu_entries = new String[][] {
71                 { "New Window",         new_command },
72                 { "Preferences",        preferences_command },
73                 { "Load Maps",          load_maps_command },
74                 { "Close",              close_command },
75                 { "Exit",               exit_command },
76         };
77
78         /* Monitor menu */
79         final static String     monitor_command = "monitor";
80         final static String     disconnect_command = "disconnect";
81         final static String     scan_command = "scan";
82
83         static final String[][] monitor_menu_entries = new String[][] {
84                 { "Monitor Device",     monitor_command },
85                 { "Disconnect",         disconnect_command },
86                 { "Scan Channels",      scan_command },
87         };
88
89         /* Device menu */
90         final static String     download_command = "download";
91         final static String     configure_command = "configure";
92         final static String     export_command = "export";
93         final static String     graph_command = "graph";
94
95         static final String[][] device_menu_entries = new String[][] {
96                 { "Download Data",      download_command },
97                 { "Configure Device",   configure_command },
98                 { "Export Data",        export_command },
99                 { "Graph Data",         graph_command },
100         };
101
102 //      private AltosInfoTable flightInfo;
103
104         boolean exit_on_close = false;
105
106         void stop_display() {
107                 if (thread != null && thread.isAlive()) {
108                         thread.interrupt();
109                         try {
110                                 thread.join();
111                         } catch (InterruptedException ie) {}
112                 }
113                 thread = null;
114         }
115
116         public void reset() {
117                 sitemap.reset();
118         }
119
120         public void set_font() {
121                 sitemap.set_font();
122         }
123
124         public void font_size_changed(int font_size) {
125                 set_font();
126         }
127
128
129 //      AltosFlightStatusUpdate status_update;
130
131         public void show(AltosState state, AltosListenerState listener_state) {
132 //              status_update.saved_state = state;
133
134                 if (state == null)
135                         state = new AltosState();
136
137                 sitemap.show(state, listener_state);
138                 telegps_status.show(state, listener_state);
139         }
140
141         Container               bag;
142         AltosFreqList           frequencies;
143         JLabel                  telemetry;
144         TeleGPSStatus           telegps_status;
145         TeleGPSStatusUpdate     status_update;
146
147         ActionListener          show_timer;
148
149         void new_window() {
150                 new TeleGPS();
151         }
152
153         void preferences() {
154                 new TeleGPSPreferences(this, voice());
155         }
156
157         void load_maps() {
158                 new AltosSiteMapPreload(this);
159         }
160
161         void disconnect() {
162                 setTitle("TeleGPS");
163                 stop_display();
164                 remove_frequency_menu();
165         }
166
167         void connect(AltosDevice device) {
168                 if (reader != null)
169                         disconnect();
170                 try {
171                         AltosFlightReader       reader = new AltosTelemetryReader(new AltosSerial(device));
172                         set_reader(reader);
173                         add_frequency_menu(device.getSerial(), reader);
174                 } catch (FileNotFoundException ee) {
175                         JOptionPane.showMessageDialog(this,
176                                                       ee.getMessage(),
177                                                       String.format ("Cannot open %s", device.toShortString()),
178                                                       JOptionPane.ERROR_MESSAGE);
179                 } catch (AltosSerialInUseException si) {
180                         JOptionPane.showMessageDialog(this,
181                                                       String.format("Device \"%s\" already in use",
182                                                                     device.toShortString()),
183                                                       "Device in use",
184                                                       JOptionPane.ERROR_MESSAGE);
185                 } catch (IOException ee) {
186                         JOptionPane.showMessageDialog(this,
187                                                       String.format ("Unknown I/O error on %s", device.toShortString()),
188                                                       "Unknown I/O error",
189                                                       JOptionPane.ERROR_MESSAGE);
190                 } catch (TimeoutException te) {
191                         JOptionPane.showMessageDialog(this,
192                                                       String.format ("Timeout on %s", device.toShortString()),
193                                                       "Timeout error",
194                                                       JOptionPane.ERROR_MESSAGE);
195                 } catch (InterruptedException ie) {
196                         JOptionPane.showMessageDialog(this,
197                                                       String.format("Interrupted %s", device.toShortString()),
198                                                       "Interrupted exception",
199                                                       JOptionPane.ERROR_MESSAGE);
200                 }
201         }
202
203         void monitor() {
204                 AltosDevice     device = AltosDeviceUIDialog.show(this,
205                                                                   AltosLib.product_basestation);
206                 if (device == null)
207                         return;
208                 connect(device);
209         }
210
211         public void scan_device_selected(AltosDevice device) {
212                 connect(device);
213         }
214
215         void scan() {
216                 new AltosScanUI(this, false);
217         }
218
219         void download(){
220                 new AltosEepromManage(this, AltosLib.product_telegps);
221         }
222
223         void configure() {
224                 new TeleGPSConfig(this);
225         }
226
227         void export() {
228         }
229
230         void graph() {
231         }
232
233         public void actionPerformed(ActionEvent ev) {
234
235                 /* File menu */
236                 if (new_command.equals(ev.getActionCommand())) {
237                         new_window();
238                         return;
239                 }
240                 if (preferences_command.equals(ev.getActionCommand())) {
241                         preferences();
242                         return;
243                 }
244                 if (load_maps_command.equals(ev.getActionCommand())) {
245                         load_maps();
246                         return;
247                 }
248                 if (close_command.equals(ev.getActionCommand())) {
249                         close();
250                         return;
251                 }
252                 if (exit_command.equals(ev.getActionCommand()))
253                         System.exit(0);
254
255                 /* Monitor menu */
256                 if (monitor_command.equals(ev.getActionCommand())) {
257                         monitor();
258                         return;
259                 }
260                 if (disconnect_command.equals(ev.getActionCommand())) {
261                         disconnect();
262                         return;
263                 }
264                 if (scan_command.equals(ev.getActionCommand())) {
265                         scan();
266                         return;
267                 }
268
269                 /* Device menu */
270                 if (download_command.equals(ev.getActionCommand())) {
271                         download();
272                         return;
273                 }
274                 if (configure_command.equals(ev.getActionCommand())) {
275                         configure();
276                         return;
277                 }
278                 if (export_command.equals(ev.getActionCommand())) {
279                         export();
280                         return;
281                 }
282                 if (graph_command.equals(ev.getActionCommand())) {
283                         graph();
284                         return;
285                 }
286         }
287
288         void add_frequency_menu(int serial, final AltosFlightReader reader) {
289                 // Channel menu
290                 frequencies = new AltosFreqList(AltosUIPreferences.frequency(serial));
291                 frequencies.set_product("Monitor");
292                 frequencies.set_serial(serial);
293                 frequencies.addActionListener(new ActionListener() {
294                                 public void actionPerformed(ActionEvent e) {
295                                         double frequency = frequencies.frequency();
296                                         try {
297                                                 reader.set_frequency(frequency);
298                                         } catch (TimeoutException te) {
299                                         } catch (InterruptedException ie) {
300                                         }
301                                         reader.save_frequency();
302                                 }
303                         });
304                 menu_bar.add(frequencies);
305         }
306
307         void remove_frequency_menu() {
308                 if (frequencies != null) {
309                         menu_bar.remove(frequencies);
310                         frequencies = null;
311                 }
312         }
313
314         public void set_reader(AltosFlightReader reader) {
315                 setTitle(String.format("TeleGPS %s", reader.name));
316                 thread = new AltosDisplayThread(this, voice(), this, reader);
317                 thread.start();
318         }
319
320         static int      number_of_windows;
321
322         private void close() {
323                 AltosUIPreferences.unregister_font_listener(this);
324                 setVisible(false);
325                 dispose();
326                 --number_of_windows;
327                 if (number_of_windows == 0)
328                         System.exit(0);
329         }
330
331         private void add_menu(JMenu menu, String label, String action) {
332                 JMenuItem       item = new JMenuItem(label);
333                 menu.add(item);
334                 item.addActionListener(this);
335                 item.setActionCommand(action);
336         }
337
338
339         private JMenu make_menu(String label, String[][] items) {
340                 JMenu   menu = new JMenu(label);
341                 for (int i = 0; i < items.length; i++)
342                         add_menu(menu, items[i][0], items[i][1]);
343                 menu_bar.add(menu);
344                 return menu;
345         }
346
347         public TeleGPS() {
348
349                 AltosUIPreferences.set_component(this);
350
351                 reader = null;
352
353                 bag = getContentPane();
354                 bag.setLayout(new GridBagLayout());
355
356                 GridBagConstraints c = new GridBagConstraints();
357
358                 setTitle("TeleGPS");
359
360                 menu_bar = new JMenuBar();
361                 setJMenuBar(menu_bar);
362
363                 file_menu = make_menu("File", file_menu_entries);
364                 monitor_menu = make_menu("Monitor", monitor_menu_entries);
365                 device_menu = make_menu("Device", device_menu_entries);
366
367                 int serial = -1;
368
369                 /* TeleGPS status is always visible */
370                 telegps_status = new TeleGPSStatus();
371                 c.gridx = 0;
372                 c.gridy = 1;
373                 c.fill = GridBagConstraints.HORIZONTAL;
374                 c.weightx = 1;
375                 c.gridwidth = 2;
376                 bag.add(telegps_status, c);
377                 c.gridwidth = 1;
378
379
380                 /* The rest of the window uses a tabbed pane to
381                  * show one of the alternate data views
382                  */
383                 pane = new JTabbedPane();
384
385                 /* Make the tabbed pane use the rest of the window space */
386                 c.gridx = 0;
387                 c.gridy = 2;
388                 c.fill = GridBagConstraints.BOTH;
389                 c.weightx = 1;
390                 c.weighty = 1;
391                 c.gridwidth = 2;
392                 bag.add(pane, c);
393
394                 sitemap = new AltosSiteMap();
395                 pane.add("Site Map", sitemap);
396
397                 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
398
399                 AltosUIPreferences.register_font_listener(this);
400
401                 addWindowListener(new WindowAdapter() {
402                                 @Override
403                                 public void windowClosing(WindowEvent e) {
404                                         close();
405                                 }
406                         });
407
408                 pack();
409                 setVisible(true);
410
411                 ++number_of_windows;
412
413                 status_update = new TeleGPSStatusUpdate(telegps_status);
414
415                 new javax.swing.Timer(100, status_update).start();
416         }
417
418         public TeleGPS(AltosFlightReader reader) {
419                 this();
420                 set_reader(reader);
421         }
422
423         static AltosStateIterable record_iterable(File file) {
424                 FileInputStream in;
425                 try {
426                         in = new FileInputStream(file);
427                 } catch (Exception e) {
428                         System.out.printf("Failed to open file '%s'\n", file);
429                         return null;
430                 }
431                 if (file.getName().endsWith("telem"))
432                         return new AltosTelemetryFile(in);
433                 else
434                         return new AltosEepromFile(in);
435         }
436
437         static AltosReplayReader replay_file(File file) {
438                 AltosStateIterable states = record_iterable(file);
439                 if (states == null)
440                         return null;
441                 return new AltosReplayReader(states.iterator(), file);
442         }
443
444         static boolean process_replay(File file) {
445                 AltosReplayReader new_reader = replay_file(file);
446                 if (new_reader == null)
447                         return false;
448
449                 new TeleGPS(new_reader);
450                 return true;
451         }
452
453         static final int process_none = 0;
454         static final int process_csv = 1;
455         static final int process_kml = 2;
456         static final int process_graph = 3;
457         static final int process_replay = 4;
458         static final int process_summary = 5;
459         static final int process_cat = 6;
460
461         public static boolean load_library(Frame frame) {
462                 if (!AltosUILib.load_library()) {
463                         JOptionPane.showMessageDialog(frame,
464                                                       String.format("No AltOS library in \"%s\"",
465                                                                     System.getProperty("java.library.path","<undefined>")),
466                                                       "Cannot load device access library",
467                                                       JOptionPane.ERROR_MESSAGE);
468                         return false;
469                 }
470                 return true;
471         }
472
473         public static void help(int code) {
474                 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
475                 System.out.printf("  Options:\n");
476                 System.out.printf("    --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n");
477                 System.out.printf("    --replay <filename>\t\trelive the glory of past flights \n");
478                 System.out.printf("    --graph <filename>\t\tgraph a flight\n");
479                 System.out.printf("    --csv\tgenerate comma separated output for spreadsheets, etc\n");
480                 System.out.printf("    --kml\tgenerate KML output for use with Google Earth\n");
481                 System.exit(code);
482         }
483
484         public static void main(String[] args) {
485                 int     errors = 0;
486
487                 load_library(null);
488                 try {
489                         UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
490                 } catch (Exception e) {
491                 }
492
493                 boolean any_created = false;
494
495
496                 /* Handle batch-mode */
497                 int process = process_none;
498                 for (int i = 0; i < args.length; i++) {
499                         if (args[i].equals("--help"))
500                                 help(0);
501                         else if (args[i].equals("--fetchmaps")) {
502                                 if (args.length < i + 3) {
503                                         help(1);
504                                 } else {
505                                         double lat = Double.parseDouble(args[i+1]);
506                                         double lon = Double.parseDouble(args[i+2]);
507                                         AltosSiteMap.prefetchMaps(lat, lon);
508                                         i += 2;
509                                 }
510                         } else if (args[i].equals("--replay"))
511                                 process = process_replay;
512                         else if (args[i].equals("--kml"))
513                                 process = process_kml;
514                         else if (args[i].equals("--csv"))
515                                 process = process_csv;
516                         else if (args[i].equals("--graph"))
517                                 process = process_graph;
518                         else if (args[i].equals("--summary"))
519                                 process = process_summary;
520                         else if (args[i].equals("--cat"))
521                                 process = process_cat;
522                         else if (args[i].startsWith("--"))
523                                 help(1);
524                         else {
525                                 File file = new File(args[i]);
526                                 switch (process) {
527                                 case process_graph:
528                                         ++errors;
529                                         break;
530                                 case process_none:
531                                 case process_replay:
532                                         if (!process_replay(file))
533                                                 ++errors;
534                                         any_created = true;
535                                         break;
536                                 case process_kml:
537                                         ++errors;
538                                         break;
539                                 case process_csv:
540                                         ++errors;
541                                         break;
542                                 case process_summary:
543                                         ++errors;
544                                         break;
545                                 case process_cat:
546                                         ++errors;
547                                 }
548                         }
549                 }
550                 if (errors != 0)
551                         System.exit(errors);
552                 if (!any_created)
553                         new TeleGPS();
554         }
555 }