X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=ao-tools%2Faltosui%2FAltosUI.java;h=2861444da7b5e9008e875d3285d7f225226ea18a;hp=1adeeccfee47ae35ce9c2256ba49654356adc427;hb=1f3e091efdfb2fe6f06a066cac60f5d267b94856;hpb=34ca8c00f4be72c314baff4c96f1e2f010948454 diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 1adeeccf..2861444d 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -39,29 +39,17 @@ import altosui.AltosPreferences; import altosui.AltosLog; import altosui.AltosVoice; import altosui.AltosFlightInfoTableModel; -import altosui.AltosChannelMenu; import altosui.AltosFlashUI; import altosui.AltosLogfileChooser; import altosui.AltosCSVUI; import altosui.AltosLine; import altosui.AltosStatusTable; import altosui.AltosInfoTable; +import altosui.AltosDisplayThread; import libaltosJNI.*; public class AltosUI extends JFrame { - private int channel = -1; - - private AltosStatusTable flightStatus; - private AltosInfoTable flightInfo; - private AltosSerial serial_line; - private AltosLog altos_log; - private Box vbox; - - private Font statusFont = new Font("SansSerif", Font.BOLD, 24); - private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); - private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); - public AltosVoice voice = new AltosVoice(); public static boolean load_library(Frame frame) { @@ -76,39 +64,117 @@ public class AltosUI extends JFrame { return true; } + void telemetry_window(AltosDevice device) { + try { + AltosFlightReader reader = new AltosTelemetryReader(device); + if (reader != null) + new AltosFlightUI(voice, reader, device.getSerial()); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Cannot open device \"%s\"", + device.getPath()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + device.getPath(), + "Unkonwn I/O error", + JOptionPane.ERROR_MESSAGE); + } + } + + Container pane; + GridBagLayout gridbag; + + JButton addButton(int x, int y, String label) { + GridBagConstraints c; + JButton b; + + c = new GridBagConstraints(); + c.gridx = x; c.gridy = y; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + b = new JButton(label); + + Dimension ps = b.getPreferredSize(); + + gridbag.setConstraints(b, c); + add(b, c); + return b; + } + public AltosUI() { load_library(null); - String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; - Object[][] statusData = { { "0", "pad", "-50", "0" } }; - java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); if (imgURL != null) setIconImage(new ImageIcon(imgURL).getImage()); AltosPreferences.init(this); - vbox = Box.createVerticalBox(); - this.add(vbox); - - flightStatus = new AltosStatusTable(this); + pane = getContentPane(); + gridbag = new GridBagLayout(); + pane.setLayout(gridbag); - vbox.add(flightStatus); + JButton b; - flightInfo = new AltosInfoTable(); - vbox.add(flightInfo.box()); + b = addButton(0, 0, "Monitor Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConnectToDevice(); + } + }); + b = addButton(1, 0, "Save Flight Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SaveFlightData(); + } + }); + b = addButton(2, 0, "Replay Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Replay(); + } + }); + b = addButton(0, 1, "Graph Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + GraphData(); + } + }); + b = addButton(1, 1, "Export Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ExportData(); + } + }); + b = addButton(2, 1, "Configure TeleMetrum"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureTeleMetrum(); + } + }); setTitle("AltOS"); createMenu(); - serial_line = new AltosSerial(); - altos_log = new AltosLog(serial_line); - int dpi = Toolkit.getDefaultToolkit().getScreenResolution(); - this.setSize(new Dimension (flightInfo.width(), - flightStatus.height() + flightInfo.height())); - this.validate(); + pane.doLayout(); + pane.validate(); + + doLayout(); + validate(); + + setVisible(true); + + Insets i = getInsets(); + Dimension ps = rootPane.getPreferredSize(); + ps.width += i.left + i.right; + ps.height += i.top + i.bottom; + setPreferredSize(ps); + setSize(ps); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override @@ -116,277 +182,14 @@ public class AltosUI extends JFrame { System.exit(0); } }); - voice.speak("Rocket flight monitor ready."); - } - - void show(AltosState state, int crc_errors) { - if (state != null) { - flightStatus.set(state); - flightInfo.show(state, crc_errors); - } - } - - class IdleThread extends Thread { - - boolean started; - private AltosState state; - int reported_landing; - int report_interval; - long report_time; - - public synchronized void report(boolean last) { - if (state == null) - return; - - /* reset the landing count once we hear about a new flight */ - if (state.state < Altos.ao_flight_drogue) - reported_landing = 0; - - /* Shut up once the rocket is on the ground */ - if (reported_landing > 2) { - return; - } - - /* If the rocket isn't on the pad, then report height */ - if (Altos.ao_flight_drogue <= state.state && - state.state < Altos.ao_flight_landed && - state.range >= 0) - { - voice.speak("Height %d, bearing %d, elevation %d, range %d.\n", - (int) (state.height + 0.5), - (int) (state.from_pad.bearing + 0.5), - (int) (state.elevation + 0.5), - (int) (state.range + 0.5)); - } else if (state.state > Altos.ao_flight_pad) { - voice.speak("%d meters", (int) (state.height + 0.5)); - } else { - reported_landing = 0; - } - - /* If the rocket is coming down, check to see if it has landed; - * either we've got a landed report or we haven't heard from it in - * a long time - */ - if (state.state >= Altos.ao_flight_drogue && - (last || - System.currentTimeMillis() - state.report_time >= 15000 || - state.state == Altos.ao_flight_landed)) - { - if (Math.abs(state.baro_speed) < 20 && state.height < 100) - voice.speak("rocket landed safely"); - else - voice.speak("rocket may have crashed"); - if (state.from_pad != null) - voice.speak("Bearing %d degrees, range %d meters.", - (int) (state.from_pad.bearing + 0.5), - (int) (state.from_pad.distance + 0.5)); - ++reported_landing; - } - } - - long now () { - return System.currentTimeMillis(); - } - - void set_report_time() { - report_time = now() + report_interval; - } - - public void run () { - - reported_landing = 0; - state = null; - report_interval = 10000; - try { - for (;;) { - set_report_time(); - for (;;) { - voice.drain(); - synchronized (this) { - long sleep_time = report_time - now(); - if (sleep_time <= 0) - break; - wait(sleep_time); - } - } - report(false); - } - } catch (InterruptedException ie) { - try { - voice.drain(); - } catch (InterruptedException iie) { } - } - } - - public synchronized void notice(AltosState new_state, boolean spoken) { - AltosState old_state = state; - state = new_state; - if (!started && state.state > Altos.ao_flight_pad) { - started = true; - start(); - } - - if (state.state < Altos.ao_flight_drogue) - report_interval = 10000; - else - report_interval = 20000; - if (old_state != null && old_state.state != state.state) { - report_time = now(); - this.notify(); - } else if (spoken) - set_report_time(); - } - } - - private boolean tell(AltosState state, AltosState old_state) { - boolean ret = false; - if (old_state == null || old_state.state != state.state) { - voice.speak(state.data.state()); - if ((old_state == null || old_state.state <= Altos.ao_flight_boost) && - state.state > Altos.ao_flight_boost) { - voice.speak("max speed: %d meters per second.", - (int) (state.max_speed + 0.5)); - ret = true; - } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) && - state.state >= Altos.ao_flight_drogue) { - voice.speak("max height: %d meters.", - (int) (state.max_height + 0.5)); - ret = true; - } - } - if (old_state == null || old_state.gps_ready != state.gps_ready) { - if (state.gps_ready) { - voice.speak("GPS ready"); - ret = true; - } - else if (old_state != null) { - voice.speak("GPS lost"); - ret = true; - } - } - old_state = state; - return ret; - } - - class DisplayThread extends Thread { - IdleThread idle_thread; - - String name; - - int crc_errors; - - void init() { } - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } - - void close(boolean interrupted) { } - - void update(AltosState state) throws InterruptedException { } - - public void run() { - boolean interrupted = false; - String line; - AltosState state = null; - AltosState old_state = null; - boolean told; - - idle_thread = new IdleThread(); - - flightInfo.clear(); - try { - for (;;) { - try { - AltosRecord record = read(); - if (record == null) - break; - old_state = state; - state = new AltosState(record, state); - update(state); - show(state, crc_errors); - told = tell(state, old_state); - idle_thread.notice(state, told); - } catch (ParseException pp) { - System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage()); - } catch (AltosCRCException ce) { - ++crc_errors; - show(state, crc_errors); - } - } - } catch (InterruptedException ee) { - interrupted = true; - } catch (IOException ie) { - JOptionPane.showMessageDialog(AltosUI.this, - String.format("Error reading from \"%s\"", name), - "Telemetry Read Error", - JOptionPane.ERROR_MESSAGE); - } finally { - close(interrupted); - idle_thread.interrupt(); - try { - idle_thread.join(); - } catch (InterruptedException ie) {} - } - } - - public void report() { - if (idle_thread != null) - idle_thread.report(true); - } - } - - class DeviceThread extends DisplayThread { - AltosSerial serial; - LinkedBlockingQueue telem; - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { - AltosLine l = telem.take(); - if (l.line == null) - throw new IOException("IO error"); - return new AltosTelemetry(l.line); - } - - void close(boolean interrupted) { - serial.close(); - serial.remove_monitor(telem); - } - - public DeviceThread(AltosSerial s, String in_name) { - serial = s; - telem = new LinkedBlockingQueue(); - serial.add_monitor(telem); - name = in_name; - } } private void ConnectToDevice() { AltosDevice device = AltosDeviceDialog.show(AltosUI.this, AltosDevice.product_basestation); - if (device != null) { - try { - stop_display(); - serial_line.open(device); - DeviceThread thread = new DeviceThread(serial_line, device.getPath()); - serial_line.set_channel(AltosPreferences.channel()); - serial_line.set_callsign(AltosPreferences.callsign()); - run_display(thread); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - String.format("Cannot open device \"%s\"", - device.getPath()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - device.getPath(), - "Unkonwn I/O error", - JOptionPane.ERROR_MESSAGE); - } - } - } - - void DisconnectFromDevice () { - stop_display(); + if (device != null) + telemetry_window(device); } void ConfigureCallsign() { @@ -394,11 +197,8 @@ public class AltosUI extends JFrame { result = JOptionPane.showInputDialog(AltosUI.this, "Configure Callsign", AltosPreferences.callsign()); - if (result != null) { + if (result != null) AltosPreferences.set_callsign(result); - if (serial_line != null) - serial_line.set_callsign(result); - } } void ConfigureTeleMetrum() { @@ -409,70 +209,18 @@ public class AltosUI extends JFrame { new AltosFlashUI(AltosUI.this); } - /* - * Open an existing telemetry file and replay it in realtime - */ - - class ReplayThread extends DisplayThread { - AltosReader reader; - String name; - - public AltosRecord read() { - try { - return reader.read(); - } catch (IOException ie) { - JOptionPane.showMessageDialog(AltosUI.this, - name, - "error reading", - JOptionPane.ERROR_MESSAGE); - } catch (ParseException pe) { - } - return null; - } - - public void close (boolean interrupted) { - if (!interrupted) - report(); - } - - public ReplayThread(AltosReader in_reader, String in_name) { - reader = in_reader; - } - void update(AltosState state) throws InterruptedException { - /* Make it run in realtime after the rocket leaves the pad */ - if (state.state > Altos.ao_flight_pad) - Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); - } - } - - Thread display_thread; - - private void stop_display() { - if (display_thread != null && display_thread.isAlive()) { - display_thread.interrupt(); - try { - display_thread.join(); - } catch (InterruptedException ie) {} - } - display_thread = null; - } - - private void run_display(Thread thread) { - stop_display(); - display_thread = thread; - display_thread.start(); - } - /* * Replay a flight from telemetry data */ private void Replay() { AltosLogfileChooser chooser = new AltosLogfileChooser( AltosUI.this); - AltosReader reader = chooser.runDialog(); - if (reader != null) - run_display(new ReplayThread(reader, - chooser.filename())); + AltosRecordIterable iterable = chooser.runDialog(); + if (iterable != null) { + AltosFlightReader reader = new AltosReplayReader(iterable.iterator(), + chooser.filename()); + new AltosFlightUI(voice, reader); + } } /* Connect to TeleMetrum, either directly or through @@ -490,6 +238,13 @@ public class AltosUI extends JFrame { new AltosCSVUI(AltosUI.this); } + /* Load a flight log CSV file and display a pretty graph. + */ + + private void GraphData() { + new AltosGraphUI(AltosUI.this); + } + /* Create the AltosUI menus */ private void createMenu() { @@ -504,34 +259,26 @@ public class AltosUI extends JFrame { menu.setMnemonic(KeyEvent.VK_F); menubar.add(menu); - item = new JMenuItem("Replay File",KeyEvent.VK_R); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Replay(); - } - }); - menu.add(item); - - item = new JMenuItem("Save Flight Data",KeyEvent.VK_S); + item = new JMenuItem("Flash Image",KeyEvent.VK_I); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - SaveFlightData(); + FlashImage(); } }); menu.add(item); - item = new JMenuItem("Flash Image",KeyEvent.VK_F); + item = new JMenuItem("Export Data",KeyEvent.VK_E); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - FlashImage(); + ExportData(); } }); menu.add(item); - item = new JMenuItem("Export Data",KeyEvent.VK_F); + item = new JMenuItem("Graph Data",KeyEvent.VK_G); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - ExportData(); + GraphData(); } }); menu.add(item); @@ -541,6 +288,7 @@ public class AltosUI extends JFrame { ActionEvent.CTRL_MASK)); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + System.out.printf("exiting\n"); System.exit(0); } }); @@ -548,7 +296,7 @@ public class AltosUI extends JFrame { } // Device menu - { + if (false) { menu = new JMenu("Device"); menu.setMnemonic(KeyEvent.VK_D); menubar.add(menu); @@ -561,14 +309,6 @@ public class AltosUI extends JFrame { }); menu.add(item); - item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - DisconnectFromDevice(); - } - }); - menu.add(item); - menu.addSeparator(); item = new JMenuItem("Set Callsign",KeyEvent.VK_S); @@ -637,49 +377,26 @@ public class AltosUI extends JFrame { }); menu.add(item); } - - // Channel menu - { - menu = new AltosChannelMenu(AltosPreferences.channel()); - menu.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int new_channel = Integer.parseInt(e.getActionCommand()); - AltosPreferences.set_channel(new_channel); - serial_line.set_channel(new_channel); - } - }); - menu.setMnemonic(KeyEvent.VK_C); - menubar.add(menu); - } - this.setJMenuBar(menubar); - } - static String replace_extension(String input, String extension) { - int dot = input.lastIndexOf("."); - if (dot > 0) - input = input.substring(0,dot); - return input.concat(extension); - } - - static AltosReader open_logfile(String filename) { + static AltosRecordIterable open_logfile(String filename) { File file = new File (filename); try { FileInputStream in; in = new FileInputStream(file); if (filename.endsWith("eeprom")) - return new AltosEepromReader(in); + return new AltosEepromIterable(in); else - return new AltosTelemetryReader(in); + return new AltosTelemetryIterable(in); } catch (FileNotFoundException fe) { System.out.printf("Cannot open '%s'\n", filename); return null; } } - static AltosCSV open_csv(String filename) { + static AltosWriter open_csv(String filename) { File file = new File (filename); try { return new AltosCSV(file); @@ -689,33 +406,91 @@ public class AltosUI extends JFrame { } } - static void process_file(String input) { - String output = replace_extension(input,".csv"); - if (input.equals(output)) { - System.out.printf("Not processing '%s'\n", input); - return; + static AltosWriter open_kml(String filename) { + File file = new File (filename); + try { + return new AltosKML(file); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; } - System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); - AltosReader reader = open_logfile(input); - if (reader == null) - return; - AltosCSV writer = open_csv(output); - if (writer == null) + } + + static final int process_csv = 1; + static final int process_kml = 2; + + static void process_file(String input, int process) { + AltosRecordIterable iterable = open_logfile(input); + if (iterable == null) return; - writer.write(reader); - reader.close(); - writer.close(); + if (process == 0) + process = process_csv; + if ((process & process_csv) != 0) { + String output = Altos.replace_extension(input,".csv"); + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + } else { + AltosWriter writer = open_csv(output); + if (writer != null) { + writer.write(iterable); + writer.close(); + } + } + } + if ((process & process_kml) != 0) { + String output = Altos.replace_extension(input,".kml"); + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + } else { + AltosWriter writer = open_kml(output); + if (writer == null) + return; + writer.write(iterable); + writer.close(); + } + } } public static void main(final String[] args) { - + int process = 0; /* Handle batch-mode */ - if (args.length > 0) { - for (int i = 0; i < args.length; i++) - process_file(args[i]); + if (args.length == 2 && args[0].equals("--replay")) { + String filename = args[1]; + FileInputStream in; + try { + in = new FileInputStream(filename); + } catch (Exception e) { + System.out.printf("Failed to open file '%s'\n", filename); + return; + } + AltosRecordIterable recs; + AltosReplayReader reader; + if (filename.endsWith("eeprom")) { + recs = new AltosEepromIterable(in); + } else { + recs = new AltosTelemetryIterable(in); + } + reader = new AltosReplayReader(recs.iterator(), filename); + new AltosFlightUI(new AltosVoice(), reader); + return; + } else if (args.length > 0) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals("--kml")) + process |= process_kml; + else if (args[i].equals("--csv")) + process |= process_csv; + else + process_file(args[i], process); + } } else { AltosUI altosui = new AltosUI(); altosui.setVisible(true); + + AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation); + for (int i = 0; i < devices.length; i++) + altosui.telemetry_window(devices[i]); } } -} \ No newline at end of file +}