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_14.*;
29 import org.altusmetrum.altosuilib_14.*;
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();
310 graph_file(this, set, chooser.file());
313 public void graph_flights(AltosEepromList list) {
314 for (AltosEepromLog log : list) {
315 if (log.file != null) {
316 AltosRecordSet set = record_set(log.file);
317 graph_file(this, set, log.file);
323 AltosFlashUI.show(this);
326 public void actionPerformed(ActionEvent ev) {
329 if (new_command.equals(ev.getActionCommand())) {
333 if (preferences_command.equals(ev.getActionCommand())) {
337 if (load_maps_command.equals(ev.getActionCommand())) {
341 if (close_command.equals(ev.getActionCommand())) {
345 if (exit_command.equals(ev.getActionCommand()))
349 if (connect_command.equals(ev.getActionCommand())) {
353 if (disconnect_command.equals(ev.getActionCommand())) {
357 if (scan_command.equals(ev.getActionCommand())) {
363 if (download_command.equals(ev.getActionCommand())) {
367 if (configure_command.equals(ev.getActionCommand())) {
371 if (export_command.equals(ev.getActionCommand())) {
375 if (graph_command.equals(ev.getActionCommand())) {
379 if (flash_command.equals(ev.getActionCommand())) {
385 void enable_frequency_menu(int serial, final AltosFlightReader reader) {
387 if (frequency_listener != null)
388 disable_frequency_menu();
390 frequency_listener = new ActionListener() {
391 public void actionPerformed(ActionEvent e) {
392 double frequency = frequencies.frequency();
394 reader.set_frequency(frequency);
395 } catch (TimeoutException te) {
396 } catch (InterruptedException ie) {
398 reader.save_frequency();
402 frequencies.addActionListener(frequency_listener);
403 frequencies.set_product("Monitor");
404 frequencies.set_serial(serial);
405 frequencies.set_frequency(AltosUIPreferences.frequency(serial));
406 frequencies.setEnabled(true);
410 void disable_frequency_menu() {
411 if (frequency_listener != null) {
412 frequencies.removeActionListener(frequency_listener);
413 frequencies.setEnabled(false);
414 frequency_listener = null;
419 void enable_rate_menu(int serial, final AltosFlightReader reader) {
421 if (rate_listener != null)
424 rate_listener = new ActionListener() {
425 public void actionPerformed(ActionEvent e) {
426 int rate = rates.rate();
428 reader.set_telemetry_rate(rate);
429 } catch (TimeoutException te) {
430 } catch (InterruptedException ie) {
432 reader.save_telemetry_rate();
436 rates.addActionListener(rate_listener);
437 rates.set_product("Monitor");
438 rates.set_serial(serial);
439 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
440 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
443 void disable_rate_menu() {
444 if (rate_listener != null) {
445 rates.removeActionListener(rate_listener);
446 rates.setEnabled(false);
447 rate_listener = null;
452 public void set_reader(AltosFlightReader reader, AltosDevice device, boolean idle_mode) {
453 this.idle_mode = idle_mode;
454 status_update = new TeleGPSStatusUpdate(telegps_status);
456 telegps_status.start(status_update);
458 setTitle(String.format("TeleGPS %s", reader.name));
459 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
462 if (device != null) {
464 disable_frequency_menu();
467 enable_frequency_menu(device.getSerial(), reader);
468 enable_rate_menu(device.getSerial(), reader);
473 static int number_of_windows;
475 static public void add_window() {
479 static public void subtract_window() {
481 if (number_of_windows == 0)
485 private void close() {
487 AltosUIPreferences.unregister_font_listener(this);
488 AltosPreferences.unregister_units_listener(this);
494 private void add_menu(JMenu menu, String label, String action) {
495 JMenuItem item = new JMenuItem(label);
497 item.addActionListener(this);
498 item.setActionCommand(action);
502 private JMenu make_menu(String label, String[][] items) {
503 JMenu menu = new JMenu(label);
504 for (int i = 0; i < items.length; i++) {
506 if (items[i][1].equals("exit"))
508 if (items[i][1].equals("preferences"))
511 add_menu(menu, items[i][0], items[i][1]);
517 /* OSXAdapter interfaces */
518 public void macosx_file_handler(String path) {
519 process_graph(new File(path));
522 public void macosx_quit_handler() {
526 public void macosx_preferences_handler() {
532 AltosUIPreferences.set_component(this);
534 register_for_macosx_events();
538 bag = getContentPane();
539 bag.setLayout(new GridBagLayout());
543 menu_bar = new JMenuBar();
544 setJMenuBar(menu_bar);
546 file_menu = make_menu("File", file_menu_entries);
547 monitor_menu = make_menu("Monitor", monitor_menu_entries);
548 device_menu = make_menu("Device", device_menu_entries);
551 frequencies = new AltosUIFreqList();
552 frequencies.setEnabled(false);
553 bag.add(frequencies, constraints (0, 1));
555 rates = new AltosUIRateList();
556 rates.setEnabled(false);
557 bag.add(rates, constraints(1, 1));
561 displays = new LinkedList<AltosFlightDisplay>();
565 /* TeleGPS status is always visible */
566 telegps_status = new TeleGPSStatus();
567 bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
570 displays.add(telegps_status);
573 /* The rest of the window uses a tabbed pane to
574 * show one of the alternate data views
576 pane = new JTabbedPane();
578 /* Make the tabbed pane use the rest of the window space */
579 bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
581 map = new AltosUIMap();
582 pane.add(map.getName(), map);
585 gps_info = new TeleGPSInfo();
586 pane.add(gps_info.getName(), gps_info);
587 displays.add(gps_info);
589 gps_state = new TeleGPSState();
590 pane.add(gps_state.getName(), gps_state);
591 displays.add(gps_state);
593 info_table = new AltosInfoTable();
594 pane.add("Table", info_table);
595 displays.add(info_table);
597 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
599 AltosUIPreferences.register_font_listener(this);
600 AltosPreferences.register_units_listener(this);
602 addWindowListener(new WindowAdapter() {
604 public void windowClosing(WindowEvent e) {
615 public TeleGPS(AltosFlightReader reader, boolean idle_mode) {
617 set_reader(reader, null, idle_mode);
620 public TeleGPS(AltosDevice device) {
625 static AltosRecordSet record_set(File file) {
627 return AltosLib.record_set(file);
628 } catch (IOException ie) {
629 System.out.printf("%s\n", ie.getMessage());
634 static AltosReplayReader replay_file(File file) {
635 AltosRecordSet set = record_set(file);
638 return new AltosReplayReader(set, file);
641 private static boolean graph_file(TeleGPS telegps, AltosRecordSet set, File file) {
645 JOptionPane.showMessageDialog(telegps,
646 String.format("Failed to parse file %s", file),
648 JOptionPane.ERROR_MESSAGE);
652 new TeleGPSGraphUI(set, file);
653 } catch (IOException e) {
654 System.out.printf("Exception %s\n", e.toString());
655 } catch (InterruptedException e) {
656 System.out.printf("Exception %s\n", e.toString());
662 static boolean process_graph(File file) {
663 AltosRecordSet set = record_set(file);
664 return graph_file(null, set, file);
667 static boolean process_replay(File file) {
668 AltosReplayReader new_reader = replay_file(file);
669 if (new_reader == null)
672 new TeleGPS(new_reader, true);
676 static final int process_none = 0;
677 static final int process_csv = 1;
678 static final int process_kml = 2;
679 static final int process_graph = 3;
680 static final int process_replay = 4;
681 static final int process_summary = 5;
682 static final int process_cat = 6;
684 public static boolean load_library(Frame frame) {
685 if (!AltosUILib.load_library()) {
686 JOptionPane.showMessageDialog(frame,
687 String.format("No AltOS library in \"%s\"",
688 System.getProperty("java.library.path","<undefined>")),
689 "Cannot load device access library",
690 JOptionPane.ERROR_MESSAGE);
696 public static void help(int code) {
697 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
698 System.out.printf(" Options:\n");
699 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
700 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
701 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
702 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
706 public static void main(String[] args) {
711 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
712 } catch (Exception e) {
715 boolean any_created = false;
718 /* Handle batch-mode */
719 int process = process_none;
720 for (int i = 0; i < args.length; i++) {
721 if (args[i].equals("--help"))
723 else if (args[i].equals("--replay"))
724 process = process_replay;
725 else if (args[i].equals("--kml"))
726 process = process_kml;
727 else if (args[i].equals("--csv"))
728 process = process_csv;
729 else if (args[i].equals("--graph"))
730 process = process_graph;
731 else if (args[i].equals("--summary"))
732 process = process_summary;
733 else if (args[i].equals("--cat"))
734 process = process_cat;
735 else if (args[i].startsWith("--"))
738 File file = new File(args[i]);
742 if (!process_graph(file))
746 if (!process_replay(file))
756 case process_summary:
766 if (number_of_windows == 0) {
767 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
769 for (AltosDevice device : devices) {
773 if (number_of_windows == 0)