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; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 package org.altusmetrum.telegps;
22 import java.awt.event.*;
25 import java.util.concurrent.*;
28 import org.altusmetrum.altoslib_12.*;
29 import org.altusmetrum.altosuilib_12.*;
33 implements AltosFlightDisplay, AltosFontListener, AltosUnitsListener, ActionListener, AltosEepromGrapher
36 static String[] telegps_icon_names = {
37 "/altusmetrum-telegps-16.png",
38 "/altusmetrum-telegps-32.png",
39 "/altusmetrum-telegps-48.png",
40 "/altusmetrum-telegps-64.png",
41 "/altusmetrum-telegps-128.png",
42 "/altusmetrum-telegps-256.png"
45 static { set_icon_names(telegps_icon_names); }
47 static AltosVoice voice;
49 static AltosVoice voice() {
51 voice = new AltosVoice();
55 AltosFlightReader reader;
56 TeleGPSDisplayThread thread;
64 AltosUIFreqList frequencies;
65 ActionListener frequency_listener;
66 AltosUIRateList rates;
67 ActionListener rate_listener;
71 TeleGPSStatus telegps_status;
72 TeleGPSStatusUpdate status_update;
78 TeleGPSState gps_state;
79 AltosInfoTable info_table;
81 LinkedList<AltosFlightDisplay> displays;
84 final static String new_command = "new";
85 final static String graph_command = "graph";
86 final static String export_command = "export";
87 final static String load_maps_command = "loadmaps";
88 final static String preferences_command = "preferences";
89 final static String close_command = "close";
90 final static String exit_command = "exit";
92 static final String[][] file_menu_entries = new String[][] {
93 { "New Window", new_command },
94 { "Graph Data", graph_command },
95 { "Export Data", export_command },
96 { "Load Maps", load_maps_command },
97 { "Preferences", preferences_command },
98 { "Close", close_command },
99 { "Exit", exit_command },
103 final static String connect_command = "connect";
104 final static String disconnect_command = "disconnect";
105 final static String scan_command = "scan";
107 static final String[][] monitor_menu_entries = new String[][] {
108 { "Connect Device", connect_command },
109 { "Disconnect", disconnect_command },
110 { "Scan Channels", scan_command },
114 final static String download_command = "download";
115 final static String configure_command = "configure";
116 final static String flash_command = "flash";
118 static final String[][] device_menu_entries = new String[][] {
119 { "Download Data", download_command },
120 { "Configure Device", configure_command },
121 { "Flash Device", flash_command },
124 void stop_display() {
125 if (thread != null && thread.isAlive()) {
129 } catch (InterruptedException ie) {}
134 public void reset() {
135 for (AltosFlightDisplay display : displays)
139 public void font_size_changed(int font_size) {
140 for (AltosFlightDisplay display : displays)
141 display.font_size_changed(font_size);
144 public void units_changed(boolean imperial_units) {
145 for (AltosFlightDisplay display : displays)
146 display.units_changed(imperial_units);
149 public void show(AltosState state, AltosListenerState listener_state) {
151 status_update.saved_state = state;
152 status_update.saved_listener_state = listener_state;
155 state = new AltosState(new AltosCalData());
158 for (AltosFlightDisplay display : displays) {
159 display.show(state, listener_state);
162 } catch (Exception ex) {
163 System.out.printf("Exception %s\n", ex.toString());
164 for (StackTraceElement e : ex.getStackTrace())
165 System.out.printf("%s\n", e.toString());
174 new TeleGPSPreferences(this, voice());
178 new AltosUIMapPreload(this);
184 telegps_status.stop();
186 telegps_status.disable_receive();
187 disable_frequency_menu();
191 void connect_flight(AltosDevice device) {
193 AltosFlightReader reader = new AltosTelemetryReader(new AltosSerial(device));
194 set_reader(reader, device, false);
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);
224 void connect_idle(AltosDevice device) {
226 AltosFlightReader reader = new AltosIdleReader(new AltosSerial(device), false);
227 set_reader(reader, device, true);
228 } catch (FileNotFoundException ee) {
229 JOptionPane.showMessageDialog(this,
231 String.format ("Cannot open %s", device.toShortString()),
232 JOptionPane.ERROR_MESSAGE);
233 } catch (AltosSerialInUseException si) {
234 JOptionPane.showMessageDialog(this,
235 String.format("Device \"%s\" already in use",
236 device.toShortString()),
238 JOptionPane.ERROR_MESSAGE);
239 } catch (IOException ee) {
240 JOptionPane.showMessageDialog(this,
241 String.format ("Unknown I/O error on %s", device.toShortString()),
243 JOptionPane.ERROR_MESSAGE);
244 } catch (TimeoutException te) {
245 JOptionPane.showMessageDialog(this,
246 String.format ("Timeout on %s", device.toShortString()),
248 JOptionPane.ERROR_MESSAGE);
249 } catch (InterruptedException ie) {
250 JOptionPane.showMessageDialog(this,
251 String.format("Interrupted %s", device.toShortString()),
252 "Interrupted exception",
253 JOptionPane.ERROR_MESSAGE);
257 void connect(AltosDevice device) {
260 if (device.matchProduct(AltosLib.product_basestation))
261 connect_flight(device);
263 connect_idle(device);
267 AltosDevice device = AltosDeviceUIDialog.show(this,
268 AltosLib.product_any);
274 public void scan_device_selected(AltosDevice device) {
279 new AltosScanUI(this, false);
283 new AltosEepromManage(this, this, AltosLib.product_telegps);
287 new TeleGPSConfig(this);
290 private static AltosFlightSeries make_series(AltosRecordSet set) {
291 AltosFlightSeries series = new AltosFlightSeries(set.cal_data());
292 set.capture_series(series);
298 AltosDataChooser chooser = new AltosDataChooser(this);
300 AltosRecordSet set = chooser.runDialog();
303 AltosFlightSeries series = make_series(set);
304 new AltosCSVUI(this, series, chooser.file());
308 AltosDataChooser chooser = new AltosDataChooser(this);
309 AltosRecordSet set = chooser.runDialog();
313 new TeleGPSGraphUI(set, chooser.file());
314 } catch (InterruptedException ie) {
315 } catch (IOException ie) {
319 public void graph_flights(AltosEepromList list) {
320 for (AltosEepromLog log : list) {
321 if (log.file != null) {
322 AltosRecordSet set = record_set(log.file);
325 new TeleGPSGraphUI(set, log.file);
326 } catch (InterruptedException ie) {
327 } catch (IOException ie) {
335 AltosFlashUI.show(this);
338 public void actionPerformed(ActionEvent ev) {
341 if (new_command.equals(ev.getActionCommand())) {
345 if (preferences_command.equals(ev.getActionCommand())) {
349 if (load_maps_command.equals(ev.getActionCommand())) {
353 if (close_command.equals(ev.getActionCommand())) {
357 if (exit_command.equals(ev.getActionCommand()))
361 if (connect_command.equals(ev.getActionCommand())) {
365 if (disconnect_command.equals(ev.getActionCommand())) {
369 if (scan_command.equals(ev.getActionCommand())) {
375 if (download_command.equals(ev.getActionCommand())) {
379 if (configure_command.equals(ev.getActionCommand())) {
383 if (export_command.equals(ev.getActionCommand())) {
387 if (graph_command.equals(ev.getActionCommand())) {
391 if (flash_command.equals(ev.getActionCommand())) {
397 void enable_frequency_menu(int serial, final AltosFlightReader reader) {
399 if (frequency_listener != null)
400 disable_frequency_menu();
402 frequency_listener = new ActionListener() {
403 public void actionPerformed(ActionEvent e) {
404 double frequency = frequencies.frequency();
406 reader.set_frequency(frequency);
407 } catch (TimeoutException te) {
408 } catch (InterruptedException ie) {
410 reader.save_frequency();
414 frequencies.addActionListener(frequency_listener);
415 frequencies.set_product("Monitor");
416 frequencies.set_serial(serial);
417 frequencies.set_frequency(AltosUIPreferences.frequency(serial));
418 frequencies.setEnabled(true);
422 void disable_frequency_menu() {
423 if (frequency_listener != null) {
424 frequencies.removeActionListener(frequency_listener);
425 frequencies.setEnabled(false);
426 frequency_listener = null;
431 void enable_rate_menu(int serial, final AltosFlightReader reader) {
433 if (rate_listener != null)
436 rate_listener = new ActionListener() {
437 public void actionPerformed(ActionEvent e) {
438 int rate = rates.rate();
440 reader.set_telemetry_rate(rate);
441 } catch (TimeoutException te) {
442 } catch (InterruptedException ie) {
444 reader.save_telemetry_rate();
448 rates.addActionListener(rate_listener);
449 rates.set_product("Monitor");
450 rates.set_serial(serial);
451 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
452 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
455 void disable_rate_menu() {
456 if (rate_listener != null) {
457 rates.removeActionListener(rate_listener);
458 rates.setEnabled(false);
459 rate_listener = null;
464 public void set_reader(AltosFlightReader reader, AltosDevice device, boolean idle_mode) {
465 this.idle_mode = idle_mode;
466 status_update = new TeleGPSStatusUpdate(telegps_status);
468 telegps_status.start(status_update);
470 setTitle(String.format("TeleGPS %s", reader.name));
471 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
474 if (device != null) {
476 disable_frequency_menu();
479 enable_frequency_menu(device.getSerial(), reader);
480 enable_rate_menu(device.getSerial(), reader);
485 static int number_of_windows;
487 static public void add_window() {
491 static public void subtract_window() {
493 if (number_of_windows == 0)
497 private void close() {
499 AltosUIPreferences.unregister_font_listener(this);
500 AltosPreferences.unregister_units_listener(this);
506 private void add_menu(JMenu menu, String label, String action) {
507 JMenuItem item = new JMenuItem(label);
509 item.addActionListener(this);
510 item.setActionCommand(action);
514 private JMenu make_menu(String label, String[][] items) {
515 JMenu menu = new JMenu(label);
516 for (int i = 0; i < items.length; i++) {
518 if (items[i][1].equals("exit"))
520 if (items[i][1].equals("preferences"))
523 add_menu(menu, items[i][0], items[i][1]);
529 /* OSXAdapter interfaces */
530 public void macosx_file_handler(String path) {
531 process_graph(new File(path));
534 public void macosx_quit_handler() {
538 public void macosx_preferences_handler() {
544 AltosUIPreferences.set_component(this);
546 register_for_macosx_events();
550 bag = getContentPane();
551 bag.setLayout(new GridBagLayout());
555 menu_bar = new JMenuBar();
556 setJMenuBar(menu_bar);
558 file_menu = make_menu("File", file_menu_entries);
559 monitor_menu = make_menu("Monitor", monitor_menu_entries);
560 device_menu = make_menu("Device", device_menu_entries);
563 frequencies = new AltosUIFreqList();
564 frequencies.setEnabled(false);
565 bag.add(frequencies, constraints (0, 1));
567 rates = new AltosUIRateList();
568 rates.setEnabled(false);
569 bag.add(rates, constraints(1, 1));
573 displays = new LinkedList<AltosFlightDisplay>();
577 /* TeleGPS status is always visible */
578 telegps_status = new TeleGPSStatus();
579 bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
582 displays.add(telegps_status);
585 /* The rest of the window uses a tabbed pane to
586 * show one of the alternate data views
588 pane = new JTabbedPane();
590 /* Make the tabbed pane use the rest of the window space */
591 bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
593 map = new AltosUIMap();
594 pane.add(map.getName(), map);
597 gps_info = new TeleGPSInfo();
598 pane.add(gps_info.getName(), gps_info);
599 displays.add(gps_info);
601 gps_state = new TeleGPSState();
602 pane.add(gps_state.getName(), gps_state);
603 displays.add(gps_state);
605 info_table = new AltosInfoTable();
606 pane.add("Table", info_table);
607 displays.add(info_table);
609 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
611 AltosUIPreferences.register_font_listener(this);
612 AltosPreferences.register_units_listener(this);
614 addWindowListener(new WindowAdapter() {
616 public void windowClosing(WindowEvent e) {
627 public TeleGPS(AltosFlightReader reader, boolean idle_mode) {
629 set_reader(reader, null, idle_mode);
632 public TeleGPS(AltosDevice device) {
637 static AltosRecordSet record_set(File file) {
639 return AltosLib.record_set(file);
640 } catch (IOException ie) {
641 System.out.printf("%s\n", ie.getMessage());
646 static AltosReplayReader replay_file(File file) {
647 AltosRecordSet set = record_set(file);
650 return new AltosReplayReader(set, file);
653 static boolean process_graph(File file) {
654 AltosRecordSet set = record_set(file);
658 new TeleGPSGraphUI(set, file);
659 } catch (Exception e) {
665 static boolean process_replay(File file) {
666 AltosReplayReader new_reader = replay_file(file);
667 if (new_reader == null)
670 new TeleGPS(new_reader, true);
674 static final int process_none = 0;
675 static final int process_csv = 1;
676 static final int process_kml = 2;
677 static final int process_graph = 3;
678 static final int process_replay = 4;
679 static final int process_summary = 5;
680 static final int process_cat = 6;
682 public static boolean load_library(Frame frame) {
683 if (!AltosUILib.load_library()) {
684 JOptionPane.showMessageDialog(frame,
685 String.format("No AltOS library in \"%s\"",
686 System.getProperty("java.library.path","<undefined>")),
687 "Cannot load device access library",
688 JOptionPane.ERROR_MESSAGE);
694 public static void help(int code) {
695 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
696 System.out.printf(" Options:\n");
697 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
698 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
699 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
700 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
704 public static void main(String[] args) {
709 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
710 } catch (Exception e) {
713 boolean any_created = false;
716 /* Handle batch-mode */
717 int process = process_none;
718 for (int i = 0; i < args.length; i++) {
719 if (args[i].equals("--help"))
721 else if (args[i].equals("--replay"))
722 process = process_replay;
723 else if (args[i].equals("--kml"))
724 process = process_kml;
725 else if (args[i].equals("--csv"))
726 process = process_csv;
727 else if (args[i].equals("--graph"))
728 process = process_graph;
729 else if (args[i].equals("--summary"))
730 process = process_summary;
731 else if (args[i].equals("--cat"))
732 process = process_cat;
733 else if (args[i].startsWith("--"))
736 File file = new File(args[i]);
740 if (!process_graph(file))
744 if (!process_replay(file))
754 case process_summary:
764 if (number_of_windows == 0) {
765 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
767 for (AltosDevice device : devices) {
771 if (number_of_windows == 0)