altosui: Abstract remote connection timeout stuff
[fw/altos] / altoslib / AltosLink.java
1 /*
2  * Copyright © 2011 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.AltosLib;
19
20 import java.lang.*;
21 import java.io.*;
22 import java.util.concurrent.*;
23 import java.util.*;
24 import java.text.*;
25
26 public abstract class AltosLink {
27         public abstract void print(String data);
28         public abstract void close();
29
30         public static boolean debug = false;
31         public static void set_debug(boolean in_debug) { debug = in_debug; }
32         LinkedList<String> pending_output = new LinkedList<String>();
33
34         public LinkedList<LinkedBlockingQueue<AltosLine>> monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();;
35         public LinkedBlockingQueue<AltosLine> reply_queue = new LinkedBlockingQueue<AltosLine>();
36
37         public void add_monitor(LinkedBlockingQueue<AltosLine> q) {
38                 set_monitor(true);
39                 monitors.add(q);
40         }
41
42         public void remove_monitor(LinkedBlockingQueue<AltosLine> q) {
43                 monitors.remove(q);
44                 if (monitors.isEmpty())
45                         set_monitor(false);
46         }
47
48         public void printf(String format, Object ... arguments) {
49                 String  line = String.format(format, arguments);
50                 if (debug)
51                         pending_output.add(line);
52                 print(line);
53         }
54
55         public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException {
56                 flush_output();
57                 AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
58                 if (line != null)
59                         return line.line;
60                 return null;
61         }
62
63         public String get_reply() throws InterruptedException {
64                 return get_reply(5000);
65         }
66
67                 
68         public abstract boolean can_cancel_reply();
69         public abstract boolean show_reply_timeout();
70         public abstract void hide_reply_timeout();
71
72         public boolean  reply_abort;
73         public int      in_reply;
74
75         boolean         reply_timeout_shown = false;
76
77         private boolean check_reply_timeout() {
78                 if (!reply_timeout_shown)
79                         reply_timeout_shown = show_reply_timeout();
80                 return reply_abort;
81         }
82
83         private void cleanup_reply_timeout() {
84                 if (reply_timeout_shown) {
85                         reply_timeout_shown = false;
86                         hide_reply_timeout();
87                 }
88         }
89
90
91         public String get_reply(int timeout) throws InterruptedException {
92                 boolean can_cancel = can_cancel_reply();
93                 String  reply = null;
94
95                 if (!can_cancel && remote)
96                         System.out.printf("Uh-oh, reading remote serial device from swing thread\n");
97
98                 if (remote && can_cancel)
99                         timeout = 500;
100                 try {
101                         ++in_reply;
102
103                         flush_output();
104
105                         reply_abort = false;
106                         reply_timeout_shown = false;
107                         for (;;) {
108                                 AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
109                                 if (line != null) {
110                                         cleanup_reply_timeout();
111                                         reply = line.line;
112                                         break;
113                                 }
114                                 if (!remote || !can_cancel || check_reply_timeout()) {
115                                         reply = null;
116                                         break;
117                                 }
118                         }
119                 } finally {
120                         --in_reply;
121                 }
122                 return reply;
123         }
124
125         public void add_telem(AltosLine line) throws InterruptedException {
126                 for (int e = 0; e < monitors.size(); e++) {
127                         LinkedBlockingQueue<AltosLine> q = monitors.get(e);
128                         q.put(line);
129                 }
130         }
131
132         public void add_reply(AltosLine line) throws InterruptedException {
133                 reply_queue.put (line);
134         }
135
136         public void add_string(String line) throws InterruptedException {
137                 if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) {
138                         add_telem(new AltosLine(line));
139                 } else {
140                         add_reply(new AltosLine(line));
141                 }
142         }
143
144         public void add_bytes(byte[] bytes, int len) throws InterruptedException {
145                 String  line;
146                 try {
147                         line = new String(bytes, 0, len, "UTF-8");
148                 } catch (UnsupportedEncodingException ue) {
149                         line = "";
150                         for (int i = 0; i < len; i++)
151                                 line = line + bytes[i];
152                 }
153                 if (debug)
154                         System.out.printf("\t\t\t\t\t%s\n", line);
155                 add_string(line);
156         }
157
158         public void flush_output() {
159                 for (String s : pending_output)
160                         System.out.print(s);
161                 pending_output.clear();
162         }
163
164         public void flush_input(int timeout) throws InterruptedException {
165                 flush_output();
166                 boolean got_some;
167
168                 do {
169                         Thread.sleep(timeout);
170                         got_some = !reply_queue.isEmpty();
171                         reply_queue.clear();
172                 } while (got_some);
173         }
174
175
176         public void flush_input() throws InterruptedException {
177                 if (remote)
178                         flush_input(500);
179                 else
180                         flush_input(100);
181         }
182
183
184         /*
185          * Various command-level operations on
186          * the link
187          */
188         public boolean monitor_mode = false;
189         public int telemetry = AltosLib.ao_telemetry_standard;
190         public double frequency;
191         AltosConfigData config_data;
192
193         private int telemetry_len() {
194                 return AltosLib.telemetry_len(telemetry);
195         }
196
197         private void set_radio_freq(int frequency) {
198                 if (monitor_mode)
199                         printf("m 0\nc F %d\nm %x\n",
200                                frequency, telemetry_len());
201                 else
202                         printf("c F %d\n", frequency);
203                 flush_output();
204         }
205
206         public void set_radio_frequency(double frequency,
207                                         boolean has_frequency,
208                                         boolean has_setting,
209                                         int cal) {
210                 if (debug)
211                         System.out.printf("set_radio_frequency %7.3f (freq %b) (set %b) %d\n", frequency, has_frequency, has_setting, cal);
212                 if (frequency == 0)
213                         return;
214                 if (has_frequency)
215                         set_radio_freq((int) Math.floor (frequency * 1000));
216                 else if (has_setting)
217                         set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
218                 else
219                         set_channel(AltosConvert.radio_frequency_to_channel(frequency));
220         }
221
222         public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException {
223                 frequency = in_frequency;
224                 config_data();
225                 set_radio_frequency(frequency,
226                                     config_data.radio_frequency != 0,
227                                     config_data.radio_setting != 0,
228                                     config_data.radio_calibration);
229         }
230
231         public void set_telemetry(int in_telemetry) {
232                 telemetry = in_telemetry;
233                 if (monitor_mode)
234                         printf("m 0\nm %x\n", telemetry_len());
235                 flush_output();
236         }
237
238         public void set_monitor(boolean monitor) {
239                 monitor_mode = monitor;
240                 if (monitor)
241                         printf("m %x\n", telemetry_len());
242                 else
243                         printf("m 0\n");
244                 flush_output();
245         }
246
247         private void set_channel(int channel) {
248                 if (monitor_mode)
249                         printf("m 0\nc r %d\nm %x\n",
250                                channel, telemetry_len());
251                 else
252                         printf("c r %d\n", channel);
253                 flush_output();
254         }
255
256         private void set_radio_setting(int setting) {
257                 if (monitor_mode)
258                         printf("m 0\nc R %d\nm %x\n",
259                                setting, telemetry_len());
260                 else
261                         printf("c R %d\n", setting);
262                 flush_output();
263         }
264
265         public AltosConfigData config_data() throws InterruptedException, TimeoutException {
266                 if (config_data == null)
267                         config_data = new AltosConfigData(this);
268                 return config_data;
269         }
270
271         public void set_callsign(String callsign) {
272                 printf ("c c %s\n", callsign);
273                 flush_output();
274         }
275
276         public boolean remote;
277         public int serial;
278         public String name;
279
280         public void start_remote() throws TimeoutException, InterruptedException {
281                 if (debug)
282                         System.out.printf("start remote %7.3f\n", frequency);
283                 if (frequency == 0.0)
284                         frequency = AltosPreferences.frequency(serial);
285                 set_radio_frequency(frequency);
286                 set_callsign(AltosPreferences.callsign());
287                 printf("p\nE 0\n");
288                 flush_input();
289                 remote = true;
290         }
291
292         public void stop_remote() throws InterruptedException {
293                 if (debug)
294                         System.out.printf("stop remote\n");
295                 try {
296                         flush_input();
297                 } finally {
298                         printf ("~\n");
299                         flush_output();
300                 }
301                 remote = false;
302         }
303
304         public AltosLink() {
305         }
306 }