2 * Copyright © 2010 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.
21 import java.awt.event.*;
23 import javax.swing.filechooser.FileNameExtensionFilter;
24 import javax.swing.table.*;
28 import java.util.prefs.*;
29 import java.util.concurrent.LinkedBlockingQueue;
32 import altosui.AltosSerial;
33 import altosui.AltosSerialMonitor;
34 import altosui.AltosRecord;
35 import altosui.AltosTelemetry;
36 import altosui.AltosState;
37 import altosui.AltosDeviceDialog;
38 import altosui.AltosPreferences;
39 import altosui.AltosLog;
40 import altosui.AltosVoice;
41 import altosui.AltosFlightInfoTableModel;
42 import altosui.AltosChannelMenu;
43 import altosui.AltosFlashUI;
44 import altosui.AltosLogfileChooser;
45 import altosui.AltosCSVUI;
46 import altosui.AltosLine;
47 import altosui.AltosStatusTable;
48 import altosui.AltosInfoTable;
49 import altosui.AltosDisplayThread;
53 public class AltosUI extends JFrame {
54 private int channel = -1;
56 private AltosStatusTable flightStatus;
57 private AltosInfoTable flightInfo;
58 private AltosSerial serial_line;
59 private AltosLog altos_log;
62 private Font statusFont = new Font("SansSerif", Font.BOLD, 24);
63 private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14);
64 private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14);
66 public AltosVoice voice = new AltosVoice();
68 public static boolean load_library(Frame frame) {
69 if (!AltosDevice.load_library()) {
70 JOptionPane.showMessageDialog(frame,
71 String.format("No AltOS library in \"%s\"",
72 System.getProperty("java.library.path","<undefined>")),
73 "Cannot load device access library",
74 JOptionPane.ERROR_MESSAGE);
84 String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" };
85 Object[][] statusData = { { "0", "pad", "-50", "0" } };
87 java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg");
89 setIconImage(new ImageIcon(imgURL).getImage());
91 AltosPreferences.init(this);
93 vbox = Box.createVerticalBox();
96 flightStatus = new AltosStatusTable(this);
98 vbox.add(flightStatus);
100 flightInfo = new AltosInfoTable();
101 vbox.add(flightInfo.box());
107 serial_line = new AltosSerial();
108 altos_log = new AltosLog(serial_line);
109 int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
110 this.setSize(new Dimension (flightInfo.width(),
111 flightStatus.height() + flightInfo.height()));
113 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
114 addWindowListener(new WindowAdapter() {
116 public void windowClosing(WindowEvent e) {
120 voice.speak("Rocket flight monitor ready.");
123 class DeviceThread extends AltosDisplayThread {
125 LinkedBlockingQueue<AltosLine> telem;
127 AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException {
128 AltosLine l = telem.take();
130 throw new IOException("IO error");
131 return new AltosTelemetry(l.line);
134 void close(boolean interrupted) {
136 serial.remove_monitor(telem);
139 public DeviceThread(AltosSerial s, String in_name, AltosVoice voice, AltosStatusTable status, AltosInfoTable info) {
140 super(AltosUI.this, voice, status, info);
142 telem = new LinkedBlockingQueue<AltosLine>();
143 serial.add_monitor(telem);
148 private void ConnectToDevice() {
149 AltosDevice device = AltosDeviceDialog.show(AltosUI.this,
150 AltosDevice.product_basestation);
152 if (device != null) {
155 serial_line.open(device);
156 DeviceThread thread = new DeviceThread(serial_line, device.getPath(), voice, flightStatus, flightInfo);
157 serial_line.set_channel(AltosPreferences.channel());
158 serial_line.set_callsign(AltosPreferences.callsign());
160 } catch (FileNotFoundException ee) {
161 JOptionPane.showMessageDialog(AltosUI.this,
162 String.format("Cannot open device \"%s\"",
164 "Cannot open target device",
165 JOptionPane.ERROR_MESSAGE);
166 } catch (IOException ee) {
167 JOptionPane.showMessageDialog(AltosUI.this,
170 JOptionPane.ERROR_MESSAGE);
175 void DisconnectFromDevice () {
179 void ConfigureCallsign() {
181 result = JOptionPane.showInputDialog(AltosUI.this,
182 "Configure Callsign",
183 AltosPreferences.callsign());
184 if (result != null) {
185 AltosPreferences.set_callsign(result);
186 if (serial_line != null)
187 serial_line.set_callsign(result);
191 void ConfigureTeleMetrum() {
192 new AltosConfig(AltosUI.this);
196 new AltosFlashUI(AltosUI.this);
200 Thread display_thread;
202 private void stop_display() {
203 if (display_thread != null && display_thread.isAlive()) {
204 display_thread.interrupt();
206 display_thread.join();
207 } catch (InterruptedException ie) {}
209 display_thread = null;
212 private void run_display(Thread thread) {
214 display_thread = thread;
215 display_thread.start();
219 * Replay a flight from telemetry data
221 private void Replay() {
222 AltosLogfileChooser chooser = new AltosLogfileChooser(
224 AltosRecordIterable iterable = chooser.runDialog();
225 if (iterable != null)
226 run_display(new AltosReplayThread(this, iterable.iterator(),
233 /* Connect to TeleMetrum, either directly or through
234 * a TeleDongle over the packet link
236 private void SaveFlightData() {
237 new AltosEepromDownload(AltosUI.this);
240 /* Load a flight log file and write out a CSV file containing
241 * all of the data in standard units
244 private void ExportData() {
245 new AltosCSVUI(AltosUI.this);
248 /* Create the AltosUI menus
250 private void createMenu() {
251 JMenuBar menubar = new JMenuBar();
254 JRadioButtonMenuItem radioitem;
258 menu = new JMenu("File");
259 menu.setMnemonic(KeyEvent.VK_F);
262 item = new JMenuItem("Replay File",KeyEvent.VK_R);
263 item.addActionListener(new ActionListener() {
264 public void actionPerformed(ActionEvent e) {
270 item = new JMenuItem("Save Flight Data",KeyEvent.VK_S);
271 item.addActionListener(new ActionListener() {
272 public void actionPerformed(ActionEvent e) {
278 item = new JMenuItem("Flash Image",KeyEvent.VK_F);
279 item.addActionListener(new ActionListener() {
280 public void actionPerformed(ActionEvent e) {
286 item = new JMenuItem("Export Data",KeyEvent.VK_F);
287 item.addActionListener(new ActionListener() {
288 public void actionPerformed(ActionEvent e) {
294 item = new JMenuItem("Quit",KeyEvent.VK_Q);
295 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
296 ActionEvent.CTRL_MASK));
297 item.addActionListener(new ActionListener() {
298 public void actionPerformed(ActionEvent e) {
307 menu = new JMenu("Device");
308 menu.setMnemonic(KeyEvent.VK_D);
311 item = new JMenuItem("Connect to Device",KeyEvent.VK_C);
312 item.addActionListener(new ActionListener() {
313 public void actionPerformed(ActionEvent e) {
319 item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D);
320 item.addActionListener(new ActionListener() {
321 public void actionPerformed(ActionEvent e) {
322 DisconnectFromDevice();
329 item = new JMenuItem("Set Callsign",KeyEvent.VK_S);
330 item.addActionListener(new ActionListener() {
331 public void actionPerformed(ActionEvent e) {
338 item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T);
339 item.addActionListener(new ActionListener() {
340 public void actionPerformed(ActionEvent e) {
341 ConfigureTeleMetrum();
349 menu = new JMenu("Log");
350 menu.setMnemonic(KeyEvent.VK_L);
353 item = new JMenuItem("New Log",KeyEvent.VK_N);
354 item.addActionListener(new ActionListener() {
355 public void actionPerformed(ActionEvent e) {
360 item = new JMenuItem("Configure Log",KeyEvent.VK_C);
361 item.addActionListener(new ActionListener() {
362 public void actionPerformed(ActionEvent e) {
363 AltosPreferences.ConfigureLog();
370 menu = new JMenu("Voice", true);
371 menu.setMnemonic(KeyEvent.VK_V);
374 radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice());
375 radioitem.addActionListener(new ActionListener() {
376 public void actionPerformed(ActionEvent e) {
377 JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource();
378 boolean enabled = item.isSelected();
379 AltosPreferences.set_voice(enabled);
381 voice.speak_always("Enable voice.");
383 voice.speak_always("Disable voice.");
387 item = new JMenuItem("Test Voice",KeyEvent.VK_T);
388 item.addActionListener(new ActionListener() {
389 public void actionPerformed(ActionEvent e) {
390 voice.speak("That's one small step for man; one giant leap for mankind.");
398 menu = new AltosChannelMenu(AltosPreferences.channel());
399 menu.addActionListener(new ActionListener() {
400 public void actionPerformed(ActionEvent e) {
401 int new_channel = Integer.parseInt(e.getActionCommand());
402 AltosPreferences.set_channel(new_channel);
403 serial_line.set_channel(new_channel);
406 menu.setMnemonic(KeyEvent.VK_C);
410 this.setJMenuBar(menubar);
414 static String replace_extension(String input, String extension) {
415 int dot = input.lastIndexOf(".");
417 input = input.substring(0,dot);
418 return input.concat(extension);
421 static AltosRecordIterable open_logfile(String filename) {
422 File file = new File (filename);
426 in = new FileInputStream(file);
427 if (filename.endsWith("eeprom"))
428 return new AltosEepromIterable(in);
430 return new AltosTelemetryIterable(in);
431 } catch (FileNotFoundException fe) {
432 System.out.printf("Cannot open '%s'\n", filename);
437 static AltosCSV open_csv(String filename) {
438 File file = new File (filename);
440 return new AltosCSV(file);
441 } catch (FileNotFoundException fe) {
442 System.out.printf("Cannot open '%s'\n", filename);
447 static void process_file(String input) {
448 String output = replace_extension(input,".csv");
449 if (input.equals(output)) {
450 System.out.printf("Not processing '%s'\n", input);
453 System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
454 AltosRecordIterable iterable = open_logfile(input);
455 if (iterable == null)
457 AltosCSV writer = open_csv(output);
460 writer.write(iterable);
464 public static void main(final String[] args) {
466 /* Handle batch-mode */
467 if (args.length > 0) {
468 for (int i = 0; i < args.length; i++)
469 process_file(args[i]);
471 AltosUI altosui = new AltosUI();
472 altosui.setVisible(true);