2 * Copyright © 2011 Keith Packard <keithp@keithp.com>
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.
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.
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.
18 package org.altusmetrum.altoslib_10;
21 import java.util.concurrent.*;
24 public abstract class AltosLink implements Runnable {
26 public final static int ERROR = -1;
27 public final static int TIMEOUT = -2;
29 public abstract int getchar() throws InterruptedException;
30 public abstract void print(String data) throws InterruptedException;
31 public abstract void putchar(byte c);
32 public abstract void close() throws InterruptedException;
34 public static boolean debug = false;
35 public static void set_debug(boolean in_debug) { debug = in_debug; }
37 public boolean has_error;
39 LinkedList<String> pending_output = new LinkedList<String>();
41 public LinkedList<LinkedBlockingQueue<AltosLine>> monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();;
42 public LinkedBlockingQueue<AltosLine> reply_queue = new LinkedBlockingQueue<AltosLine>();
43 public LinkedBlockingQueue<byte[]> binary_queue = new LinkedBlockingQueue<byte[]>();
45 public synchronized void add_monitor(LinkedBlockingQueue<AltosLine> q) {
50 public synchronized void remove_monitor(LinkedBlockingQueue<AltosLine> q) {
52 if (monitors.isEmpty())
56 public void printf(String format, Object ... arguments) {
57 String line = String.format(format, arguments);
59 synchronized (pending_output) {
60 pending_output.add(line);
65 } catch (InterruptedException ie) {
70 public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException {
72 AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
78 public String get_reply() throws InterruptedException {
79 return get_reply(5000);
83 public abstract boolean can_cancel_reply();
84 public abstract boolean show_reply_timeout();
85 public abstract void hide_reply_timeout();
87 public boolean reply_abort;
89 boolean cancel_enable = true;
91 public void set_cancel_enable(boolean e) {
95 boolean reply_timeout_shown = false;
97 private boolean check_reply_timeout() {
100 if (!reply_timeout_shown)
101 reply_timeout_shown = show_reply_timeout();
105 private void cleanup_reply_timeout() {
106 if (reply_timeout_shown) {
107 reply_timeout_shown = false;
108 hide_reply_timeout();
112 private int len_read = 0;
116 byte[] line_bytes = null;
122 if (Thread.interrupted()) {
127 System.out.printf("ERROR\n");
129 add_telem (new AltosLine());
130 add_reply (new AltosLine());
135 System.out.printf("TIMEOUT\n");
138 if (c == '\r' && len_read == 0)
141 if (c == '\n' && len_read == 0) {
142 if (line_count != 0) {
143 add_bytes(line_bytes, line_count);
147 if (line_bytes == null) {
148 line_bytes = new byte[256];
149 } else if (line_count == line_bytes.length) {
150 byte[] new_line_bytes = new byte[line_count * 2];
151 System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count);
152 line_bytes = new_line_bytes;
154 line_bytes[line_count] = (byte) c;
156 if (len_read !=0 && line_count == len_read) {
157 add_binary(line_bytes, line_count);
164 } catch (InterruptedException e) {
169 public String get_reply(int timeout) throws InterruptedException {
170 boolean can_cancel = can_cancel_reply();
173 // if (!can_cancel && remote)
174 // System.out.printf("Uh-oh, reading remote serial device from swing thread\n");
176 if (remote && can_cancel) {
178 switch (telemetry_rate) {
179 case AltosLib.ao_telemetry_rate_38400:
183 case AltosLib.ao_telemetry_rate_9600:
186 case AltosLib.ao_telemetry_rate_2400:
197 reply_timeout_shown = false;
199 AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
201 cleanup_reply_timeout();
205 if (!remote || !can_cancel || check_reply_timeout()) {
216 public byte[] get_binary_reply(int timeout, int len) throws InterruptedException {
217 boolean can_cancel = can_cancel_reply();
229 reply_timeout_shown = false;
231 bytes = binary_queue.poll(timeout, TimeUnit.MILLISECONDS);
233 cleanup_reply_timeout();
236 if (!remote || !can_cancel || check_reply_timeout()) {
248 public void add_telem(AltosLine line) throws InterruptedException {
249 for (int e = 0; e < monitors.size(); e++) {
250 LinkedBlockingQueue<AltosLine> q = monitors.get(e);
255 public void add_reply(AltosLine line) throws InterruptedException {
256 reply_queue.put (line);
259 public void abort_reply() {
261 add_telem (new AltosLine());
262 add_reply (new AltosLine());
263 } catch (InterruptedException ie) {
267 public void add_string(String line) throws InterruptedException {
268 if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) {
269 add_telem(new AltosLine(line));
271 add_reply(new AltosLine(line));
275 public void add_bytes(byte[] bytes, int len) throws InterruptedException {
277 line = new String(bytes, 0, len, AltosLib.unicode_set);
279 System.out.printf("\t\t\t\t\t%s\n", line);
283 public void add_binary(byte[] bytes, int len) throws InterruptedException {
284 byte[] dup = new byte[len];
287 System.out.printf ("\t\t\t\t\t%d:", len);
288 for(int i = 0; i < len; i++) {
291 System.out.printf(" %02x", dup[i]);
294 System.out.printf("\n");
296 binary_queue.put(dup);
299 public synchronized void flush_output() {
300 if (pending_output == null)
302 synchronized (pending_output) {
303 for (String s : pending_output)
305 pending_output.clear();
309 public void flush_input(int timeout) throws InterruptedException {
314 Thread.sleep(timeout);
315 got_some = !reply_queue.isEmpty();
321 public void flush_input() throws InterruptedException {
330 * Various command-level operations on
333 public boolean monitor_mode = false;
334 public int telemetry = AltosLib.ao_telemetry_standard;
335 public int telemetry_rate = -1;
336 public double frequency;
337 public String callsign;
338 AltosConfigData config_data;
340 private Object config_data_lock = new Object();
342 private int telemetry_len() {
343 return AltosLib.telemetry_len(telemetry);
346 private void set_radio_freq(int frequency) {
348 printf("m 0\nc F %d\nm %x\n",
349 frequency, telemetry_len());
351 printf("c F %d\n", frequency);
355 public void set_radio_frequency(double frequency,
356 boolean has_frequency,
360 System.out.printf("set_radio_frequency %7.3f (freq %b) (set %b) %d\n", frequency, has_frequency, has_setting, cal);
364 set_radio_freq((int) Math.floor (frequency * 1000 + 0.5));
365 else if (has_setting)
366 set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
368 set_channel(AltosConvert.radio_frequency_to_channel(frequency));
371 public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException {
372 frequency = in_frequency;
374 set_radio_frequency(frequency,
375 config_data.radio_frequency > 0,
376 config_data.radio_setting > 0,
377 config_data.radio_calibration);
380 public void set_telemetry(int in_telemetry) {
381 telemetry = in_telemetry;
383 printf("m 0\nm %x\n", telemetry_len());
387 public void set_telemetry_rate(int in_telemetry_rate) {
388 telemetry_rate = in_telemetry_rate;
390 printf("m 0\nc T %d\nm %x\n", telemetry_rate, telemetry_len());
392 printf("c T %d\n", telemetry_rate);
396 public synchronized void set_monitor(boolean monitor) {
397 monitor_mode = monitor;
399 printf("m %x\n", telemetry_len());
405 public synchronized boolean get_monitor() {
409 private void set_channel(int channel) {
411 printf("m 0\nc r %d\nm %x\n",
412 channel, telemetry_len());
414 printf("c r %d\n", channel);
418 private void set_radio_setting(int setting) {
420 printf("m 0\nc R %d\nm %x\n",
421 setting, telemetry_len());
423 printf("c R %d\n", setting);
427 public AltosConfigData config_data() throws InterruptedException, TimeoutException {
428 synchronized(config_data_lock) {
429 if (config_data == null) {
431 config_data = new AltosConfigData(this);
439 public void set_callsign(String callsign) {
440 this.callsign = callsign;
441 if (callsign != null) {
442 printf ("c c %s\n", callsign);
447 public boolean is_loader() throws InterruptedException {
451 String line = get_reply();
455 if (line.startsWith("software-version"))
457 if (line.startsWith("altos-loader"))
463 public void to_loader() throws InterruptedException {
470 public boolean remote;
474 public void start_remote() throws TimeoutException, InterruptedException {
475 if (frequency == 0.0)
476 frequency = AltosPreferences.frequency(serial);
478 System.out.printf("start remote %7.3f\n", frequency);
479 set_radio_frequency(frequency);
480 if (telemetry_rate < 0)
481 telemetry_rate = AltosPreferences.telemetry_rate(serial);
482 set_telemetry_rate(telemetry_rate);
483 if (callsign == null || callsign.equals(""))
484 callsign = AltosPreferences.callsign();
485 set_callsign(callsign);
491 public void stop_remote() throws InterruptedException {
493 System.out.printf("stop remote\n");
503 public int rssi() throws TimeoutException, InterruptedException {
507 String line = get_reply_no_dialog(5000);
509 throw new TimeoutException();
510 String[] items = line.split("\\s+");
511 if (items.length < 2)
513 if (!items[0].equals("RSSI:"))
515 int rssi = Integer.parseInt(items[1]);
519 public String[] adc() throws TimeoutException, InterruptedException {
522 String line = get_reply_no_dialog(5000);
524 throw new TimeoutException();
526 if (!line.startsWith("tick:"))
528 String[] items = line.split("\\s+");
533 public boolean has_monitor_battery() {
534 return config_data.has_monitor_battery();
537 public double monitor_battery() throws InterruptedException {
538 int monitor_batt = AltosLib.MISSING;
540 if (config_data.has_monitor_battery()) {
542 String[] items = adc();
543 for (int i = 0; i < items.length;) {
544 if (items[i].equals("batt")) {
545 monitor_batt = Integer.parseInt(items[i+1]);
551 } catch (TimeoutException te) {
554 if (monitor_batt == AltosLib.MISSING)
555 return AltosLib.MISSING;
557 double volts = AltosLib.MISSING;
558 if (config_data.product.startsWith("TeleBT-v3")) {
559 volts = AltosConvert.tele_bt_3_battery(monitor_batt);
561 volts = AltosConvert.cc_battery_to_voltage(monitor_batt);