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; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 package org.altusmetrum.altoslib_13;
22 import java.util.concurrent.*;
25 public abstract class AltosLink implements Runnable {
27 public final static int ERROR = -1;
28 public final static int TIMEOUT = -2;
30 public abstract int getchar() throws InterruptedException;
31 public abstract void print(String data) throws InterruptedException;
32 public abstract void putchar(byte c);
33 public abstract void close() throws InterruptedException;
35 public static boolean debug = false;
36 public static void set_debug(boolean in_debug) { debug = in_debug; }
38 public boolean has_error;
40 LinkedList<String> pending_output = new LinkedList<String>();
42 public LinkedList<LinkedBlockingQueue<AltosLine>> monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();;
43 public LinkedBlockingQueue<AltosLine> reply_queue = new LinkedBlockingQueue<AltosLine>();
44 public LinkedBlockingQueue<byte[]> binary_queue = new LinkedBlockingQueue<byte[]>();
46 private String match_string = null;
48 public synchronized void add_monitor(LinkedBlockingQueue<AltosLine> q) {
53 public synchronized void remove_monitor(LinkedBlockingQueue<AltosLine> q) {
55 if (monitors.isEmpty())
59 public void printf(String format, Object ... arguments) {
60 String line = String.format(format, arguments);
62 synchronized (pending_output) {
63 pending_output.add(line);
68 } catch (InterruptedException ie) {
73 public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException {
75 AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
81 public String get_reply() throws InterruptedException {
82 return get_reply(5000);
86 public abstract boolean can_cancel_reply();
87 public abstract boolean show_reply_timeout();
88 public abstract void hide_reply_timeout();
90 public boolean reply_abort;
92 boolean cancel_enable = true;
94 public void set_cancel_enable(boolean e) {
98 boolean reply_timeout_shown = false;
100 private boolean check_reply_timeout() {
103 if (!reply_timeout_shown)
104 reply_timeout_shown = show_reply_timeout();
108 private void cleanup_reply_timeout() {
109 if (reply_timeout_shown) {
110 reply_timeout_shown = false;
111 hide_reply_timeout();
115 private int len_read = 0;
117 private boolean match_bytes(byte[] bytes, int byte_count, String match) {
118 if (byte_count < match.length())
120 String line = new String(bytes, 0, byte_count, AltosLib.unicode_set);
123 return line.indexOf(match) >= 0;
128 byte[] line_bytes = null;
134 if (Thread.interrupted()) {
139 System.out.printf("ERROR\n");
141 add_telem (new AltosLine());
142 add_reply (new AltosLine());
147 System.out.printf("TIMEOUT\n");
150 if (c == '\r' && len_read == 0)
153 if (c == '\n' && len_read == 0) {
154 if (line_count != 0) {
155 add_bytes(line_bytes, line_count);
159 if (line_bytes == null) {
160 line_bytes = new byte[256];
161 } else if (line_count == line_bytes.length) {
162 byte[] new_line_bytes = new byte[line_count * 2];
163 System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count);
164 line_bytes = new_line_bytes;
166 line_bytes[line_count] = (byte) c;
168 if (len_read !=0 && line_count == len_read) {
169 add_binary(line_bytes, line_count);
173 if (match_string != null && match_bytes(line_bytes, line_count, match_string)) {
175 add_bytes(line_bytes, line_count);
181 } catch (InterruptedException e) {
185 public void set_match(String match) {
186 match_string = match;
189 public String get_reply(int timeout) throws InterruptedException {
190 boolean can_cancel = can_cancel_reply();
193 // if (!can_cancel && remote)
194 // System.out.printf("Uh-oh, reading remote serial device from swing thread\n");
196 if (remote && can_cancel) {
198 switch (telemetry_rate) {
199 case AltosLib.ao_telemetry_rate_38400:
203 case AltosLib.ao_telemetry_rate_9600:
206 case AltosLib.ao_telemetry_rate_2400:
217 reply_timeout_shown = false;
219 AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
221 cleanup_reply_timeout();
225 if (!remote || !can_cancel || check_reply_timeout()) {
236 public byte[] get_binary_reply(int timeout, int len) throws InterruptedException {
237 boolean can_cancel = can_cancel_reply();
249 reply_timeout_shown = false;
251 bytes = binary_queue.poll(timeout, TimeUnit.MILLISECONDS);
253 cleanup_reply_timeout();
256 if (!remote || !can_cancel || check_reply_timeout()) {
268 public void add_telem(AltosLine line) throws InterruptedException {
269 for (int e = 0; e < monitors.size(); e++) {
270 LinkedBlockingQueue<AltosLine> q = monitors.get(e);
275 public void add_reply(AltosLine line) throws InterruptedException {
276 reply_queue.put (line);
279 public void abort_reply() {
281 add_telem (new AltosLine());
282 add_reply (new AltosLine());
283 } catch (InterruptedException ie) {
287 public void add_string(String line) throws InterruptedException {
288 if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) {
289 add_telem(new AltosLine(line));
291 add_reply(new AltosLine(line));
295 public void add_bytes(byte[] bytes, int len) throws InterruptedException {
297 line = new String(bytes, 0, len, AltosLib.unicode_set);
299 System.out.printf("\t\t\t\t\t%s\n", line);
303 public void add_binary(byte[] bytes, int len) throws InterruptedException {
304 byte[] dup = new byte[len];
307 System.out.printf ("\t\t\t\t\t%d:", len);
308 for(int i = 0; i < len; i++) {
311 System.out.printf(" %02x", dup[i]);
314 System.out.printf("\n");
316 binary_queue.put(dup);
319 public synchronized void flush_output() {
320 if (pending_output == null)
322 synchronized (pending_output) {
323 for (String s : pending_output)
325 pending_output.clear();
329 public void flush_input(int timeout) throws InterruptedException {
334 Thread.sleep(timeout);
335 got_some = !reply_queue.isEmpty();
341 public void flush_input() throws InterruptedException {
344 switch (telemetry_rate) {
345 case AltosLib.ao_telemetry_rate_38400:
349 case AltosLib.ao_telemetry_rate_9600:
352 case AltosLib.ao_telemetry_rate_2400:
356 flush_input(timeout);
363 * Various command-level operations on
366 public boolean monitor_mode = false;
367 public int telemetry = AltosLib.ao_telemetry_standard;
368 public int telemetry_rate = -1;
369 public double frequency;
370 public String callsign;
371 private AltosConfigData config_data_local;
372 private AltosConfigData config_data_remote;
374 private Object config_data_lock = new Object();
376 private int telemetry_len() {
377 return AltosLib.telemetry_len(telemetry);
380 private void set_radio_freq(int frequency) {
382 printf("m 0\nc F %d\nm %x\n",
383 frequency, telemetry_len());
385 printf("c F %d\n", frequency);
389 public void set_radio_frequency(double frequency,
390 boolean has_frequency,
394 System.out.printf("set_radio_frequency %7.3f (freq %b) (set %b) %d\n", frequency, has_frequency, has_setting, cal);
398 set_radio_freq((int) Math.floor (frequency * 1000 + 0.5));
399 else if (has_setting)
400 set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
402 set_channel(AltosConvert.radio_frequency_to_channel(frequency));
405 public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException {
406 frequency = in_frequency;
407 AltosConfigData config_data = config_data();
408 set_radio_frequency(frequency,
409 config_data.radio_frequency > 0,
410 config_data.radio_setting > 0,
411 config_data.radio_calibration);
414 public void set_telemetry(int in_telemetry) {
415 telemetry = in_telemetry;
417 printf("m 0\nm %x\n", telemetry_len());
421 public void set_telemetry_rate(int in_telemetry_rate) {
422 telemetry_rate = in_telemetry_rate;
424 printf("m 0\nc T %d\nm %x\n", telemetry_rate, telemetry_len());
426 printf("c T %d\n", telemetry_rate);
430 public synchronized void set_monitor(boolean monitor) {
431 monitor_mode = monitor;
433 printf("m %x\n", telemetry_len());
439 public synchronized boolean get_monitor() {
443 private void set_channel(int channel) {
445 printf("m 0\nc r %d\nm %x\n",
446 channel, telemetry_len());
448 printf("c r %d\n", channel);
452 private void set_radio_setting(int setting) {
454 printf("m 0\nc R %d\nm %x\n",
455 setting, telemetry_len());
457 printf("c R %d\n", setting);
461 public AltosConfigData config_data() throws InterruptedException, TimeoutException {
462 synchronized(config_data_lock) {
463 AltosConfigData config_data;
466 if (config_data_remote == null) {
468 config_data_remote = new AltosConfigData(this);
472 config_data = config_data_remote;
474 if (config_data_local == null) {
476 config_data_local = new AltosConfigData(this);
480 config_data = config_data_local;
486 public void set_callsign(String callsign) {
487 this.callsign = callsign;
488 if (callsign != null) {
489 printf ("c c %s\n", callsign);
494 public boolean is_loader() throws InterruptedException {
498 String line = get_reply();
502 if (line.startsWith("software-version"))
504 if (line.startsWith("altos-loader"))
510 public void to_loader() throws InterruptedException {
517 public boolean remote;
521 public void start_remote() throws TimeoutException, InterruptedException {
522 if (frequency == 0.0)
523 frequency = AltosPreferences.frequency(serial);
525 System.out.printf("start remote %7.3f\n", frequency);
526 set_radio_frequency(frequency);
527 if (telemetry_rate < 0)
528 telemetry_rate = AltosPreferences.telemetry_rate(serial);
529 set_telemetry_rate(telemetry_rate);
530 if (callsign == null || callsign.equals(""))
531 callsign = AltosPreferences.callsign();
532 set_callsign(callsign);
538 public void stop_remote() throws InterruptedException {
540 System.out.printf("stop remote\n");
550 public int rssi() throws TimeoutException, InterruptedException {
554 String line = get_reply_no_dialog(5000);
556 throw new TimeoutException();
557 String[] items = line.split("\\s+");
558 if (items.length < 2)
560 if (!items[0].equals("RSSI:"))
562 int rssi = Integer.parseInt(items[1]);
566 public String[] adc() throws TimeoutException, InterruptedException {
569 String line = get_reply_no_dialog(5000);
571 throw new TimeoutException();
573 if (!line.startsWith("tick:"))
575 String[] items = line.split("\\s+");
580 public boolean has_monitor_battery() {
582 return config_data().has_monitor_battery();
583 } catch (InterruptedException ie) {
585 } catch (TimeoutException te) {
590 public double monitor_battery() throws InterruptedException {
591 double volts = AltosLib.MISSING;
594 AltosConfigData config_data = config_data();
595 int monitor_batt = AltosLib.MISSING;
597 if (config_data.has_monitor_battery()) {
598 String[] items = adc();
599 for (int i = 0; i < items.length;) {
600 if (items[i].equals("batt")) {
601 monitor_batt = Integer.parseInt(items[i+1]);
608 if (monitor_batt != AltosLib.MISSING) {
609 if (config_data.product.startsWith("TeleBT-v3") || config_data.product.startsWith("TeleBT-v4")) {
610 volts = AltosConvert.tele_bt_3_battery(monitor_batt);
612 volts = AltosConvert.cc_battery_to_voltage(monitor_batt);
616 } catch (TimeoutException te) {