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