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_9.*;
28 import org.altusmetrum.altosuilib_9.*;
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 reader.set_telemetry_rate(rate);
377 } catch (TimeoutException te) {
378 } catch (InterruptedException ie) {
380 reader.save_telemetry_rate();
384 rates.addActionListener(rate_listener);
385 rates.set_product("Monitor");
386 rates.set_serial(serial);
387 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
388 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
391 void disable_rate_menu() {
392 if (rate_listener != null) {
393 rates.removeActionListener(rate_listener);
394 rates.setEnabled(false);
395 rate_listener = null;
400 public void set_reader(AltosFlightReader reader, AltosDevice device) {
401 status_update = new TeleGPSStatusUpdate(telegps_status);
403 telegps_status.start(status_update);
405 setTitle(String.format("TeleGPS %s", reader.name));
406 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
409 if (device != null) {
410 enable_frequency_menu(device.getSerial(), reader);
411 enable_rate_menu(device.getSerial(), reader);
415 static int number_of_windows;
417 static public void add_window() {
421 static public void subtract_window() {
423 if (number_of_windows == 0)
427 private void close() {
429 AltosUIPreferences.unregister_font_listener(this);
430 AltosPreferences.unregister_units_listener(this);
436 private void add_menu(JMenu menu, String label, String action) {
437 JMenuItem item = new JMenuItem(label);
439 item.addActionListener(this);
440 item.setActionCommand(action);
444 private JMenu make_menu(String label, String[][] items) {
445 JMenu menu = new JMenu(label);
446 for (int i = 0; i < items.length; i++) {
448 if (items[i][1].equals("exit"))
450 if (items[i][1].equals("preferences"))
453 add_menu(menu, items[i][0], items[i][1]);
459 /* OSXAdapter interfaces */
460 public void macosx_file_handler(String path) {
461 process_graph(new File(path));
464 public void macosx_quit_handler() {
468 public void macosx_preferences_handler() {
474 AltosUIPreferences.set_component(this);
476 register_for_macosx_events();
480 bag = getContentPane();
481 bag.setLayout(new GridBagLayout());
485 menu_bar = new JMenuBar();
486 setJMenuBar(menu_bar);
488 file_menu = make_menu("File", file_menu_entries);
489 monitor_menu = make_menu("Monitor", monitor_menu_entries);
490 device_menu = make_menu("Device", device_menu_entries);
493 frequencies = new AltosUIFreqList();
494 frequencies.setEnabled(false);
495 bag.add(frequencies, constraints (0, 1));
497 rates = new AltosUIRateList();
498 rates.setEnabled(false);
499 bag.add(rates, constraints(1, 1));
503 displays = new LinkedList<AltosFlightDisplay>();
507 /* TeleGPS status is always visible */
508 telegps_status = new TeleGPSStatus();
509 bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
512 displays.add(telegps_status);
515 /* The rest of the window uses a tabbed pane to
516 * show one of the alternate data views
518 pane = new JTabbedPane();
520 /* Make the tabbed pane use the rest of the window space */
521 bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
523 map = new AltosUIMapNew();
524 pane.add(map.getName(), map);
527 gps_info = new TeleGPSInfo();
528 pane.add(gps_info.getName(), gps_info);
529 displays.add(gps_info);
531 gps_state = new TeleGPSState();
532 pane.add(gps_state.getName(), gps_state);
533 displays.add(gps_state);
535 info_table = new AltosInfoTable();
536 pane.add("Table", info_table);
537 displays.add(info_table);
539 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
541 AltosUIPreferences.register_font_listener(this);
542 AltosPreferences.register_units_listener(this);
544 addWindowListener(new WindowAdapter() {
546 public void windowClosing(WindowEvent e) {
557 public TeleGPS(AltosFlightReader reader) {
559 set_reader(reader, null);
562 public TeleGPS(AltosDevice device) {
567 static AltosStateIterable record_iterable(File file) {
570 in = new FileInputStream(file);
571 } catch (Exception e) {
572 System.out.printf("Failed to open file '%s'\n", file);
575 if (file.getName().endsWith("telem"))
576 return new AltosTelemetryFile(in);
578 return new AltosEepromFile(in);
581 static AltosReplayReader replay_file(File file) {
582 AltosStateIterable states = record_iterable(file);
585 return new AltosReplayReader(states.iterator(), file);
588 static boolean process_graph(File file) {
589 AltosStateIterable states = record_iterable(file);
593 new TeleGPSGraphUI(states, file);
594 } catch (Exception e) {
600 static boolean process_replay(File file) {
601 AltosReplayReader new_reader = replay_file(file);
602 if (new_reader == null)
605 new TeleGPS(new_reader);
609 static final int process_none = 0;
610 static final int process_csv = 1;
611 static final int process_kml = 2;
612 static final int process_graph = 3;
613 static final int process_replay = 4;
614 static final int process_summary = 5;
615 static final int process_cat = 6;
617 public static boolean load_library(Frame frame) {
618 if (!AltosUILib.load_library()) {
619 JOptionPane.showMessageDialog(frame,
620 String.format("No AltOS library in \"%s\"",
621 System.getProperty("java.library.path","<undefined>")),
622 "Cannot load device access library",
623 JOptionPane.ERROR_MESSAGE);
629 public static void help(int code) {
630 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
631 System.out.printf(" Options:\n");
632 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
633 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
634 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
635 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
639 public static void main(String[] args) {
644 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
645 } catch (Exception e) {
648 boolean any_created = false;
651 /* Handle batch-mode */
652 int process = process_none;
653 for (int i = 0; i < args.length; i++) {
654 if (args[i].equals("--help"))
656 else if (args[i].equals("--replay"))
657 process = process_replay;
658 else if (args[i].equals("--kml"))
659 process = process_kml;
660 else if (args[i].equals("--csv"))
661 process = process_csv;
662 else if (args[i].equals("--graph"))
663 process = process_graph;
664 else if (args[i].equals("--summary"))
665 process = process_summary;
666 else if (args[i].equals("--cat"))
667 process = process_cat;
668 else if (args[i].startsWith("--"))
671 File file = new File(args[i]);
675 if (!process_graph(file))
679 if (!process_replay(file))
689 case process_summary:
699 if (number_of_windows == 0) {
700 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
702 for (AltosDevice device : devices) {
706 if (number_of_windows == 0)