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_11.*;
28 import org.altusmetrum.altosuilib_11.*;
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;
63 AltosUIFreqList frequencies;
64 ActionListener frequency_listener;
65 AltosUIRateList rates;
66 ActionListener rate_listener;
70 TeleGPSStatus telegps_status;
71 TeleGPSStatusUpdate status_update;
77 TeleGPSState gps_state;
78 AltosInfoTable info_table;
80 LinkedList<AltosFlightDisplay> displays;
83 final static String new_command = "new";
84 final static String graph_command = "graph";
85 final static String export_command = "export";
86 final static String load_maps_command = "loadmaps";
87 final static String preferences_command = "preferences";
88 final static String close_command = "close";
89 final static String exit_command = "exit";
91 static final String[][] file_menu_entries = new String[][] {
92 { "New Window", new_command },
93 { "Graph Data", graph_command },
94 { "Export Data", export_command },
95 { "Load Maps", load_maps_command },
96 { "Preferences", preferences_command },
97 { "Close", close_command },
98 { "Exit", exit_command },
102 final static String connect_command = "connect";
103 final static String disconnect_command = "disconnect";
104 final static String scan_command = "scan";
106 static final String[][] monitor_menu_entries = new String[][] {
107 { "Connect Device", connect_command },
108 { "Disconnect", disconnect_command },
109 { "Scan Channels", scan_command },
113 final static String download_command = "download";
114 final static String configure_command = "configure";
115 final static String flash_command = "flash";
117 static final String[][] device_menu_entries = new String[][] {
118 { "Download Data", download_command },
119 { "Configure Device", configure_command },
120 { "Flash Device", flash_command },
123 void stop_display() {
124 if (thread != null && thread.isAlive()) {
128 } catch (InterruptedException ie) {}
133 public void reset() {
134 for (AltosFlightDisplay display : displays)
138 public void font_size_changed(int font_size) {
139 for (AltosFlightDisplay display : displays)
140 display.font_size_changed(font_size);
143 public void units_changed(boolean imperial_units) {
144 for (AltosFlightDisplay display : displays)
145 display.units_changed(imperial_units);
148 public void show(AltosState state, AltosListenerState listener_state) {
150 status_update.saved_state = state;
151 status_update.saved_listener_state = listener_state;
154 state = new AltosState();
157 for (AltosFlightDisplay display : displays) {
158 display.show(state, listener_state);
161 } catch (Exception ex) {
162 System.out.printf("Exception %s\n", ex.toString());
163 for (StackTraceElement e : ex.getStackTrace())
164 System.out.printf("%s\n", e.toString());
173 new TeleGPSPreferences(this, voice());
177 new AltosUIMapPreload(this);
183 telegps_status.stop();
185 telegps_status.disable_receive();
186 disable_frequency_menu();
190 void connect_flight(AltosDevice device) {
192 AltosFlightReader reader = new AltosTelemetryReader(new AltosSerial(device));
193 set_reader(reader, device, false);
194 } catch (FileNotFoundException ee) {
195 JOptionPane.showMessageDialog(this,
197 String.format ("Cannot open %s", device.toShortString()),
198 JOptionPane.ERROR_MESSAGE);
199 } catch (AltosSerialInUseException si) {
200 JOptionPane.showMessageDialog(this,
201 String.format("Device \"%s\" already in use",
202 device.toShortString()),
204 JOptionPane.ERROR_MESSAGE);
205 } catch (IOException ee) {
206 JOptionPane.showMessageDialog(this,
207 String.format ("Unknown I/O error on %s", device.toShortString()),
209 JOptionPane.ERROR_MESSAGE);
210 } catch (TimeoutException te) {
211 JOptionPane.showMessageDialog(this,
212 String.format ("Timeout on %s", device.toShortString()),
214 JOptionPane.ERROR_MESSAGE);
215 } catch (InterruptedException ie) {
216 JOptionPane.showMessageDialog(this,
217 String.format("Interrupted %s", device.toShortString()),
218 "Interrupted exception",
219 JOptionPane.ERROR_MESSAGE);
223 void connect_idle(AltosDevice device) {
225 AltosFlightReader reader = new AltosIdleReader(new AltosSerial(device), false);
226 set_reader(reader, device, true);
227 } catch (FileNotFoundException ee) {
228 JOptionPane.showMessageDialog(this,
230 String.format ("Cannot open %s", device.toShortString()),
231 JOptionPane.ERROR_MESSAGE);
232 } catch (AltosSerialInUseException si) {
233 JOptionPane.showMessageDialog(this,
234 String.format("Device \"%s\" already in use",
235 device.toShortString()),
237 JOptionPane.ERROR_MESSAGE);
238 } catch (IOException ee) {
239 JOptionPane.showMessageDialog(this,
240 String.format ("Unknown I/O error on %s", device.toShortString()),
242 JOptionPane.ERROR_MESSAGE);
243 } catch (TimeoutException te) {
244 JOptionPane.showMessageDialog(this,
245 String.format ("Timeout on %s", device.toShortString()),
247 JOptionPane.ERROR_MESSAGE);
248 } catch (InterruptedException ie) {
249 JOptionPane.showMessageDialog(this,
250 String.format("Interrupted %s", device.toShortString()),
251 "Interrupted exception",
252 JOptionPane.ERROR_MESSAGE);
256 void connect(AltosDevice device) {
259 if (device.matchProduct(AltosLib.product_basestation))
260 connect_flight(device);
262 connect_idle(device);
266 AltosDevice device = AltosDeviceUIDialog.show(this,
267 AltosLib.product_any);
273 public void scan_device_selected(AltosDevice device) {
278 new AltosScanUI(this, false);
282 new AltosEepromManage(this, AltosLib.product_telegps);
286 new TeleGPSConfig(this);
290 AltosDataChooser chooser;
291 chooser = new AltosDataChooser(this);
292 AltosStateIterable states = chooser.runDialog();
295 new AltosCSVUI(this, states, chooser.file());
299 AltosDataChooser chooser;
300 chooser = new AltosDataChooser(this);
301 AltosStateIterable states = chooser.runDialog();
305 new TeleGPSGraphUI(states, chooser.file());
306 } catch (InterruptedException ie) {
307 } catch (IOException ie) {
312 AltosFlashUI.show(this);
315 public void actionPerformed(ActionEvent ev) {
318 if (new_command.equals(ev.getActionCommand())) {
322 if (preferences_command.equals(ev.getActionCommand())) {
326 if (load_maps_command.equals(ev.getActionCommand())) {
330 if (close_command.equals(ev.getActionCommand())) {
334 if (exit_command.equals(ev.getActionCommand()))
338 if (connect_command.equals(ev.getActionCommand())) {
342 if (disconnect_command.equals(ev.getActionCommand())) {
346 if (scan_command.equals(ev.getActionCommand())) {
352 if (download_command.equals(ev.getActionCommand())) {
356 if (configure_command.equals(ev.getActionCommand())) {
360 if (export_command.equals(ev.getActionCommand())) {
364 if (graph_command.equals(ev.getActionCommand())) {
368 if (flash_command.equals(ev.getActionCommand())) {
374 void enable_frequency_menu(int serial, final AltosFlightReader reader) {
376 if (frequency_listener != null)
377 disable_frequency_menu();
379 frequency_listener = new ActionListener() {
380 public void actionPerformed(ActionEvent e) {
381 double frequency = frequencies.frequency();
383 reader.set_frequency(frequency);
384 } catch (TimeoutException te) {
385 } catch (InterruptedException ie) {
387 reader.save_frequency();
391 frequencies.addActionListener(frequency_listener);
392 frequencies.set_product("Monitor");
393 frequencies.set_serial(serial);
394 frequencies.set_frequency(AltosUIPreferences.frequency(serial));
395 frequencies.setEnabled(true);
399 void disable_frequency_menu() {
400 if (frequency_listener != null) {
401 frequencies.removeActionListener(frequency_listener);
402 frequencies.setEnabled(false);
403 frequency_listener = null;
408 void enable_rate_menu(int serial, final AltosFlightReader reader) {
410 if (rate_listener != null)
413 rate_listener = new ActionListener() {
414 public void actionPerformed(ActionEvent e) {
415 int rate = rates.rate();
417 reader.set_telemetry_rate(rate);
418 } catch (TimeoutException te) {
419 } catch (InterruptedException ie) {
421 reader.save_telemetry_rate();
425 rates.addActionListener(rate_listener);
426 rates.set_product("Monitor");
427 rates.set_serial(serial);
428 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
429 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
432 void disable_rate_menu() {
433 if (rate_listener != null) {
434 rates.removeActionListener(rate_listener);
435 rates.setEnabled(false);
436 rate_listener = null;
441 public void set_reader(AltosFlightReader reader, AltosDevice device, boolean idle_mode) {
442 this.idle_mode = idle_mode;
443 status_update = new TeleGPSStatusUpdate(telegps_status);
445 telegps_status.start(status_update);
447 setTitle(String.format("TeleGPS %s", reader.name));
448 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
451 if (device != null) {
453 disable_frequency_menu();
456 enable_frequency_menu(device.getSerial(), reader);
457 enable_rate_menu(device.getSerial(), reader);
462 static int number_of_windows;
464 static public void add_window() {
468 static public void subtract_window() {
470 if (number_of_windows == 0)
474 private void close() {
476 AltosUIPreferences.unregister_font_listener(this);
477 AltosPreferences.unregister_units_listener(this);
483 private void add_menu(JMenu menu, String label, String action) {
484 JMenuItem item = new JMenuItem(label);
486 item.addActionListener(this);
487 item.setActionCommand(action);
491 private JMenu make_menu(String label, String[][] items) {
492 JMenu menu = new JMenu(label);
493 for (int i = 0; i < items.length; i++) {
495 if (items[i][1].equals("exit"))
497 if (items[i][1].equals("preferences"))
500 add_menu(menu, items[i][0], items[i][1]);
506 /* OSXAdapter interfaces */
507 public void macosx_file_handler(String path) {
508 process_graph(new File(path));
511 public void macosx_quit_handler() {
515 public void macosx_preferences_handler() {
521 AltosUIPreferences.set_component(this);
523 register_for_macosx_events();
527 bag = getContentPane();
528 bag.setLayout(new GridBagLayout());
532 menu_bar = new JMenuBar();
533 setJMenuBar(menu_bar);
535 file_menu = make_menu("File", file_menu_entries);
536 monitor_menu = make_menu("Monitor", monitor_menu_entries);
537 device_menu = make_menu("Device", device_menu_entries);
540 frequencies = new AltosUIFreqList();
541 frequencies.setEnabled(false);
542 bag.add(frequencies, constraints (0, 1));
544 rates = new AltosUIRateList();
545 rates.setEnabled(false);
546 bag.add(rates, constraints(1, 1));
550 displays = new LinkedList<AltosFlightDisplay>();
554 /* TeleGPS status is always visible */
555 telegps_status = new TeleGPSStatus();
556 bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
559 displays.add(telegps_status);
562 /* The rest of the window uses a tabbed pane to
563 * show one of the alternate data views
565 pane = new JTabbedPane();
567 /* Make the tabbed pane use the rest of the window space */
568 bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
570 map = new AltosUIMap();
571 pane.add(map.getName(), map);
574 gps_info = new TeleGPSInfo();
575 pane.add(gps_info.getName(), gps_info);
576 displays.add(gps_info);
578 gps_state = new TeleGPSState();
579 pane.add(gps_state.getName(), gps_state);
580 displays.add(gps_state);
582 info_table = new AltosInfoTable();
583 pane.add("Table", info_table);
584 displays.add(info_table);
586 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
588 AltosUIPreferences.register_font_listener(this);
589 AltosPreferences.register_units_listener(this);
591 addWindowListener(new WindowAdapter() {
593 public void windowClosing(WindowEvent e) {
604 public TeleGPS(AltosFlightReader reader, boolean idle_mode) {
606 set_reader(reader, null, idle_mode);
609 public TeleGPS(AltosDevice device) {
614 static AltosStateIterable record_iterable(File file) {
617 in = new FileInputStream(file);
618 } catch (Exception e) {
619 System.out.printf("Failed to open file '%s'\n", file);
622 if (file.getName().endsWith("telem"))
623 return new AltosTelemetryFile(in);
625 return new AltosEepromFile(in);
628 static AltosReplayReader replay_file(File file) {
629 AltosStateIterable states = record_iterable(file);
632 return new AltosReplayReader(states.iterator(), file);
635 static boolean process_graph(File file) {
636 AltosStateIterable states = record_iterable(file);
640 new TeleGPSGraphUI(states, file);
641 } catch (Exception e) {
647 static boolean process_replay(File file) {
648 AltosReplayReader new_reader = replay_file(file);
649 if (new_reader == null)
652 new TeleGPS(new_reader, true);
656 static final int process_none = 0;
657 static final int process_csv = 1;
658 static final int process_kml = 2;
659 static final int process_graph = 3;
660 static final int process_replay = 4;
661 static final int process_summary = 5;
662 static final int process_cat = 6;
664 public static boolean load_library(Frame frame) {
665 if (!AltosUILib.load_library()) {
666 JOptionPane.showMessageDialog(frame,
667 String.format("No AltOS library in \"%s\"",
668 System.getProperty("java.library.path","<undefined>")),
669 "Cannot load device access library",
670 JOptionPane.ERROR_MESSAGE);
676 public static void help(int code) {
677 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
678 System.out.printf(" Options:\n");
679 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
680 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
681 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
682 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
686 public static void main(String[] args) {
691 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
692 } catch (Exception e) {
695 boolean any_created = false;
698 /* Handle batch-mode */
699 int process = process_none;
700 for (int i = 0; i < args.length; i++) {
701 if (args[i].equals("--help"))
703 else if (args[i].equals("--replay"))
704 process = process_replay;
705 else if (args[i].equals("--kml"))
706 process = process_kml;
707 else if (args[i].equals("--csv"))
708 process = process_csv;
709 else if (args[i].equals("--graph"))
710 process = process_graph;
711 else if (args[i].equals("--summary"))
712 process = process_summary;
713 else if (args[i].equals("--cat"))
714 process = process_cat;
715 else if (args[i].startsWith("--"))
718 File file = new File(args[i]);
722 if (!process_graph(file))
726 if (!process_replay(file))
736 case process_summary:
746 if (number_of_windows == 0) {
747 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
749 for (AltosDevice device : devices) {
753 if (number_of_windows == 0)