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.*;
27 import org.altusmetrum.altoslib_7.*;
28 import org.altusmetrum.altosuilib_7.*;
32 implements AltosFlightDisplay, AltosFontListener, AltosUnitsListener, ActionListener
35 static String[] telegps_icon_names = {
36 "/altusmetrum-telegps-16.png",
37 "/altusmetrum-telegps-32.png",
38 "/altusmetrum-telegps-48.png",
39 "/altusmetrum-telegps-64.png",
40 "/altusmetrum-telegps-128.png",
41 "/altusmetrum-telegps-256.png"
44 static { set_icon_names(telegps_icon_names); }
46 static AltosVoice voice;
48 static AltosVoice voice() {
50 voice = new AltosVoice();
54 AltosFlightReader reader;
55 TeleGPSDisplayThread thread;
62 AltosUIFreqList frequencies;
63 ActionListener frequency_listener;
64 AltosUIRateList rates;
65 ActionListener rate_listener;
69 TeleGPSStatus telegps_status;
70 TeleGPSStatusUpdate status_update;
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;
150 status_update.saved_listener_state = listener_state;
153 state = new AltosState();
156 for (AltosFlightDisplay display : displays) {
157 display.show(state, listener_state);
160 } catch (Exception ex) {
161 System.out.printf("Exception %s\n", ex.toString());
162 for (StackTraceElement e : ex.getStackTrace())
163 System.out.printf("%s\n", e.toString());
172 new TeleGPSPreferences(this, voice());
176 new AltosUIMapPreloadNew(this);
182 telegps_status.stop();
184 telegps_status.disable_receive();
185 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 void enable_rate_menu(int serial, final AltosFlightReader reader) {
369 if (rate_listener != null)
372 rate_listener = new ActionListener() {
373 public void actionPerformed(ActionEvent e) {
374 int rate = rates.rate();
376 System.out.printf("set rate %d\n", rate);
377 reader.set_telemetry_rate(rate);
378 } catch (TimeoutException te) {
379 } catch (InterruptedException ie) {
381 reader.save_telemetry_rate();
385 rates.addActionListener(rate_listener);
386 rates.set_product("Monitor");
387 rates.set_serial(serial);
388 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
389 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
392 void disable_rate_menu() {
393 if (rate_listener != null) {
394 rates.removeActionListener(rate_listener);
395 rates.setEnabled(false);
396 rate_listener = null;
401 public void set_reader(AltosFlightReader reader, AltosDevice device) {
402 status_update = new TeleGPSStatusUpdate(telegps_status);
404 telegps_status.start(status_update);
406 setTitle(String.format("TeleGPS %s", reader.name));
407 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
410 if (device != null) {
411 enable_frequency_menu(device.getSerial(), reader);
412 enable_rate_menu(device.getSerial(), reader);
416 static int number_of_windows;
418 static public void add_window() {
422 static public void subtract_window() {
424 if (number_of_windows == 0)
428 private void close() {
430 AltosUIPreferences.unregister_font_listener(this);
431 AltosPreferences.unregister_units_listener(this);
437 private void add_menu(JMenu menu, String label, String action) {
438 JMenuItem item = new JMenuItem(label);
440 item.addActionListener(this);
441 item.setActionCommand(action);
445 private JMenu make_menu(String label, String[][] items) {
446 JMenu menu = new JMenu(label);
447 for (int i = 0; i < items.length; i++) {
449 if (items[i][1].equals("exit"))
451 if (items[i][1].equals("preferences"))
454 add_menu(menu, items[i][0], items[i][1]);
460 /* OSXAdapter interfaces */
461 public void macosx_file_handler(String path) {
462 process_graph(new File(path));
465 public void macosx_quit_handler() {
469 public void macosx_preferences_handler() {
475 AltosUIPreferences.set_component(this);
477 register_for_macosx_events();
481 bag = getContentPane();
482 bag.setLayout(new GridBagLayout());
486 menu_bar = new JMenuBar();
487 setJMenuBar(menu_bar);
489 file_menu = make_menu("File", file_menu_entries);
490 monitor_menu = make_menu("Monitor", monitor_menu_entries);
491 device_menu = make_menu("Device", device_menu_entries);
494 frequencies = new AltosUIFreqList();
495 frequencies.setEnabled(false);
496 bag.add(frequencies, constraints (0, 1));
498 rates = new AltosUIRateList();
499 rates.setEnabled(false);
500 bag.add(rates, constraints(1, 1));
504 displays = new LinkedList<AltosFlightDisplay>();
508 /* TeleGPS status is always visible */
509 telegps_status = new TeleGPSStatus();
510 bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
513 displays.add(telegps_status);
516 /* The rest of the window uses a tabbed pane to
517 * show one of the alternate data views
519 pane = new JTabbedPane();
521 /* Make the tabbed pane use the rest of the window space */
522 bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
524 map = new AltosUIMapNew();
525 pane.add(map.getName(), map);
528 gps_info = new TeleGPSInfo();
529 pane.add(gps_info.getName(), gps_info);
530 displays.add(gps_info);
532 gps_state = new TeleGPSState();
533 pane.add(gps_state.getName(), gps_state);
534 displays.add(gps_state);
536 info_table = new AltosInfoTable();
537 pane.add("Table", info_table);
538 displays.add(info_table);
540 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
542 AltosUIPreferences.register_font_listener(this);
543 AltosPreferences.register_units_listener(this);
545 addWindowListener(new WindowAdapter() {
547 public void windowClosing(WindowEvent e) {
558 public TeleGPS(AltosFlightReader reader) {
560 set_reader(reader, null);
563 public TeleGPS(AltosDevice device) {
568 static AltosStateIterable record_iterable(File file) {
571 in = new FileInputStream(file);
572 } catch (Exception e) {
573 System.out.printf("Failed to open file '%s'\n", file);
576 if (file.getName().endsWith("telem"))
577 return new AltosTelemetryFile(in);
579 return new AltosEepromFile(in);
582 static AltosReplayReader replay_file(File file) {
583 AltosStateIterable states = record_iterable(file);
586 return new AltosReplayReader(states.iterator(), file);
589 static boolean process_graph(File file) {
590 AltosStateIterable states = record_iterable(file);
594 new TeleGPSGraphUI(states, file);
595 } catch (Exception e) {
601 static boolean process_replay(File file) {
602 AltosReplayReader new_reader = replay_file(file);
603 if (new_reader == null)
606 new TeleGPS(new_reader);
610 static final int process_none = 0;
611 static final int process_csv = 1;
612 static final int process_kml = 2;
613 static final int process_graph = 3;
614 static final int process_replay = 4;
615 static final int process_summary = 5;
616 static final int process_cat = 6;
618 public static boolean load_library(Frame frame) {
619 if (!AltosUILib.load_library()) {
620 JOptionPane.showMessageDialog(frame,
621 String.format("No AltOS library in \"%s\"",
622 System.getProperty("java.library.path","<undefined>")),
623 "Cannot load device access library",
624 JOptionPane.ERROR_MESSAGE);
630 public static void help(int code) {
631 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
632 System.out.printf(" Options:\n");
633 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
634 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
635 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
636 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
640 public static void main(String[] args) {
645 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
646 } catch (Exception e) {
649 boolean any_created = false;
652 /* Handle batch-mode */
653 int process = process_none;
654 for (int i = 0; i < args.length; i++) {
655 if (args[i].equals("--help"))
657 else if (args[i].equals("--replay"))
658 process = process_replay;
659 else if (args[i].equals("--kml"))
660 process = process_kml;
661 else if (args[i].equals("--csv"))
662 process = process_csv;
663 else if (args[i].equals("--graph"))
664 process = process_graph;
665 else if (args[i].equals("--summary"))
666 process = process_summary;
667 else if (args[i].equals("--cat"))
668 process = process_cat;
669 else if (args[i].startsWith("--"))
672 File file = new File(args[i]);
676 if (!process_graph(file))
680 if (!process_replay(file))
690 case process_summary:
700 if (number_of_windows == 0) {
701 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
703 for (AltosDevice device : devices) {
707 if (number_of_windows == 0)