altoslib: Set default log format to FULL until we learn different
[fw/altos] / altoslib / AltosEepromNew.java
1 /*
2  * Copyright © 2017 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, either version 2 of the License, or
7  * (at your option) any later version.
8  *
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.
13  */
14
15 package org.altusmetrum.altoslib_11;
16
17 import java.util.*;
18 import java.io.*;
19
20 public class AltosEepromNew {
21
22         private AltosJson       config;
23         ArrayList<Byte>         data;
24         private AltosConfigData config_data;
25
26         /*
27          * Public accessor APIs
28          */
29         public int data8(int offset) {
30                 return ((int) data.get(offset)) & 0xff;
31         }
32
33         public int data16(int offset) {
34                 return data8(offset) | (data8(offset+1) << 8);
35         }
36
37         public int data24(int offset) {
38                 return (data8(offset) |
39                         (data8(offset+1) << 8) |
40                         (data8(offset+2) << 16));
41         }
42
43         public int data32(int offset) {
44                 return (data8(offset) |
45                         (data8(offset+1) << 8) |
46                         (data8(offset+2) << 16) |
47                         (data8(offset+3) << 24));
48         }
49
50         public int size() {
51                 return data.size();
52         }
53
54         public AltosConfigData config_data() {
55                 if (config_data == null) {
56                         config_data = (AltosConfigData) config.make(AltosConfigData.class);
57                         if (config_data == null)
58                                 config_data = new AltosConfigData();
59
60                         if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN) {
61                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
62                                 if (config_data.product != null) {
63                                         if (config_data.product.startsWith("TeleMetrum"))
64                                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
65                                         else if (config_data.product.startsWith("TeleMini"))
66                                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_TINY;
67                                 }
68                         }
69                 }
70                 return config_data;
71         }
72
73         public void reset_config_data() {
74                 config_data = null;
75         }
76
77         private void write_config(Writer w) throws IOException {
78                 config.write(w, 0, true);
79                 w.append('\n');
80         }
81
82         /*
83          * Private I/O APIs
84          */
85         private void write_data(Writer w) throws IOException {
86                 PrintWriter pw = new PrintWriter(w);
87
88                 for (int i = 0; i < data.size(); i++) {
89                         if (i > 0) {
90                                 if ((i & 0x1f) == 0)
91                                         pw.printf("\n");
92                                 else
93                                         pw.printf(" ");
94                         }
95                         pw.printf("%02x", data.get(i));
96                 }
97                 w.append('\n');
98         }
99
100         private boolean read_config(Reader r) throws IOException {
101                 config = AltosJson.fromReader(r);
102                 if (config == null)
103                         return false;
104                 return true;
105         }
106
107         private boolean read_data(Reader r) throws IOException {
108                 BufferedReader  br = new BufferedReader(r);
109                 String          s;
110
111                 data = new ArrayList<Byte>();
112                 while ((s = br.readLine()) != null) {
113
114                         String[] tokens = s.split("\\s+");
115
116                         for (int i = 0; i < tokens.length; i++) {
117                                 if (tokens[i].length() > 0) {
118                                         try {
119                                                 data.add((byte) AltosLib.fromhex(tokens[i]));
120                                         } catch (NumberFormatException e) {
121                                                 throw new IOException(e.toString());
122                                         }
123                                 }
124                         }
125                 }
126                 return true;
127         }
128
129         private boolean read_old_config(BufferedReader r) throws IOException {
130                 AltosConfigData cfg = new AltosConfigData();
131                 for (;;) {
132                         boolean done = false;
133
134                         /* The data starts with an upper case F character followed by a space */
135                         r.mark(2);
136                         int     first = r.read();
137                         if (first == 'F') {
138                                 int second =  r.read();
139                                 if (second == ' ')
140                                         done = true;
141                         }
142                         r.reset();
143                         if (done)
144                                 break;
145
146                         String line = r.readLine();
147                         if (line == null)
148                                 return false;
149                         cfg.parse_line(line);
150                 }
151                 config = new AltosJson(cfg);
152                 return true;
153         }
154
155         private boolean read_old_data(BufferedReader r) throws IOException {
156                 String line;
157
158                 data = new ArrayList<Byte>();
159                 while ((line = r.readLine()) != null) {
160                         String[] tokens = line.split("\\s+");
161
162                         /* Make sure there's at least a type and time */
163                         if (tokens.length < 2)
164                                 break;
165
166                         /* packet type */
167                         if (tokens[0].length() != 1)
168                                 break;
169                         int start = data.size();
170
171                         if (config_data().log_format != AltosLib.AO_LOG_FORMAT_TINY) {
172                                 data.add((byte) tokens[0].codePointAt(0));
173
174                                 int time = AltosLib.fromhex(tokens[1]);
175
176                                 data.add((byte) 0);
177                                 data.add((byte) (time & 0xff));
178                                 data.add((byte) (time >> 8));
179                         }
180                         if (tokens.length == 4) {
181                                 /* Handle ancient log files */
182                                 if (config_data().log_format == AltosLib.AO_LOG_FORMAT_TINY) {
183                                         /*
184                                          * Ancient TeleMini log files stored "extra" data to pretend
185                                          * that it was a TeleMetrum device. Throw that away and
186                                          * just save the actual log data.
187                                          */
188                                         int a = AltosLib.fromhex(tokens[2]);
189                                         int b = AltosLib.fromhex(tokens[3]);
190                                         if (a != 0)
191                                                 b = 0x8000 | a;
192                                         data.add((byte) (b & 0xff));
193                                         data.add((byte) ((b >> 8)));
194                                 } else {
195                                         for (int i = 2; i < tokens.length; i++) {
196                                                 int v = AltosLib.fromhex(tokens[i]);
197                                                 data.add((byte) (v & 0xff));
198                                                 data.add((byte) ((v >> 8)));
199                                         }
200                                         /* Re-compute the checksum byte */
201                                         data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
202                                 }
203                         } else {
204                                 for (int i = 2; i < tokens.length; i++)
205                                         data.add((byte) AltosLib.fromhex(tokens[i]));
206                                 /* Re-compute the checksum byte */
207                                 data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
208                         }
209                 }
210                 return true;
211         }
212
213         private void read(Reader r) throws IOException {
214                 BufferedReader  br = new BufferedReader(r);
215
216                 br.mark(1);
217                 int c = br.read();
218                 br.reset();
219
220                 if (c == '{') {
221                         if (!read_config(br))
222                                 throw new IOException("failed to read config");
223                         if (!read_data(br))
224                                 throw new IOException("failed to read data");
225                 } else {
226                         if (!read_old_config(br))
227                                 throw new IOException("failed to read old config");
228                         if (!read_old_data(br))
229                                 throw new IOException("failed to read old data");
230                 }
231         }
232
233         /*
234          * Public APIs for I/O
235          */
236         public void write(Writer w) throws IOException {
237                 write_config(w);
238                 write_data(w);
239         }
240
241         public String toString() {
242                 try {
243                         Writer  w = new StringWriter();
244
245                         write(w);
246                         return w.toString();
247                 } catch (Exception e) {
248                         return null;
249                 }
250         }
251
252         public void print() throws IOException {
253                 System.out.printf("%s", toString());
254         }
255
256         /*
257          * Constructors
258          */
259         public AltosEepromNew(Reader r) throws IOException {
260                 read(r);
261         }
262
263         public AltosEepromNew(String s) throws IOException {
264                 read(new StringReader(s));
265         }
266
267         public AltosEepromNew(AltosJson config, ArrayList<Byte> data) {
268                 this.config = config;
269                 this.data = data;
270         }
271
272         public AltosEepromNew(AltosConfigData config_data, ArrayList<Byte> data) {
273                 this.config = new AltosJson(config_data);
274                 this.data = data;
275         }
276
277         public AltosEepromNew() {
278         }
279 }