telegps: Show flight number in monitor window
[fw/altos] / telegps / TeleGPSDisplayThread.java
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
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.
7  *
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.
12  *
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.
16  */
17
18 package org.altusmetrum.telegps;
19
20 import java.awt.*;
21 import javax.swing.*;
22 import java.io.*;
23 import java.text.*;
24 import org.altusmetrum.altoslib_4.*;
25 import org.altusmetrum.altosuilib_2.*;
26
27 public class TeleGPSDisplayThread extends Thread {
28
29         Frame                   parent;
30         IdleThread              idle_thread;
31         AltosVoice              voice;
32         AltosFlightReader       reader;
33         AltosState              old_state, state;
34         AltosListenerState      listener_state;
35         AltosFlightDisplay      display;
36
37         synchronized void show_safely() {
38                 final AltosState my_state = state;
39                 final AltosListenerState my_listener_state = listener_state;
40                 Runnable r = new Runnable() {
41                                 public void run() {
42                                         try {
43                                                 display.show(my_state, my_listener_state);
44                                         } catch (Exception ex) {
45                                         }
46                                 }
47                         };
48                 SwingUtilities.invokeLater(r);
49         }
50
51         void reading_error_internal() {
52                 JOptionPane.showMessageDialog(parent,
53                                               String.format("Error reading from \"%s\"", reader.name),
54                                               "Telemetry Read Error",
55                                               JOptionPane.ERROR_MESSAGE);
56         }
57
58         void reading_error_safely() {
59                 Runnable r = new Runnable() {
60                                 public void run() {
61                                         try {
62                                                 reading_error_internal();
63                                         } catch (Exception ex) {
64                                         }
65                                 }
66                         };
67                 SwingUtilities.invokeLater(r);
68         }
69
70         class IdleThread extends Thread {
71
72                 boolean started;
73                 int     report_interval;
74                 long    report_time;
75
76                 public synchronized void report(boolean last) {
77                         if (state == null)
78                                 return;
79
80                         if (state.height() != AltosLib.MISSING) {
81                                 if (state.from_pad != null) {
82                                         voice.speak("Height %s, bearing %s %d, elevation %d, range %s, .\n",
83                                                     AltosConvert.height.say(state.gps_height()),
84                                                     state.from_pad.bearing_words(
85                                                             AltosGreatCircle.BEARING_VOICE),
86                                                     (int) (state.from_pad.bearing + 0.5),
87                                                     (int) (state.elevation + 0.5),
88                                                     AltosConvert.distance.say(state.range));
89                                 } else {
90                                         voice.speak("Height %s.\n",
91                                                     AltosConvert.height.say(state.height()));
92                                 }
93                         } else {
94                                 voice.speak("Height is unknown.\n");
95                         }
96                 }
97
98                 long now () {
99                         return System.currentTimeMillis();
100                 }
101
102                 void set_report_time() {
103                         report_time = now() + report_interval;
104                 }
105
106                 public void run () {
107                         try {
108                                 for (;;) {
109                                         if (reader.has_monitor_battery()) {
110                                                 listener_state.battery = reader.monitor_battery();
111                                                 show_safely();
112                                         }
113                                         set_report_time();
114                                         for (;;) {
115                                                 voice.drain();
116                                                 synchronized (this) {
117                                                         long    sleep_time = report_time - now();
118                                                         if (sleep_time <= 0)
119                                                                 break;
120                                                         wait(sleep_time);
121                                                 }
122                                         }
123
124                                         report(false);
125                                 }
126                         } catch (InterruptedException ie) {
127                                 try {
128                                         voice.drain();
129                                 } catch (InterruptedException iie) { }
130                         }
131                 }
132
133                 public synchronized void notice(boolean spoken) {
134                         if (old_state != null && old_state.state != state.state) {
135                                 report_time = now();
136                                 this.notify();
137                         } else if (spoken)
138                                 set_report_time();
139                 }
140
141                 public IdleThread() {
142                         report_interval = 10000;
143                 }
144         }
145
146         synchronized boolean tell() {
147                 boolean ret = false;
148                 if (old_state == null || old_state.gps_ready != state.gps_ready) {
149                         if (state.gps_ready) {
150                                 voice.speak("GPS ready");
151                                 ret = true;
152                         }
153                         else if (old_state != null) {
154                                 voice.speak("GPS lost");
155                                 ret = true;
156                         }
157                 }
158                 old_state = state;
159                 return ret;
160         }
161
162         public void run() {
163                 boolean         interrupted = false;
164                 boolean         told;
165
166                 idle_thread = new IdleThread();
167                 idle_thread.start();
168
169                 try {
170                         for (;;) {
171                                 try {
172                                         state = reader.read();
173                                         if (state == null)
174                                                 break;
175                                         reader.update(state);
176                                         show_safely();
177                                         told = tell();
178                                         idle_thread.notice(told);
179                                 } catch (ParseException pp) {
180                                         System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
181                                 } catch (AltosCRCException ce) {
182                                         ++listener_state.crc_errors;
183                                         show_safely();
184                                 }
185                         }
186                 } catch (InterruptedException ee) {
187                         interrupted = true;
188                 } catch (IOException ie) {
189                         reading_error_safely();
190                 } finally {
191                         if (!interrupted)
192                                 idle_thread.report(true);
193                         reader.close(interrupted);
194                         idle_thread.interrupt();
195                         try {
196                                 idle_thread.join();
197                         } catch (InterruptedException ie) {}
198                 }
199         }
200
201         public TeleGPSDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) {
202                 listener_state = new AltosListenerState();
203                 parent = in_parent;
204                 voice = in_voice;
205                 display = in_display;
206                 reader = in_reader;
207                 display.reset();
208         }
209 }