2 * Copyright © 2014 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; version 2 of the License.
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.
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.
18 package org.altusmetrum.telegps;
21 import java.awt.event.*;
24 import java.util.concurrent.*;
26 import org.altusmetrum.altoslib_5.*;
27 import org.altusmetrum.altosuilib_3.*;
31 implements AltosFlightDisplay, AltosFontListener, AltosUnitsListener, ActionListener
34 static String[] telegps_icon_names = {
43 static { set_icon_names(telegps_icon_names); }
45 static AltosVoice voice;
47 static AltosVoice voice() {
49 voice = new AltosVoice();
53 AltosFlightReader reader;
54 TeleGPSDisplayThread thread;
61 AltosUIFreqList frequencies;
62 ActionListener frequency_listener;
66 TeleGPSStatus telegps_status;
67 TeleGPSStatusUpdate status_update;
68 javax.swing.Timer status_timer;
74 TeleGPSState gps_state;
75 AltosInfoTable info_table;
77 LinkedList<AltosFlightDisplay> displays;
80 final static String new_command = "new";
81 final static String graph_command = "graph";
82 final static String export_command = "export";
83 final static String load_maps_command = "loadmaps";
84 final static String preferences_command = "preferences";
85 final static String close_command = "close";
86 final static String exit_command = "exit";
88 static final String[][] file_menu_entries = new String[][] {
89 { "New Window", new_command },
90 { "Graph Data", graph_command },
91 { "Export Data", export_command },
92 { "Load Maps", load_maps_command },
93 { "Preferences", preferences_command },
94 { "Close", close_command },
95 { "Exit", exit_command },
99 final static String connect_command = "connect";
100 final static String disconnect_command = "disconnect";
101 final static String scan_command = "scan";
103 static final String[][] monitor_menu_entries = new String[][] {
104 { "Connect Device", connect_command },
105 { "Disconnect", disconnect_command },
106 { "Scan Channels", scan_command },
110 final static String download_command = "download";
111 final static String configure_command = "configure";
112 final static String flash_command = "flash";
114 static final String[][] device_menu_entries = new String[][] {
115 { "Download Data", download_command },
116 { "Configure Device", configure_command },
117 { "Flash Device", flash_command },
120 void stop_display() {
121 if (thread != null && thread.isAlive()) {
125 } catch (InterruptedException ie) {}
130 public void reset() {
131 for (AltosFlightDisplay display : displays)
135 public void font_size_changed(int font_size) {
136 for (AltosFlightDisplay display : displays)
137 display.font_size_changed(font_size);
140 public void units_changed(boolean imperial_units) {
141 for (AltosFlightDisplay display : displays)
142 display.units_changed(imperial_units);
145 public void show(AltosState state, AltosListenerState listener_state) {
147 status_update.saved_state = state;
150 state = new AltosState();
153 for (AltosFlightDisplay display : displays) {
154 display.show(state, listener_state);
157 } catch (Exception ex) {
158 System.out.printf("Exception %s\n", ex.toString());
159 for (StackTraceElement e : ex.getStackTrace())
160 System.out.printf("%s\n", e.toString());
169 new TeleGPSPreferences(this, voice());
173 new AltosUIMapPreload(this);
179 if (status_timer != null) {
182 status_update = null;
185 telegps_status.disable_receive();
186 disable_frequency_menu();
189 void connect(AltosDevice device) {
193 AltosFlightReader reader = new AltosTelemetryReader(new AltosSerial(device));
194 set_reader(reader, device);
195 } catch (FileNotFoundException ee) {
196 JOptionPane.showMessageDialog(this,
198 String.format ("Cannot open %s", device.toShortString()),
199 JOptionPane.ERROR_MESSAGE);
200 } catch (AltosSerialInUseException si) {
201 JOptionPane.showMessageDialog(this,
202 String.format("Device \"%s\" already in use",
203 device.toShortString()),
205 JOptionPane.ERROR_MESSAGE);
206 } catch (IOException ee) {
207 JOptionPane.showMessageDialog(this,
208 String.format ("Unknown I/O error on %s", device.toShortString()),
210 JOptionPane.ERROR_MESSAGE);
211 } catch (TimeoutException te) {
212 JOptionPane.showMessageDialog(this,
213 String.format ("Timeout on %s", device.toShortString()),
215 JOptionPane.ERROR_MESSAGE);
216 } catch (InterruptedException ie) {
217 JOptionPane.showMessageDialog(this,
218 String.format("Interrupted %s", device.toShortString()),
219 "Interrupted exception",
220 JOptionPane.ERROR_MESSAGE);
225 AltosDevice device = AltosDeviceUIDialog.show(this,
226 AltosLib.product_basestation);
232 public void scan_device_selected(AltosDevice device) {
237 new AltosScanUI(this, false);
241 new AltosEepromManage(this, AltosLib.product_telegps);
245 new TeleGPSConfig(this);
249 AltosDataChooser chooser;
250 chooser = new AltosDataChooser(this);
251 AltosStateIterable states = chooser.runDialog();
254 new AltosCSVUI(this, states, chooser.file());
258 AltosDataChooser chooser;
259 chooser = new AltosDataChooser(this);
260 AltosStateIterable states = chooser.runDialog();
264 new TeleGPSGraphUI(states, chooser.file());
265 } catch (InterruptedException ie) {
266 } catch (IOException ie) {
271 AltosFlashUI.show(this);
274 public void actionPerformed(ActionEvent ev) {
277 if (new_command.equals(ev.getActionCommand())) {
281 if (preferences_command.equals(ev.getActionCommand())) {
285 if (load_maps_command.equals(ev.getActionCommand())) {
289 if (close_command.equals(ev.getActionCommand())) {
293 if (exit_command.equals(ev.getActionCommand()))
297 if (connect_command.equals(ev.getActionCommand())) {
301 if (disconnect_command.equals(ev.getActionCommand())) {
305 if (scan_command.equals(ev.getActionCommand())) {
311 if (download_command.equals(ev.getActionCommand())) {
315 if (configure_command.equals(ev.getActionCommand())) {
319 if (export_command.equals(ev.getActionCommand())) {
323 if (graph_command.equals(ev.getActionCommand())) {
327 if (flash_command.equals(ev.getActionCommand())) {
333 void enable_frequency_menu(int serial, final AltosFlightReader reader) {
335 if (frequency_listener != null)
336 disable_frequency_menu();
338 frequency_listener = new ActionListener() {
339 public void actionPerformed(ActionEvent e) {
340 double frequency = frequencies.frequency();
342 reader.set_frequency(frequency);
343 } catch (TimeoutException te) {
344 } catch (InterruptedException ie) {
346 reader.save_frequency();
350 frequencies.addActionListener(frequency_listener);
351 frequencies.set_product("Monitor");
352 frequencies.set_serial(serial);
353 frequencies.set_frequency(AltosUIPreferences.frequency(serial));
354 frequencies.setEnabled(true);
358 void disable_frequency_menu() {
359 if (frequency_listener != null) {
360 frequencies.removeActionListener(frequency_listener);
361 frequencies.setEnabled(false);
362 frequency_listener = null;
367 public void set_reader(AltosFlightReader reader, AltosDevice device) {
368 status_update = new TeleGPSStatusUpdate(telegps_status);
370 status_timer = new javax.swing.Timer(100, status_update);
371 status_timer.start();
373 setTitle(String.format("TeleGPS %s", reader.name));
374 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
378 enable_frequency_menu(device.getSerial(), reader);
381 static int number_of_windows;
383 static public void add_window() {
387 static public void subtract_window() {
389 if (number_of_windows == 0)
393 private void close() {
395 AltosUIPreferences.unregister_font_listener(this);
396 AltosPreferences.unregister_units_listener(this);
402 private void add_menu(JMenu menu, String label, String action) {
403 JMenuItem item = new JMenuItem(label);
405 item.addActionListener(this);
406 item.setActionCommand(action);
410 private JMenu make_menu(String label, String[][] items) {
411 JMenu menu = new JMenu(label);
412 for (int i = 0; i < items.length; i++) {
414 if (items[i][1].equals("exit"))
416 if (items[i][1].equals("preferences"))
419 add_menu(menu, items[i][0], items[i][1]);
425 /* OSXAdapter interfaces */
426 public void macosx_file_handler(String path) {
427 process_graph(new File(path));
430 public void macosx_quit_handler() {
434 public void macosx_preferences_handler() {
440 AltosUIPreferences.set_component(this);
442 register_for_macosx_events();
446 bag = getContentPane();
447 bag.setLayout(new GridBagLayout());
449 GridBagConstraints c = new GridBagConstraints();
453 menu_bar = new JMenuBar();
454 setJMenuBar(menu_bar);
456 file_menu = make_menu("File", file_menu_entries);
457 monitor_menu = make_menu("Monitor", monitor_menu_entries);
458 device_menu = make_menu("Device", device_menu_entries);
460 frequencies = new AltosUIFreqList();
461 frequencies.setEnabled(false);
464 c.fill = GridBagConstraints.NONE;
465 c.anchor = GridBagConstraints.WEST;
468 bag.add(frequencies, c);
470 displays = new LinkedList<AltosFlightDisplay>();
474 /* TeleGPS status is always visible */
475 telegps_status = new TeleGPSStatus();
478 c.fill = GridBagConstraints.HORIZONTAL;
481 bag.add(telegps_status, c);
483 displays.add(telegps_status);
486 /* The rest of the window uses a tabbed pane to
487 * show one of the alternate data views
489 pane = new JTabbedPane();
491 /* Make the tabbed pane use the rest of the window space */
494 c.fill = GridBagConstraints.BOTH;
500 map = new AltosUIMap();
501 pane.add(map.getName(), map);
504 gps_info = new TeleGPSInfo();
505 pane.add(gps_info.getName(), gps_info);
506 displays.add(gps_info);
508 gps_state = new TeleGPSState();
509 pane.add(gps_state.getName(), gps_state);
510 displays.add(gps_state);
512 info_table = new AltosInfoTable();
513 pane.add("Table", info_table);
514 displays.add(info_table);
516 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
518 AltosUIPreferences.register_font_listener(this);
519 AltosPreferences.register_units_listener(this);
521 addWindowListener(new WindowAdapter() {
523 public void windowClosing(WindowEvent e) {
534 public TeleGPS(AltosFlightReader reader) {
536 set_reader(reader, null);
539 public TeleGPS(AltosDevice device) {
544 static AltosStateIterable record_iterable(File file) {
547 in = new FileInputStream(file);
548 } catch (Exception e) {
549 System.out.printf("Failed to open file '%s'\n", file);
552 if (file.getName().endsWith("telem"))
553 return new AltosTelemetryFile(in);
555 return new AltosEepromFile(in);
558 static AltosReplayReader replay_file(File file) {
559 AltosStateIterable states = record_iterable(file);
562 return new AltosReplayReader(states.iterator(), file);
565 static boolean process_graph(File file) {
566 AltosStateIterable states = record_iterable(file);
570 new TeleGPSGraphUI(states, file);
571 } catch (Exception e) {
577 static boolean process_replay(File file) {
578 AltosReplayReader new_reader = replay_file(file);
579 if (new_reader == null)
582 new TeleGPS(new_reader);
586 static final int process_none = 0;
587 static final int process_csv = 1;
588 static final int process_kml = 2;
589 static final int process_graph = 3;
590 static final int process_replay = 4;
591 static final int process_summary = 5;
592 static final int process_cat = 6;
594 public static boolean load_library(Frame frame) {
595 if (!AltosUILib.load_library()) {
596 JOptionPane.showMessageDialog(frame,
597 String.format("No AltOS library in \"%s\"",
598 System.getProperty("java.library.path","<undefined>")),
599 "Cannot load device access library",
600 JOptionPane.ERROR_MESSAGE);
606 public static void help(int code) {
607 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
608 System.out.printf(" Options:\n");
609 System.out.printf(" --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n");
610 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
611 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
612 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
613 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
617 public static void main(String[] args) {
622 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
623 } catch (Exception e) {
626 boolean any_created = false;
629 /* Handle batch-mode */
630 int process = process_none;
631 for (int i = 0; i < args.length; i++) {
632 if (args[i].equals("--help"))
634 else if (args[i].equals("--fetchmaps")) {
635 if (args.length < i + 3) {
638 double lat = Double.parseDouble(args[i+1]);
639 double lon = Double.parseDouble(args[i+2]);
640 AltosUIMap.prefetch_maps(lat, lon);
643 } else if (args[i].equals("--replay"))
644 process = process_replay;
645 else if (args[i].equals("--kml"))
646 process = process_kml;
647 else if (args[i].equals("--csv"))
648 process = process_csv;
649 else if (args[i].equals("--graph"))
650 process = process_graph;
651 else if (args[i].equals("--summary"))
652 process = process_summary;
653 else if (args[i].equals("--cat"))
654 process = process_cat;
655 else if (args[i].startsWith("--"))
658 File file = new File(args[i]);
662 if (!process_graph(file))
666 if (!process_replay(file))
676 case process_summary:
686 if (number_of_windows == 0) {
687 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
689 for (AltosDevice device : devices) {
693 if (number_of_windows == 0)