X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=altoslib%2FAltosEeprom.java;fp=altoslib%2FAltosEeprom.java;h=ad7bf88104e8cb9ff106e02500b2737b8aa631a1;hp=6ed14d3abe576773100660fc29e0e67e72fd6742;hb=c8dbcaf69cd538a31ab6e2b568237ae7c8656a9a;hpb=0cbfa444a9f9159cb509bb47ca5590fc1d709f64 diff --git a/altoslib/AltosEeprom.java b/altoslib/AltosEeprom.java index 6ed14d3a..ad7bf881 100644 --- a/altoslib/AltosEeprom.java +++ b/altoslib/AltosEeprom.java @@ -1,132 +1,291 @@ /* - * Copyright © 2013 Keith Packard + * Copyright © 2017 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_11; +package org.altusmetrum.altoslib_12; -import java.io.*; import java.util.*; -import java.text.*; +import java.io.*; + +public class AltosEeprom { -public abstract class AltosEeprom implements AltosStateUpdate { - public int cmd; - public int tick; - public int data8[]; - public boolean valid; + private AltosJson config; + ArrayList data; + private AltosConfigData config_data; - public int data8(int i) { - return data8[i]; + /* + * Public accessor APIs + */ + public int data8(int offset) { + return ((int) data.get(offset)) & 0xff; } - public int data16(int i) { - return ((data8[i] | (data8[i+1] << 8)) << 16) >> 16; + public int data16(int offset) { + return data8(offset) | (data8(offset+1) << 8); } - public int data24(int i) { - return data8[i] | (data8[i+1] << 8) | (data8[i+2] << 16); + public int data24(int offset) { + return (data8(offset) | + (data8(offset+1) << 8) | + (data8(offset+2) << 16)); } - public int data32(int i) { - return data8[i] | (data8[i+1] << 8) | (data8[i+2] << 16) | (data8[i+3] << 24); + public int data32(int offset) { + return (data8(offset) | + (data8(offset+1) << 8) | + (data8(offset+2) << 16) | + (data8(offset+3) << 24)); } - public boolean has_seconds() { return false; } + public int size() { + return data.size(); + } - public int seconds() { return 0; } + public AltosConfigData config_data() { + if (config_data == null) { + config_data = (AltosConfigData) config.make(AltosConfigData.class); + if (config_data == null) + config_data = new AltosConfigData(); - public final static int header_length = 4; + if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN) { + config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL; + if (config_data.product != null) { + if (config_data.product.startsWith("TeleMetrum")) + config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL; + else if (config_data.product.startsWith("TeleMini")) + config_data.log_format = AltosLib.AO_LOG_FORMAT_TINY; + } + } + } + return config_data; + } + + private void write_config(Writer w) throws IOException { + config.write(w, 0, true); + w.append('\n'); + } + + /* + * Private I/O APIs + */ + private void write_data(Writer w) throws IOException { + PrintWriter pw = new PrintWriter(w); - public abstract int record_length(); + for (int i = 0; i < data.size(); i++) { + if (i > 0) { + if ((i & 0x1f) == 0) + pw.printf("\n"); + else + pw.printf(" "); + } + pw.printf("%02x", data.get(i)); + } + w.append('\n'); + } - public void update_state(AltosState state) { - if (cmd == AltosLib.AO_LOG_FLIGHT) - state.set_boost_tick(tick); - else - state.set_tick(tick); + private boolean read_config(InputStream stream) throws IOException { + config = AltosJson.fromInputStream(stream); + if (config == null) + return false; + return true; } - public void write(PrintStream out) { - out.printf("%c %04x", cmd, tick); - if (data8 != null) { - for (int i = 0; i < data8.length; i++) - out.printf (" %02x", data8[i]); + private String read_line(InputStream stream) throws IOException { + StringBuffer buffer = null; + int c; + + for (;;) { + c = stream.read(); + if (c == -1 && buffer == null) + return null; + if (buffer == null) + buffer = new StringBuffer(); + if (c == -1 || c == '\n') + return buffer.toString(); + buffer.append((char) c); } - out.printf ("\n"); } - public String string() { - String s; + private boolean read_data(InputStream stream) throws IOException { + String s; + + data = new ArrayList(); + while ((s = read_line(stream)) != null) { - s = String.format("%c %04x", cmd, tick); - if (data8 != null) { - for (int i = 0; i < data8.length; i++) { - String d = String.format(" %02x", data8[i]); - s = s.concat(d); + String[] tokens = s.split("\\s+"); + + for (int i = 0; i < tokens.length; i++) { + if (tokens[i].length() > 0) { + try { + data.add((byte) AltosLib.fromhex(tokens[i])); + } catch (NumberFormatException e) { + throw new IOException(e.toString()); + } + } } } - s = s.concat("\n"); - return s; + return true; } - void parse_chunk(AltosEepromChunk chunk, int start) throws ParseException { - cmd = chunk.data(start); + private boolean read_old_config(InputStream stream) throws IOException { + AltosConfigData cfg = new AltosConfigData(); + for (;;) { + boolean done = false; - int data_length = record_length() - header_length; + /* The data starts with an upper case F character followed by a space */ + stream.mark(2); + int first = stream.read(); + if (first == 'F') { + int second = stream.read(); + if (second == ' ') + done = true; + } + stream.reset(); + if (done) + break; - valid = !chunk.erased(start, record_length()); - if (valid) { - if (AltosConvert.checksum(chunk.data, start, record_length()) != 0) - throw new ParseException(String.format("invalid checksum at 0x%x", - chunk.address + start), 0); - } else { - cmd = AltosLib.AO_LOG_INVALID; + String line = read_line(stream); + if (line == null) + return false; + cfg.parse_line(line); } + config = new AltosJson(cfg); + return true; + } - tick = chunk.data16(start+2); + private boolean read_old_data(InputStream stream) throws IOException { + String line; - data8 = new int[data_length]; - for (int i = 0; i < data_length; i++) - data8[i] = chunk.data(start + header_length + i); - } + data = new ArrayList(); + while ((line = read_line(stream)) != null) { + String[] tokens = line.split("\\s+"); - void parse_string(String line) { - valid = false; - tick = 0; - cmd = AltosLib.AO_LOG_INVALID; + /* Make sure there's at least a type and time */ + if (tokens.length < 2) + break; - int data_length = record_length() - header_length; + /* packet type */ + if (tokens[0].length() != 1) + break; + int start = data.size(); - if (line == null) - return; - try { - String[] tokens = line.split("\\s+"); + if (config_data().log_format != AltosLib.AO_LOG_FORMAT_TINY) { + byte cmd = (byte) tokens[0].codePointAt(0); + data.add(cmd); - if (tokens[0].length() == 1) { - if (tokens.length == 2 + data_length) { - cmd = tokens[0].codePointAt(0); - tick = Integer.parseInt(tokens[1],16); - valid = true; - data8 = new int[data_length]; + int time = AltosLib.fromhex(tokens[1]); - for (int i = 0; i < data_length; i++) - data8[i] = Integer.parseInt(tokens[2 + i],16); + data.add((byte) 0); + data.add((byte) (time & 0xff)); + data.add((byte) (time >> 8)); + } + if (tokens.length == 4) { + /* Handle ancient log files */ + if (config_data().log_format == AltosLib.AO_LOG_FORMAT_TINY) { + /* + * Ancient TeleMini log files stored "extra" data to pretend + * that it was a TeleMetrum device. Throw that away and + * just save the actual log data. + */ + int a = AltosLib.fromhex(tokens[2]); + int b = AltosLib.fromhex(tokens[3]); + if (a != 0) + b = 0x8000 | a; + data.add((byte) (b & 0xff)); + data.add((byte) ((b >> 8))); + } else { + for (int i = 2; i < tokens.length; i++) { + int v = AltosLib.fromhex(tokens[i]); + data.add((byte) (v & 0xff)); + data.add((byte) ((v >> 8))); + } + /* Re-compute the checksum byte */ + data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start))); } + } else { + for (int i = 2; i < tokens.length; i++) + data.add((byte) AltosLib.fromhex(tokens[i])); + /* Re-compute the checksum byte */ + data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start))); } - } catch (NumberFormatException ne) { } + return true; + } + + private void read(InputStream stream) throws IOException { + BufferedInputStream bis = new BufferedInputStream(stream); + + bis.mark(1); + int c = bis.read(); + bis.reset(); + + if (c == '{') { + if (!read_config(bis)) + throw new IOException("failed to read config"); + if (!read_data(bis)) + throw new IOException("failed to read data"); + } else { + if (!read_old_config(bis)) + throw new IOException("failed to read old config"); + if (!read_old_data(bis)) + throw new IOException("failed to read old data"); + } + } + + /* + * Public APIs for I/O + */ + public void write(Writer w) throws IOException { + write_config(w); + write_data(w); + } + + public String toString() { + try { + Writer w = new StringWriter(); + + write(w); + return w.toString(); + } catch (Exception e) { + return null; + } + } + + public void print() throws IOException { + System.out.printf("%s", toString()); + } + + /* + * Constructors + */ + public AltosEeprom(InputStream stream) throws IOException { + read(stream); + } + + public AltosEeprom(String s) throws IOException { + read(new AltosStringInputStream(s)); + } + + public AltosEeprom(AltosJson config, ArrayList data) { + this.config = config; + this.data = data; + } + + public AltosEeprom(AltosConfigData config_data, ArrayList data) { + this.config = new AltosJson(config_data); + this.data = data; + } + + public AltosEeprom() { } }