altosui: Catch I/O errors on telemetry device, report to user
[fw/altos] / ao-tools / altosui / AltosUI.java
index 4f3b5ddeb1c1c2fa68ab8d639135a0c32e18232e..7e3fb7f96b3acdac76f0ea183defb5d74805f698 100644 (file)
@@ -44,6 +44,7 @@ import altosui.AltosChannelMenu;
 import altosui.AltosFlashUI;
 import altosui.AltosLogfileChooser;
 import altosui.AltosCSVUI;
+import altosui.AltosLine;
 
 import libaltosJNI.*;
 
@@ -168,21 +169,19 @@ public class AltosUI extends JFrame {
                        flightInfoModel[i].finish();
        }
 
-       public void show(AltosState state) {
+       public void show(AltosState state, int crc_errors) {
+               if (state == null)
+                       return;
                flightStatusModel.set(state);
 
                info_reset();
-               if (state.gps_ready)
-                       info_add_row(0, "Ground state", "%s", "ready");
-               else
-                       info_add_row(0, "Ground state", "wait (%d)",
-                                    state.gps_waiting);
                info_add_row(0, "Rocket state", "%s", state.data.state());
                info_add_row(0, "Callsign", "%s", state.data.callsign);
                info_add_row(0, "Rocket serial", "%6d", state.data.serial);
                info_add_row(0, "Rocket flight", "%6d", state.data.flight);
 
                info_add_row(0, "RSSI", "%6d    dBm", state.data.rssi);
+               info_add_row(0, "CRC Errors", "%6d", crc_errors);
                info_add_row(0, "Height", "%6.0f    m", state.height);
                info_add_row(0, "Max height", "%6.0f    m", state.max_height);
                info_add_row(0, "Acceleration", "%8.1f  m/s²", state.acceleration);
@@ -197,6 +196,11 @@ public class AltosUI extends JFrame {
                if (state.gps == null) {
                        info_add_row(1, "GPS", "not available");
                } else {
+                       if (state.gps_ready)
+                               info_add_row(1, "GPS state", "%s", "ready");
+                       else
+                               info_add_row(1, "GPS state", "wait (%d)",
+                                            state.gps_waiting);
                        if (state.data.gps.locked)
                                info_add_row(1, "GPS", "   locked");
                        else if (state.data.gps.connected)
@@ -264,7 +268,7 @@ public class AltosUI extends JFrame {
                private AltosState state;
                int     reported_landing;
 
-               public void report(boolean last) {
+               public synchronized void report(boolean last) {
                        if (state == null)
                                return;
 
@@ -278,7 +282,16 @@ public class AltosUI extends JFrame {
                        }
 
                        /* If the rocket isn't on the pad, then report height */
-                       if (state.state > Altos.ao_flight_pad) {
+                       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;
@@ -288,7 +301,7 @@ public class AltosUI extends JFrame {
                         * either we've got a landed report or we haven't heard from it in
                         * a long time
                         */
-                       if (!state.ascent &&
+                       if (state.state >= Altos.ao_flight_drogue &&
                            (last ||
                             System.currentTimeMillis() - state.report_time >= 15000 ||
                             state.state == Altos.ao_flight_landed))
@@ -298,7 +311,7 @@ public class AltosUI extends JFrame {
                                else
                                        voice.speak("rocket may have crashed");
                                if (state.from_pad != null)
-                                       voice.speak("bearing %d degrees, range %d meters",
+                                       voice.speak("Bearing %d degrees, range %d meters.",
                                                    (int) (state.from_pad.bearing + 0.5),
                                                    (int) (state.from_pad.distance + 0.5));
                                ++reported_landing;
@@ -311,7 +324,7 @@ public class AltosUI extends JFrame {
                        state = null;
                        try {
                                for (;;) {
-                                       Thread.sleep(10000);
+                                       Thread.sleep(20000);
                                        report(false);
                                }
                        } catch (InterruptedException ie) {
@@ -319,7 +332,10 @@ public class AltosUI extends JFrame {
                }
 
                public void notice(AltosState new_state) {
+                       AltosState old_state = state;
                        state = new_state;
+                       if (old_state != null && old_state.state != state.state)
+                               report(false);
                }
        }
 
@@ -350,7 +366,11 @@ public class AltosUI extends JFrame {
 
                String          name;
 
-               AltosRecord read() throws InterruptedException, ParseException { return null; }
+               int             crc_errors;
+
+               void init() { }
+
+               AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
 
                void close() { }
 
@@ -375,14 +395,22 @@ public class AltosUI extends JFrame {
                                                old_state = state;
                                                state = new AltosState(record, state);
                                                update(state);
-                                               show(state);
+                                               show(state, crc_errors);
                                                tell(state, old_state);
                                                idle_thread.notice(state);
                                        } 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) {
+                       } catch (IOException ie) {
+                               JOptionPane.showMessageDialog(AltosUI.this,
+                                                             String.format("Error reading from \"%s\"", name),
+                                                             "Telemetry Read Error",
+                                                             JOptionPane.ERROR_MESSAGE);
                        } finally {
                                close();
                                idle_thread.interrupt();
@@ -397,10 +425,13 @@ public class AltosUI extends JFrame {
 
        class DeviceThread extends DisplayThread {
                AltosSerial     serial;
-               LinkedBlockingQueue<String> telem;
+               LinkedBlockingQueue<AltosLine> telem;
 
-               AltosRecord read() throws InterruptedException, ParseException {
-                       return new AltosTelemetry(telem.take());
+               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() {
@@ -408,11 +439,11 @@ public class AltosUI extends JFrame {
                        serial.remove_monitor(telem);
                }
 
-               public DeviceThread(AltosSerial s) {
+               public DeviceThread(AltosSerial s, String in_name) {
                        serial = s;
-                       telem = new LinkedBlockingQueue<String>();
+                       telem = new LinkedBlockingQueue<AltosLine>();
                        serial.add_monitor(telem);
-                       name = "telemetry";
+                       name = in_name;
                }
        }
 
@@ -421,8 +452,9 @@ public class AltosUI extends JFrame {
 
                if (device != null) {
                        try {
+                               stop_display();
                                serial_line.open(device);
-                               DeviceThread thread = new DeviceThread(serial_line);
+                               DeviceThread thread = new DeviceThread(serial_line, device.getPath());
                                serial_line.set_channel(AltosPreferences.channel());
                                serial_line.set_callsign(AltosPreferences.callsign());
                                run_display(thread);
@@ -516,8 +548,12 @@ public class AltosUI extends JFrame {
        Thread          display_thread;
 
        private void stop_display() {
-               if (display_thread != null && display_thread.isAlive())
+               if (display_thread != null && display_thread.isAlive()) {
                        display_thread.interrupt();
+                       try {
+                               display_thread.join();
+                       } catch (InterruptedException ie) {}
+               }
                display_thread = null;
        }