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
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, 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) {
320 AltosFlashUI.show(this);
323 public void actionPerformed(ActionEvent ev) {
326 if (new_command.equals(ev.getActionCommand())) {
330 if (preferences_command.equals(ev.getActionCommand())) {
334 if (load_maps_command.equals(ev.getActionCommand())) {
338 if (close_command.equals(ev.getActionCommand())) {
342 if (exit_command.equals(ev.getActionCommand()))
346 if (connect_command.equals(ev.getActionCommand())) {
350 if (disconnect_command.equals(ev.getActionCommand())) {
354 if (scan_command.equals(ev.getActionCommand())) {
360 if (download_command.equals(ev.getActionCommand())) {
364 if (configure_command.equals(ev.getActionCommand())) {
368 if (export_command.equals(ev.getActionCommand())) {
372 if (graph_command.equals(ev.getActionCommand())) {
376 if (flash_command.equals(ev.getActionCommand())) {
382 void enable_frequency_menu(int serial, final AltosFlightReader reader) {
384 if (frequency_listener != null)
385 disable_frequency_menu();
387 frequency_listener = new ActionListener() {
388 public void actionPerformed(ActionEvent e) {
389 double frequency = frequencies.frequency();
391 reader.set_frequency(frequency);
392 } catch (TimeoutException te) {
393 } catch (InterruptedException ie) {
395 reader.save_frequency();
399 frequencies.addActionListener(frequency_listener);
400 frequencies.set_product("Monitor");
401 frequencies.set_serial(serial);
402 frequencies.set_frequency(AltosUIPreferences.frequency(serial));
403 frequencies.setEnabled(true);
407 void disable_frequency_menu() {
408 if (frequency_listener != null) {
409 frequencies.removeActionListener(frequency_listener);
410 frequencies.setEnabled(false);
411 frequency_listener = null;
416 void enable_rate_menu(int serial, final AltosFlightReader reader) {
418 if (rate_listener != null)
421 rate_listener = new ActionListener() {
422 public void actionPerformed(ActionEvent e) {
423 int rate = rates.rate();
425 reader.set_telemetry_rate(rate);
426 } catch (TimeoutException te) {
427 } catch (InterruptedException ie) {
429 reader.save_telemetry_rate();
433 rates.addActionListener(rate_listener);
434 rates.set_product("Monitor");
435 rates.set_serial(serial);
436 rates.set_rate(AltosUIPreferences.telemetry_rate(serial));
437 rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
440 void disable_rate_menu() {
441 if (rate_listener != null) {
442 rates.removeActionListener(rate_listener);
443 rates.setEnabled(false);
444 rate_listener = null;
449 public void set_reader(AltosFlightReader reader, AltosDevice device, boolean idle_mode) {
450 this.idle_mode = idle_mode;
451 status_update = new TeleGPSStatusUpdate(telegps_status);
453 telegps_status.start(status_update);
455 setTitle(String.format("TeleGPS %s", reader.name));
456 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
459 if (device != null) {
461 disable_frequency_menu();
464 enable_frequency_menu(device.getSerial(), reader);
465 enable_rate_menu(device.getSerial(), reader);
470 static int number_of_windows;
472 static public void add_window() {
476 static public void subtract_window() {
478 if (number_of_windows == 0)
482 private void close() {
484 AltosUIPreferences.unregister_font_listener(this);
485 AltosPreferences.unregister_units_listener(this);
491 private void add_menu(JMenu menu, String label, String action) {
492 JMenuItem item = new JMenuItem(label);
494 item.addActionListener(this);
495 item.setActionCommand(action);
499 private JMenu make_menu(String label, String[][] items) {
500 JMenu menu = new JMenu(label);
501 for (int i = 0; i < items.length; i++) {
503 if (items[i][1].equals("exit"))
505 if (items[i][1].equals("preferences"))
508 add_menu(menu, items[i][0], items[i][1]);
514 /* OSXAdapter interfaces */
515 public void macosx_file_handler(String path) {
516 process_graph(new File(path));
519 public void macosx_quit_handler() {
523 public void macosx_preferences_handler() {
529 AltosUIPreferences.set_component(this);
531 register_for_macosx_events();
535 bag = getContentPane();
536 bag.setLayout(new GridBagLayout());
540 menu_bar = new JMenuBar();
541 setJMenuBar(menu_bar);
543 file_menu = make_menu("File", file_menu_entries);
544 monitor_menu = make_menu("Monitor", monitor_menu_entries);
545 device_menu = make_menu("Device", device_menu_entries);
548 frequencies = new AltosUIFreqList();
549 frequencies.setEnabled(false);
550 bag.add(frequencies, constraints (0, 1));
552 rates = new AltosUIRateList();
553 rates.setEnabled(false);
554 bag.add(rates, constraints(1, 1));
558 displays = new LinkedList<AltosFlightDisplay>();
562 /* TeleGPS status is always visible */
563 telegps_status = new TeleGPSStatus();
564 bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
567 displays.add(telegps_status);
570 /* The rest of the window uses a tabbed pane to
571 * show one of the alternate data views
573 pane = new JTabbedPane();
575 /* Make the tabbed pane use the rest of the window space */
576 bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
578 map = new AltosUIMap();
579 pane.add(map.getName(), map);
582 gps_info = new TeleGPSInfo();
583 pane.add(gps_info.getName(), gps_info);
584 displays.add(gps_info);
586 gps_state = new TeleGPSState();
587 pane.add(gps_state.getName(), gps_state);
588 displays.add(gps_state);
590 info_table = new AltosInfoTable();
591 pane.add("Table", info_table);
592 displays.add(info_table);
594 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
596 AltosUIPreferences.register_font_listener(this);
597 AltosPreferences.register_units_listener(this);
599 addWindowListener(new WindowAdapter() {
601 public void windowClosing(WindowEvent e) {
612 public TeleGPS(AltosFlightReader reader, boolean idle_mode) {
614 set_reader(reader, null, idle_mode);
617 public TeleGPS(AltosDevice device) {
622 static AltosRecordSet record_set(File file) {
624 return AltosLib.record_set(file);
625 } catch (IOException ie) {
626 System.out.printf("%s\n", ie.getMessage());
631 static AltosReplayReader replay_file(File file) {
632 AltosRecordSet set = record_set(file);
635 return new AltosReplayReader(set, file);
638 static boolean process_graph(File file) {
639 AltosRecordSet set = record_set(file);
643 new TeleGPSGraphUI(set, file);
644 } catch (Exception e) {
650 static boolean process_replay(File file) {
651 AltosReplayReader new_reader = replay_file(file);
652 if (new_reader == null)
655 new TeleGPS(new_reader, true);
659 static final int process_none = 0;
660 static final int process_csv = 1;
661 static final int process_kml = 2;
662 static final int process_graph = 3;
663 static final int process_replay = 4;
664 static final int process_summary = 5;
665 static final int process_cat = 6;
667 public static boolean load_library(Frame frame) {
668 if (!AltosUILib.load_library()) {
669 JOptionPane.showMessageDialog(frame,
670 String.format("No AltOS library in \"%s\"",
671 System.getProperty("java.library.path","<undefined>")),
672 "Cannot load device access library",
673 JOptionPane.ERROR_MESSAGE);
679 public static void help(int code) {
680 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
681 System.out.printf(" Options:\n");
682 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
683 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
684 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
685 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
689 public static void main(String[] args) {
694 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
695 } catch (Exception e) {
698 boolean any_created = false;
701 /* Handle batch-mode */
702 int process = process_none;
703 for (int i = 0; i < args.length; i++) {
704 if (args[i].equals("--help"))
706 else if (args[i].equals("--replay"))
707 process = process_replay;
708 else if (args[i].equals("--kml"))
709 process = process_kml;
710 else if (args[i].equals("--csv"))
711 process = process_csv;
712 else if (args[i].equals("--graph"))
713 process = process_graph;
714 else if (args[i].equals("--summary"))
715 process = process_summary;
716 else if (args[i].equals("--cat"))
717 process = process_cat;
718 else if (args[i].startsWith("--"))
721 File file = new File(args[i]);
725 if (!process_graph(file))
729 if (!process_replay(file))
739 case process_summary:
749 if (number_of_windows == 0) {
750 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
752 for (AltosDevice device : devices) {
756 if (number_of_windows == 0)