altoslib: Add new eeprom management code
[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.MISSING) {
61                                 if (config_data.product != null) {
62                                         if (config_data.product.startsWith("TeleMetrum"))
63                                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
64                                         else if (config_data.product.startsWith("TeleMini"))
65                                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_TINY;
66                                 }
67                         }
68                 }
69                 return config_data;
70         }
71
72         public void reset_config_data() {
73                 config_data = null;
74         }
75
76         private void write_config(Writer w) throws IOException {
77                 config.write(w, 0, true);
78                 w.append('\n');
79         }
80
81         /*
82          * Private I/O APIs
83          */
84         private void write_data(Writer w) throws IOException {
85                 PrintWriter pw = new PrintWriter(w);
86
87                 for (int i = 0; i < data.size(); i++) {
88                         if (i > 0) {
89                                 if ((i & 0x1f) == 0)
90                                         pw.printf("\n");
91                                 else
92                                         pw.printf(" ");
93                         }
94                         pw.printf("%02x", data.get(i));
95                 }
96                 w.append('\n');
97         }
98
99         private boolean read_config(Reader r) throws IOException {
100                 config = AltosJson.fromReader(r);
101                 if (config == null)
102                         return false;
103                 return true;
104         }
105
106         static private byte[] byte_list_to_array(List<Byte> bytes) {
107                 byte[] data = new byte[bytes.size()];
108                 int i = 0;
109
110                 for (Byte b : bytes) {
111                         data[i++] = b;
112                 }
113                 return data;
114         }
115
116         private boolean read_data(Reader r) throws IOException {
117                 BufferedReader  br = new BufferedReader(r);
118                 String          s;
119
120                 data = new ArrayList<Byte>();
121                 while ((s = br.readLine()) != null) {
122                         String[] tokens = s.split("\\s+");
123
124                         for (int i = 0; i < tokens.length; i++) {
125                                 try {
126                                         data.add((byte) AltosLib.fromhex(tokens[i]));
127                                 } catch (NumberFormatException e) {
128                                         throw new IOException(e.toString());
129                                 }
130                         }
131                 }
132                 return true;
133         }
134
135         private boolean read_old_config(BufferedReader r) throws IOException {
136                 AltosConfigData cfg = new AltosConfigData();
137                 for (;;) {
138                         boolean done = false;
139
140                         /* The data starts with an upper case F character followed by a space */
141                         r.mark(2);
142                         int     first = r.read();
143                         if (first == 'F') {
144                                 int second =  r.read();
145                                 if (second == ' ')
146                                         done = true;
147                         }
148                         r.reset();
149                         if (done)
150                                 break;
151
152                         String line = r.readLine();
153                         if (line == null)
154                                 return false;
155                         cfg.parse_line(line);
156                 }
157                 config = new AltosJson(cfg);
158                 return true;
159         }
160
161         private boolean read_old_data(BufferedReader r) throws IOException {
162                 String line;
163
164                 data = new ArrayList<Byte>();
165                 while ((line = r.readLine()) != null) {
166                         String[] tokens = line.split("\\s+");
167
168                         /* Make sure there's at least a type and time */
169                         if (tokens.length < 2)
170                                 break;
171
172                         /* packet type */
173                         if (tokens[0].length() != 1)
174                                 break;
175                         int start = data.size();
176
177                         if (config_data().log_format != AltosLib.AO_LOG_FORMAT_TINY) {
178                                 data.add((byte) tokens[0].codePointAt(0));
179
180                                 int time = AltosLib.fromhex(tokens[1]);
181
182                                 data.add((byte) 0);
183                                 data.add((byte) (time & 0xff));
184                                 data.add((byte) (time >> 8));
185                         }
186                         if (tokens.length == 4) {
187                                 /* Handle ancient log files */
188                                 if (config_data().log_format == AltosLib.AO_LOG_FORMAT_TINY) {
189                                         /*
190                                          * Ancient TeleMini log files stored "extra" data to pretend
191                                          * that it was a TeleMetrum device. Throw that away and
192                                          * just save the actual log data.
193                                          */
194                                         int a = AltosLib.fromhex(tokens[2]);
195                                         int b = AltosLib.fromhex(tokens[3]);
196                                         if (a != 0)
197                                                 b = 0x8000 | a;
198                                         data.add((byte) (b & 0xff));
199                                         data.add((byte) ((b >> 8)));
200                                 } else {
201                                         for (int i = 2; i < tokens.length; i++) {
202                                                 int v = AltosLib.fromhex(tokens[i]);
203                                                 data.add((byte) (v & 0xff));
204                                                 data.add((byte) ((v >> 8)));
205                                         }
206                                         /* Re-compute the checksum byte */
207                                         data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
208                                 }
209                         } else {
210                                 for (int i = 2; i < tokens.length; i++)
211                                         data.add((byte) AltosLib.fromhex(tokens[i]));
212                                 /* Re-compute the checksum byte */
213                                 data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
214                         }
215                 }
216                 return true;
217         }
218
219         private void read(Reader r) throws IOException {
220                 BufferedReader  br = new BufferedReader(r);
221
222                 br.mark(1);
223                 int c = br.read();
224                 br.reset();
225
226                 if (c == '{') {
227                         if (!read_config(br))
228                                 throw new IOException("failed to read config");
229                         if (!read_data(br))
230                                 throw new IOException("failed to read data");
231                 } else {
232                         if (!read_old_config(br))
233                                 throw new IOException("failed to read old config");
234                         if (!read_old_data(br))
235                                 throw new IOException("failed to read old data");
236                 }
237         }
238
239         /*
240          * Public APIs for I/O
241          */
242         public void write(Writer w) throws IOException {
243                 write_config(w);
244                 write_data(w);
245         }
246
247         public String toString() {
248                 try {
249                         Writer  w = new StringWriter();
250
251                         write(w);
252                         return w.toString();
253                 } catch (Exception e) {
254                         return null;
255                 }
256         }
257
258         public void print() throws IOException {
259                 System.out.printf("%s", toString());
260         }
261
262         /*
263          * Constructors
264          */
265         public AltosEepromNew(Reader r) throws IOException {
266                 read(r);
267         }
268
269         public AltosEepromNew(String s) throws IOException {
270                 read(new StringReader(s));
271         }
272
273         public AltosEepromNew() {
274         }
275 }