Rename icon files to fit XDG specifications. Add file icons. Add mime types
[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_5.*;
25 import org.altusmetrum.altosuilib_3.*;
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                         }
94                 }
95
96                 long now () {
97                         return System.currentTimeMillis();
98                 }
99
100                 void set_report_time() {
101                         report_time = now() + report_interval;
102                 }
103
104                 public void run () {
105                         try {
106                                 for (;;) {
107                                         if (reader.has_monitor_battery()) {
108                                                 listener_state.battery = reader.monitor_battery();
109                                                 show_safely();
110                                         }
111                                         set_report_time();
112                                         for (;;) {
113                                                 voice.drain();
114                                                 synchronized (this) {
115                                                         long    sleep_time = report_time - now();
116                                                         if (sleep_time <= 0)
117                                                                 break;
118                                                         wait(sleep_time);
119                                                 }
120                                         }
121
122                                         report(false);
123                                 }
124                         } catch (InterruptedException ie) {
125                                 try {
126                                         voice.drain();
127                                 } catch (InterruptedException iie) { }
128                         }
129                 }
130
131                 public synchronized void notice(boolean spoken) {
132                         if (old_state != null && old_state.state != state.state) {
133                                 report_time = now();
134                                 this.notify();
135                         } else if (spoken)
136                                 set_report_time();
137                 }
138
139                 public IdleThread() {
140                         report_interval = 10000;
141                 }
142         }
143
144         synchronized boolean tell() {
145                 boolean ret = false;
146                 if (old_state == null || old_state.gps_ready != state.gps_ready) {
147                         if (state.gps_ready) {
148                                 voice.speak("GPS ready");
149                                 ret = true;
150                         }
151                         else if (old_state != null) {
152                                 voice.speak("GPS lost");
153                                 ret = true;
154                         }
155                 }
156                 old_state = state;
157                 return ret;
158         }
159
160         public void run() {
161                 boolean         interrupted = false;
162                 boolean         told;
163
164                 idle_thread = new IdleThread();
165                 idle_thread.start();
166
167                 try {
168                         for (;;) {
169                                 try {
170                                         state = reader.read();
171                                         if (state == null)
172                                                 break;
173                                         reader.update(state);
174                                         show_safely();
175                                         told = tell();
176                                         idle_thread.notice(told);
177                                 } catch (ParseException pp) {
178                                         System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
179                                 } catch (AltosCRCException ce) {
180                                         ++listener_state.crc_errors;
181                                         show_safely();
182                                 }
183                         }
184                 } catch (InterruptedException ee) {
185                         interrupted = true;
186                 } catch (IOException ie) {
187                         reading_error_safely();
188                 } finally {
189                         if (!interrupted)
190                                 idle_thread.report(true);
191                         reader.close(interrupted);
192                         idle_thread.interrupt();
193                         try {
194                                 idle_thread.join();
195                         } catch (InterruptedException ie) {}
196                 }
197         }
198
199         public TeleGPSDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) {
200                 listener_state = new AltosListenerState();
201                 parent = in_parent;
202                 voice = in_voice;
203                 display = in_display;
204                 reader = in_reader;
205                 display.reset();
206         }
207 }