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.*;
26 import org.altusmetrum.altoslib_4.*;
27 import org.altusmetrum.altosuilib_2.*;
31 implements AltosFlightDisplay, AltosFontListener, AltosUnitsListener, ActionListener
34 static String[] telegps_icon_names = {
43 static { set_icon_names(telegps_icon_names); }
45 static AltosVoice voice;
47 static AltosVoice voice() {
49 voice = new AltosVoice();
53 AltosFlightReader reader;
54 TeleGPSDisplayThread thread;
61 AltosFreqList frequencies;
62 ActionListener frequency_listener;
66 TeleGPSStatus telegps_status;
67 TeleGPSStatusUpdate status_update;
68 javax.swing.Timer status_timer;
74 TeleGPSState gps_state;
75 AltosInfoTable info_table;
77 LinkedList<AltosFlightDisplay> displays;
80 final static String new_command = "new";
81 final static String graph_command = "graph";
82 final static String export_command = "export";
83 final static String load_maps_command = "loadmaps";
84 final static String preferences_command = "preferences";
85 final static String close_command = "close";
86 final static String exit_command = "exit";
88 static final String[][] file_menu_entries = new String[][] {
89 { "New Window", new_command },
90 { "Graph Data", graph_command },
91 { "Export Data", export_command },
92 { "Load Maps", load_maps_command },
93 { "Preferences", preferences_command },
94 { "Close", close_command },
95 { "Exit", exit_command },
99 final static String connect_command = "connect";
100 final static String disconnect_command = "disconnect";
101 final static String scan_command = "scan";
103 static final String[][] monitor_menu_entries = new String[][] {
104 { "Connect Device", connect_command },
105 { "Disconnect", disconnect_command },
106 { "Scan Channels", scan_command },
110 final static String download_command = "download";
111 final static String configure_command = "configure";
112 final static String flash_command = "flash";
114 static final String[][] device_menu_entries = new String[][] {
115 { "Download Data", download_command },
116 { "Configure Device", configure_command },
117 { "Flash Device", flash_command },
120 void stop_display() {
121 if (thread != null && thread.isAlive()) {
125 } catch (InterruptedException ie) {}
130 public void reset() {
131 for (AltosFlightDisplay display : displays)
135 public void font_size_changed(int font_size) {
136 for (AltosFlightDisplay display : displays)
137 display.font_size_changed(font_size);
140 public void units_changed(boolean imperial_units) {
141 for (AltosFlightDisplay display : displays)
142 display.units_changed(imperial_units);
145 public void show(AltosState state, AltosListenerState listener_state) {
147 status_update.saved_state = state;
150 state = new AltosState();
153 for (AltosFlightDisplay display : displays) {
154 display.show(state, listener_state);
157 } catch (Exception ex) {
158 System.out.printf("Exception %s\n", ex.toString());
159 for (StackTraceElement e : ex.getStackTrace())
160 System.out.printf("%s\n", e.toString());
169 new TeleGPSPreferences(this, voice());
173 new AltosUIMapPreload(this);
179 if (status_timer != null) {
182 status_update = null;
185 telegps_status.disable_receive();
186 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));
355 menu_bar.add(frequencies);
359 void disable_frequency_menu() {
360 if (frequency_listener == null)
362 frequencies.removeActionListener(frequency_listener);
363 menu_bar.remove(frequencies);
365 frequency_listener = null;
368 public void set_reader(AltosFlightReader reader, AltosDevice device) {
369 status_update = new TeleGPSStatusUpdate(telegps_status);
371 status_timer = new javax.swing.Timer(100, status_update);
372 status_timer.start();
374 setTitle(String.format("TeleGPS %s", reader.name));
375 thread = new TeleGPSDisplayThread(this, voice(), this, reader);
379 enable_frequency_menu(device.getSerial(), reader);
382 static int number_of_windows;
384 static public void add_window() {
388 static public void subtract_window() {
390 if (number_of_windows == 0)
394 private void close() {
396 AltosUIPreferences.unregister_font_listener(this);
397 AltosPreferences.unregister_units_listener(this);
403 private void add_menu(JMenu menu, String label, String action) {
404 JMenuItem item = new JMenuItem(label);
406 item.addActionListener(this);
407 item.setActionCommand(action);
411 private JMenu make_menu(String label, String[][] items) {
412 JMenu menu = new JMenu(label);
413 for (int i = 0; i < items.length; i++)
414 add_menu(menu, items[i][0], items[i][1]);
421 AltosUIPreferences.set_component(this);
425 bag = getContentPane();
426 bag.setLayout(new GridBagLayout());
428 GridBagConstraints c = new GridBagConstraints();
432 menu_bar = new JMenuBar();
433 setJMenuBar(menu_bar);
435 file_menu = make_menu("File", file_menu_entries);
436 monitor_menu = make_menu("Monitor", monitor_menu_entries);
437 device_menu = make_menu("Device", device_menu_entries);
438 frequencies = new AltosFreqList();
440 displays = new LinkedList<AltosFlightDisplay>();
444 /* TeleGPS status is always visible */
445 telegps_status = new TeleGPSStatus();
448 c.fill = GridBagConstraints.HORIZONTAL;
451 bag.add(telegps_status, c);
453 displays.add(telegps_status);
456 /* The rest of the window uses a tabbed pane to
457 * show one of the alternate data views
459 pane = new JTabbedPane();
461 /* Make the tabbed pane use the rest of the window space */
464 c.fill = GridBagConstraints.BOTH;
470 map = new AltosUIMap();
471 pane.add(map.getName(), map);
474 gps_info = new TeleGPSInfo();
475 pane.add(gps_info.getName(), gps_info);
476 displays.add(gps_info);
478 gps_state = new TeleGPSState();
479 pane.add(gps_state.getName(), gps_state);
480 displays.add(gps_state);
482 info_table = new AltosInfoTable();
483 pane.add("Table", info_table);
484 displays.add(info_table);
486 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
488 AltosUIPreferences.register_font_listener(this);
489 AltosPreferences.register_units_listener(this);
491 addWindowListener(new WindowAdapter() {
493 public void windowClosing(WindowEvent e) {
504 public TeleGPS(AltosFlightReader reader) {
506 set_reader(reader, null);
509 public TeleGPS(AltosDevice device) {
514 static AltosStateIterable record_iterable(File file) {
517 in = new FileInputStream(file);
518 } catch (Exception e) {
519 System.out.printf("Failed to open file '%s'\n", file);
522 if (file.getName().endsWith("telem"))
523 return new AltosTelemetryFile(in);
525 return new AltosEepromFile(in);
528 static AltosReplayReader replay_file(File file) {
529 AltosStateIterable states = record_iterable(file);
532 return new AltosReplayReader(states.iterator(), file);
535 static boolean process_graph(File file) {
536 AltosStateIterable states = record_iterable(file);
540 new TeleGPSGraphUI(states, file);
541 } catch (Exception e) {
547 static boolean process_replay(File file) {
548 AltosReplayReader new_reader = replay_file(file);
549 if (new_reader == null)
552 new TeleGPS(new_reader);
556 static final int process_none = 0;
557 static final int process_csv = 1;
558 static final int process_kml = 2;
559 static final int process_graph = 3;
560 static final int process_replay = 4;
561 static final int process_summary = 5;
562 static final int process_cat = 6;
564 public static boolean load_library(Frame frame) {
565 if (!AltosUILib.load_library()) {
566 JOptionPane.showMessageDialog(frame,
567 String.format("No AltOS library in \"%s\"",
568 System.getProperty("java.library.path","<undefined>")),
569 "Cannot load device access library",
570 JOptionPane.ERROR_MESSAGE);
576 public static void help(int code) {
577 System.out.printf("Usage: altosui [OPTION]... [FILE]...\n");
578 System.out.printf(" Options:\n");
579 System.out.printf(" --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n");
580 System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n");
581 System.out.printf(" --graph <filename>\t\tgraph a flight\n");
582 System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n");
583 System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n");
587 public static void main(String[] args) {
592 UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
593 } catch (Exception e) {
596 boolean any_created = false;
599 /* Handle batch-mode */
600 int process = process_none;
601 for (int i = 0; i < args.length; i++) {
602 if (args[i].equals("--help"))
604 else if (args[i].equals("--fetchmaps")) {
605 if (args.length < i + 3) {
608 double lat = Double.parseDouble(args[i+1]);
609 double lon = Double.parseDouble(args[i+2]);
610 AltosUIMap.prefetch_maps(lat, lon);
613 } else if (args[i].equals("--replay"))
614 process = process_replay;
615 else if (args[i].equals("--kml"))
616 process = process_kml;
617 else if (args[i].equals("--csv"))
618 process = process_csv;
619 else if (args[i].equals("--graph"))
620 process = process_graph;
621 else if (args[i].equals("--summary"))
622 process = process_summary;
623 else if (args[i].equals("--cat"))
624 process = process_cat;
625 else if (args[i].startsWith("--"))
628 File file = new File(args[i]);
632 if (!process_graph(file))
636 if (!process_replay(file))
646 case process_summary:
656 if (number_of_windows == 0) {
657 java.util.List<AltosDevice> devices = AltosUSBDevice.list(AltosLib.product_basestation);
659 for (AltosDevice device : devices) {
663 if (number_of_windows == 0)