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());
484 GridBagConstraints c = new GridBagConstraints();
488 menu_bar = new JMenuBar();
489 setJMenuBar(menu_bar);
491 file_menu = make_menu("File", file_menu_entries);
492 monitor_menu = make_menu("Monitor", monitor_menu_entries);
493 device_menu = make_menu("Device", device_menu_entries);
495 frequencies = new AltosUIFreqList();
496 frequencies.setEnabled(false);
499 c.fill = GridBagConstraints.NONE;
500 c.anchor = GridBagConstraints.WEST;
503 bag.add(frequencies, c);
505 rates = new AltosUIRateList();
506 rates.setEnabled(false);
509 c.fill = GridBagConstraints.NONE;
510 c.anchor = GridBagConstraints.WEST;
515 displays = new LinkedList<AltosFlightDisplay>();
519 /* TeleGPS status is always visible */
520 telegps_status = new TeleGPSStatus();
523 c.fill = GridBagConstraints.HORIZONTAL;
526 bag.add(telegps_status, c);
528 displays.add(telegps_status);
531 /* The rest of the window uses a tabbed pane to
532 * show one of the alternate data views
534 pane = new JTabbedPane();
536 /* Make the tabbed pane use the rest of the window space */
539 c.fill = GridBagConstraints.BOTH;
545 map = new AltosUIMapNew();
546 pane.add(map.getName(), map);
549 gps_info = new TeleGPSInfo();
550 pane.add(gps_info.getName(), gps_info);
551 displays.add(gps_info);
553 gps_state = new TeleGPSState();
554 pane.add(gps_state.getName(), gps_state);
555 displays.add(gps_state);
557 info_table = new AltosInfoTable();
558 pane.add("Table", info_table);
559 displays.add(info_table);
561 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
563 AltosUIPreferences.register_font_listener(this);
564 AltosPreferences.register_units_listener(this);
566 addWindowListener(new WindowAdapter() {
568 public void windowClosing(WindowEvent e) {
579 public TeleGPS(AltosFlightReader reader) {
581 set_reader(reader, null);
584 public TeleGPS(AltosDevice device) {
589 static AltosStateIterable record_iterable(File file) {
592 in = new FileInputStream(file);
593 } catch (Exception e) {
594 System.out.printf("Failed to open file '%s'\n", file);
597 if (file.getName().endsWith("telem"))
598 return new AltosTelemetryFile(in);
600 return new AltosEepromFile(in);
603 static AltosReplayReader replay_file(File file) {
604 AltosStateIterable states = record_iterable(file);
607 return new AltosReplayReader(states.iterator(), file);
610 static boolean process_graph(File file) {
611 AltosStateIterable states = record_iterable(file);
615 new TeleGPSGraphUI(states, file);
616 } catch (Exception e) {
622 static boolean process_replay(File file) {
623 AltosReplayReader new_reader = replay_file(file);
624 if (new_reader == null)
627 new TeleGPS(new_reader);
631 static final int process_none = 0;
632 static final int process_csv = 1;
633 static final int process_kml = 2;
634 static final int process_graph = 3;
635 static final int process_replay = 4;
636 static final int process_summary = 5;
637 static final int process_cat = 6;
639 public static boolean load_library(Frame frame) {
640 if (!AltosUILib.load_library()) {
641 JOptionPane.showMessageDialog(frame,
642 String.format("No AltOS library in \"%s\"",
643 System.getProperty("java.library.path","<undefined>")),
644 "Cannot load device access library",
645 JOptionPane.ERROR_MESSAGE);
651 public static void help(int code) {
652 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
653 System.out.printf(" Options:\n");
654 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
655 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
656 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
657 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
661 public static void main(String[] args) {
666 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
667 } catch (Exception e) {
670 boolean any_created = false;
673 /* Handle batch-mode */
674 int process = process_none;
675 for (int i = 0; i < args.length; i++) {
676 if (args[i].equals("--help"))
678 else if (args[i].equals("--replay"))
679 process = process_replay;
680 else if (args[i].equals("--kml"))
681 process = process_kml;
682 else if (args[i].equals("--csv"))
683 process = process_csv;
684 else if (args[i].equals("--graph"))
685 process = process_graph;
686 else if (args[i].equals("--summary"))
687 process = process_summary;
688 else if (args[i].equals("--cat"))
689 process = process_cat;
690 else if (args[i].startsWith("--"))
693 File file = new File(args[i]);
697 if (!process_graph(file))
701 if (!process_replay(file))
711 case process_summary:
721 if (number_of_windows == 0) {
722 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
724 for (AltosDevice device : devices) {
728 if (number_of_windows == 0)