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 = {
35 "/altusmetrum-telegps-16.png",
36 "/altusmetrum-telegps-32.png",
37 "/altusmetrum-telegps-48.png",
38 "/altusmetrum-telegps-64.png",
39 "/altusmetrum-telegps-128.png",
40 "/altusmetrum-telegps-256.png"
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;
63 AltosUIRateList rates;
64 ActionListener rate_listener;
68 TeleGPSStatus telegps_status;
69 TeleGPSStatusUpdate status_update;
70 javax.swing.Timer status_timer;
76 TeleGPSState gps_state;
77 AltosInfoTable info_table;
79 LinkedList<AltosFlightDisplay> displays;
82 final static String new_command = "new";
83 final static String graph_command = "graph";
84 final static String export_command = "export";
85 final static String load_maps_command = "loadmaps";
86 final static String preferences_command = "preferences";
87 final static String close_command = "close";
88 final static String exit_command = "exit";
90 static final String[][] file_menu_entries = new String[][] {
91 { "New Window", new_command },
92 { "Graph Data", graph_command },
93 { "Export Data", export_command },
94 { "Load Maps", load_maps_command },
95 { "Preferences", preferences_command },
96 { "Close", close_command },
97 { "Exit", exit_command },
101 final static String connect_command = "connect";
102 final static String disconnect_command = "disconnect";
103 final static String scan_command = "scan";
105 static final String[][] monitor_menu_entries = new String[][] {
106 { "Connect Device", connect_command },
107 { "Disconnect", disconnect_command },
108 { "Scan Channels", scan_command },
112 final static String download_command = "download";
113 final static String configure_command = "configure";
114 final static String flash_command = "flash";
116 static final String[][] device_menu_entries = new String[][] {
117 { "Download Data", download_command },
118 { "Configure Device", configure_command },
119 { "Flash Device", flash_command },
122 void stop_display() {
123 if (thread != null && thread.isAlive()) {
127 } catch (InterruptedException ie) {}
132 public void reset() {
133 for (AltosFlightDisplay display : displays)
137 public void font_size_changed(int font_size) {
138 for (AltosFlightDisplay display : displays)
139 display.font_size_changed(font_size);
142 public void units_changed(boolean imperial_units) {
143 for (AltosFlightDisplay display : displays)
144 display.units_changed(imperial_units);
147 public void show(AltosState state, AltosListenerState listener_state) {
149 status_update.saved_state = state;
152 state = new AltosState();
155 for (AltosFlightDisplay display : displays) {
156 display.show(state, listener_state);
159 } catch (Exception ex) {
160 System.out.printf("Exception %s\n", ex.toString());
161 for (StackTraceElement e : ex.getStackTrace())
162 System.out.printf("%s\n", e.toString());
171 new TeleGPSPreferences(this, voice());
175 new AltosUIMapPreload(this);
181 if (status_timer != null) {
184 status_update = null;
187 telegps_status.disable_receive();
188 disable_frequency_menu();
192 void connect(AltosDevice device) {
196 AltosFlightReader reader = new AltosTelemetryReader(new AltosSerial(device));
197 set_reader(reader, device);
198 } catch (FileNotFoundException ee) {
199 JOptionPane.showMessageDialog(this,
201 String.format ("Cannot open %s", device.toShortString()),
202 JOptionPane.ERROR_MESSAGE);
203 } catch (AltosSerialInUseException si) {
204 JOptionPane.showMessageDialog(this,
205 String.format("Device \"%s\" already in use",
206 device.toShortString()),
208 JOptionPane.ERROR_MESSAGE);
209 } catch (IOException ee) {
210 JOptionPane.showMessageDialog(this,
211 String.format ("Unknown I/O error on %s", device.toShortString()),
213 JOptionPane.ERROR_MESSAGE);
214 } catch (TimeoutException te) {
215 JOptionPane.showMessageDialog(this,
216 String.format ("Timeout on %s", device.toShortString()),
218 JOptionPane.ERROR_MESSAGE);
219 } catch (InterruptedException ie) {
220 JOptionPane.showMessageDialog(this,
221 String.format("Interrupted %s", device.toShortString()),
222 "Interrupted exception",
223 JOptionPane.ERROR_MESSAGE);
228 AltosDevice device = AltosDeviceUIDialog.show(this,
229 AltosLib.product_basestation);
235 public void scan_device_selected(AltosDevice device) {
240 new AltosScanUI(this, false);
244 new AltosEepromManage(this, AltosLib.product_telegps);
248 new TeleGPSConfig(this);
252 AltosDataChooser chooser;
253 chooser = new AltosDataChooser(this);
254 AltosStateIterable states = chooser.runDialog();
257 new AltosCSVUI(this, states, chooser.file());
261 AltosDataChooser chooser;
262 chooser = new AltosDataChooser(this);
263 AltosStateIterable states = chooser.runDialog();
267 new TeleGPSGraphUI(states, chooser.file());
268 } catch (InterruptedException ie) {
269 } catch (IOException ie) {
274 AltosFlashUI.show(this);
277 public void actionPerformed(ActionEvent ev) {
280 if (new_command.equals(ev.getActionCommand())) {
284 if (preferences_command.equals(ev.getActionCommand())) {
288 if (load_maps_command.equals(ev.getActionCommand())) {
292 if (close_command.equals(ev.getActionCommand())) {
296 if (exit_command.equals(ev.getActionCommand()))
300 if (connect_command.equals(ev.getActionCommand())) {
304 if (disconnect_command.equals(ev.getActionCommand())) {
308 if (scan_command.equals(ev.getActionCommand())) {
314 if (download_command.equals(ev.getActionCommand())) {
318 if (configure_command.equals(ev.getActionCommand())) {
322 if (export_command.equals(ev.getActionCommand())) {
326 if (graph_command.equals(ev.getActionCommand())) {
330 if (flash_command.equals(ev.getActionCommand())) {
336 void enable_frequency_menu(int serial, final AltosFlightReader reader) {
338 if (frequency_listener != null)
339 disable_frequency_menu();
341 frequency_listener = new ActionListener() {
342 public void actionPerformed(ActionEvent e) {
343 double frequency = frequencies.frequency();
345 reader.set_frequency(frequency);
346 } catch (TimeoutException te) {
347 } catch (InterruptedException ie) {
349 reader.save_frequency();
353 frequencies.addActionListener(frequency_listener);
354 frequencies.set_product("Monitor");
355 frequencies.set_serial(serial);
356 frequencies.set_frequency(AltosUIPreferences.frequency(serial));
357 frequencies.setEnabled(true);
361 void disable_frequency_menu() {
362 if (frequency_listener != null) {
363 frequencies.removeActionListener(frequency_listener);
364 frequencies.setEnabled(false);
365 frequency_listener = null;
370 void enable_rate_menu(int serial, final AltosFlightReader reader) {
372 if (rate_listener != null)
375 rate_listener = new ActionListener() {
376 public void actionPerformed(ActionEvent e) {
377 int rate = rates.rate();
379 System.out.printf("set rate %d\n", rate);
380 reader.set_telemetry_rate(rate);
381 } catch (TimeoutException te) {
382 } catch (InterruptedException ie) {
384 reader.save_telemetry_rate();
388 rates.addActionListener(rate_listener);
389 rates.set_product("Monitor");
390 rates.set_serial(serial);
391 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
392 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
395 void disable_rate_menu() {
396 if (rate_listener != null) {
397 rates.removeActionListener(rate_listener);
398 rates.setEnabled(false);
399 rate_listener = null;
404 public void set_reader(AltosFlightReader reader, AltosDevice device) {
405 status_update = new TeleGPSStatusUpdate(telegps_status);
407 status_timer = new javax.swing.Timer(100, status_update);
408 status_timer.start();
410 setTitle(String.format("TeleGPS %s", reader.name));
411 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
414 if (device != null) {
415 enable_frequency_menu(device.getSerial(), reader);
416 enable_rate_menu(device.getSerial(), reader);
420 static int number_of_windows;
422 static public void add_window() {
426 static public void subtract_window() {
428 if (number_of_windows == 0)
432 private void close() {
434 AltosUIPreferences.unregister_font_listener(this);
435 AltosPreferences.unregister_units_listener(this);
441 private void add_menu(JMenu menu, String label, String action) {
442 JMenuItem item = new JMenuItem(label);
444 item.addActionListener(this);
445 item.setActionCommand(action);
449 private JMenu make_menu(String label, String[][] items) {
450 JMenu menu = new JMenu(label);
451 for (int i = 0; i < items.length; i++) {
453 if (items[i][1].equals("exit"))
455 if (items[i][1].equals("preferences"))
458 add_menu(menu, items[i][0], items[i][1]);
464 /* OSXAdapter interfaces */
465 public void macosx_file_handler(String path) {
466 process_graph(new File(path));
469 public void macosx_quit_handler() {
473 public void macosx_preferences_handler() {
479 AltosUIPreferences.set_component(this);
481 register_for_macosx_events();
485 bag = getContentPane();
486 bag.setLayout(new GridBagLayout());
488 GridBagConstraints c = new GridBagConstraints();
492 menu_bar = new JMenuBar();
493 setJMenuBar(menu_bar);
495 file_menu = make_menu("File", file_menu_entries);
496 monitor_menu = make_menu("Monitor", monitor_menu_entries);
497 device_menu = make_menu("Device", device_menu_entries);
499 frequencies = new AltosUIFreqList();
500 frequencies.setEnabled(false);
503 c.fill = GridBagConstraints.NONE;
504 c.anchor = GridBagConstraints.WEST;
507 bag.add(frequencies, c);
509 rates = new AltosUIRateList();
510 rates.setEnabled(false);
513 c.fill = GridBagConstraints.NONE;
514 c.anchor = GridBagConstraints.WEST;
519 displays = new LinkedList<AltosFlightDisplay>();
523 /* TeleGPS status is always visible */
524 telegps_status = new TeleGPSStatus();
527 c.fill = GridBagConstraints.HORIZONTAL;
530 bag.add(telegps_status, c);
532 displays.add(telegps_status);
535 /* The rest of the window uses a tabbed pane to
536 * show one of the alternate data views
538 pane = new JTabbedPane();
540 /* Make the tabbed pane use the rest of the window space */
543 c.fill = GridBagConstraints.BOTH;
549 map = new AltosUIMap();
550 pane.add(map.getName(), map);
553 gps_info = new TeleGPSInfo();
554 pane.add(gps_info.getName(), gps_info);
555 displays.add(gps_info);
557 gps_state = new TeleGPSState();
558 pane.add(gps_state.getName(), gps_state);
559 displays.add(gps_state);
561 info_table = new AltosInfoTable();
562 pane.add("Table", info_table);
563 displays.add(info_table);
565 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
567 AltosUIPreferences.register_font_listener(this);
568 AltosPreferences.register_units_listener(this);
570 addWindowListener(new WindowAdapter() {
572 public void windowClosing(WindowEvent e) {
583 public TeleGPS(AltosFlightReader reader) {
585 set_reader(reader, null);
588 public TeleGPS(AltosDevice device) {
593 static AltosStateIterable record_iterable(File file) {
596 in = new FileInputStream(file);
597 } catch (Exception e) {
598 System.out.printf("Failed to open file '%s'\n", file);
601 if (file.getName().endsWith("telem"))
602 return new AltosTelemetryFile(in);
604 return new AltosEepromFile(in);
607 static AltosReplayReader replay_file(File file) {
608 AltosStateIterable states = record_iterable(file);
611 return new AltosReplayReader(states.iterator(), file);
614 static boolean process_graph(File file) {
615 AltosStateIterable states = record_iterable(file);
619 new TeleGPSGraphUI(states, file);
620 } catch (Exception e) {
626 static boolean process_replay(File file) {
627 AltosReplayReader new_reader = replay_file(file);
628 if (new_reader == null)
631 new TeleGPS(new_reader);
635 static final int process_none = 0;
636 static final int process_csv = 1;
637 static final int process_kml = 2;
638 static final int process_graph = 3;
639 static final int process_replay = 4;
640 static final int process_summary = 5;
641 static final int process_cat = 6;
643 public static boolean load_library(Frame frame) {
644 if (!AltosUILib.load_library()) {
645 JOptionPane.showMessageDialog(frame,
646 String.format("No AltOS library in \"%s\"",
647 System.getProperty("java.library.path","<undefined>")),
648 "Cannot load device access library",
649 JOptionPane.ERROR_MESSAGE);
655 public static void help(int code) {
656 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
657 System.out.printf(" Options:\n");
658 System.out.printf(" --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n");
659 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
660 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
661 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
662 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
666 public static void main(String[] args) {
671 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
672 } catch (Exception e) {
675 boolean any_created = false;
678 /* Handle batch-mode */
679 int process = process_none;
680 for (int i = 0; i < args.length; i++) {
681 if (args[i].equals("--help"))
683 else if (args[i].equals("--fetchmaps")) {
684 if (args.length < i + 3) {
687 double lat = Double.parseDouble(args[i+1]);
688 double lon = Double.parseDouble(args[i+2]);
689 AltosUIMap.prefetch_maps(lat, lon);
692 } else if (args[i].equals("--replay"))
693 process = process_replay;
694 else if (args[i].equals("--kml"))
695 process = process_kml;
696 else if (args[i].equals("--csv"))
697 process = process_csv;
698 else if (args[i].equals("--graph"))
699 process = process_graph;
700 else if (args[i].equals("--summary"))
701 process = process_summary;
702 else if (args[i].equals("--cat"))
703 process = process_cat;
704 else if (args[i].startsWith("--"))
707 File file = new File(args[i]);
711 if (!process_graph(file))
715 if (!process_replay(file))
725 case process_summary:
735 if (number_of_windows == 0) {
736 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
738 for (AltosDevice device : devices) {
742 if (number_of_windows == 0)