1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.diyefi.openlogviewer.decoder;
24
25
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.BufferedInputStream;
29 import java.io.IOException;
30 import java.util.Arrays;
31
32 import org.diyefi.openlogviewer.OpenLogViewer;
33 import org.diyefi.openlogviewer.decoder.LogField.types;
34 import org.diyefi.openlogviewer.genericlog.GenericLog;
35
36
37
38
39
40
41 public class FreeEMSBin extends AbstractDecoder implements Runnable {
42 private static final int initialLength = 75000;
43 private static final int loadFactor = 2;
44
45 private static final int MINIMUM_PACKET_LENGTH = 3;
46 private static final int MAXIMUM_PACKET_LENGTH = 0x0820;
47
48
49
50 private static final short ESCAPE_BYTE = 0xBB;
51 private static final short START_BYTE = 0xAA;
52 private static final short STOP_BYTE = 0xCC;
53 private static final short ESCAPED_ESCAPE_BYTE = 0x44;
54 private static final short ESCAPED_START_BYTE = 0x55;
55 private static final short ESCAPED_STOP_BYTE = 0x33;
56
57 private boolean startFound;
58 private File logFile;
59 private short[] packetBuffer;
60 private GenericLog decodedLog;
61 private Thread t;
62 private int packetLength;
63 private int firstPayloadIDFound = -1;
64
65 private long startTime;
66
67 private static String[] coreStatusAFlagNames = {
68 "CS-FuelPumpPrime",
69 "CS-unused1",
70 "CS-unused2",
71 "CS-unused3",
72 "CS-unused4",
73 "CS-unused5",
74 "CS-unused6",
75 "CS-unused7"
76 };
77
78 private static String[] decoderFlagsFlagNames = {
79 "DF-CombustionSync",
80 "DF-CrankSync",
81 "DF-CamSync",
82 "DF-LAST_TIMESTAMP_VALID",
83 "DF-LAST_PERIOD_VALID",
84 "DF-LAST_MATCH_VALID",
85 "DF-Spare-6",
86 "DF-Spare-7"
87 };
88
89 private static String[] flaggableFlagsNames = {
90 "FF-callsToUISRs",
91 "FF-lowVoltageConditions",
92 "FF-decoderSyncLosses",
93 "FF-decoderSyncCorrections",
94 "FF-decoderSyncStateClears",
95 "FF-serialNoiseErrors",
96 "FF-serialFramingErrors",
97 "FF-serialParityErrors",
98 "FF-serialOverrunErrors",
99 "FF-serialEscapePairMismatches",
100 "FF-serialStartsInsideAPacket",
101 "FF-serialPacketsOverLength",
102 "FF-serialChecksumMismatches",
103 "FF-serialPacketsUnderLength",
104 "FF-commsDebugMessagesNotSent",
105 "FF-commsErrorMessagesNotSent"
106 };
107
108
109 private static LogField[] fields = {
110
111 new LogField("IAT", 100),
112 new LogField("CHT", 100),
113 new LogField("TPS", 640),
114 new LogField("EGO", 32768),
115 new LogField("MAP", 100),
116 new LogField("AAP", 100),
117 new LogField("BRV", 1000),
118 new LogField("MAT", 100),
119 new LogField("EGO2", 32768),
120 new LogField("IAP", 100),
121 new LogField("MAF"),
122 new LogField("DMAP"),
123 new LogField("DTPS"),
124 new LogField("RPM", 2),
125 new LogField("DRPM"),
126 new LogField("DDRPM"),
127
128
129 new LogField("LoadMain", 512),
130 new LogField("VEMain", 512),
131 new LogField("Lambda", 32768),
132 new LogField("AirFlow"),
133 new LogField("densityAndFuel"),
134 new LogField("BasePW", 1250),
135 new LogField("ETE", (100.0 / 32768.0)),
136 new LogField("TFCTotal", 1250),
137 new LogField("EffectivePW", 1250),
138 new LogField("IDT", 1250),
139 new LogField("RefPW", 1250),
140 new LogField("Advance", 50),
141 new LogField("Dwell", 1250),
142
143
144 new LogField("tempClock"),
145 new LogField("coreStatusA", types.BITS8, coreStatusAFlagNames),
146 new LogField("decoderFlags", types.BITS8, decoderFlagsFlagNames),
147 new LogField("flaggableFlags", types.BITS16, flaggableFlagsNames),
148 new LogField("currentEvent", types.UINT8),
149 new LogField("syncLostWithThisID", types.UINT8),
150 new LogField("syncLostOnThisEvent", types.UINT8),
151 new LogField("syncCaughtOnThisEvent", types.UINT8),
152 new LogField("syncResetCalls", types.UINT8),
153 new LogField("primaryTeethSeen", types.UINT8),
154 new LogField("secondaryTeethSeen", types.UINT8),
155 new LogField("serialOverrunErrors", types.UINT8),
156 new LogField("serialHardwareErrors", types.UINT8),
157 new LogField("serialAndCommsCodeErrors", types.UINT8),
158 new LogField("inputEventTimeTolerance"),
159 new LogField("zsp10"),
160 new LogField("zsp9"),
161 new LogField("zsp8"),
162 new LogField("zsp7"),
163 new LogField("zsp6"),
164 new LogField("zsp5"),
165 new LogField("zsp4"),
166 new LogField("zsp3"),
167 new LogField("clockInMilliSeconds"),
168 new LogField("clock8thMSsInMillis", 8)
169 };
170
171
172
173
174
175
176
177
178 public FreeEMSBin(final String path) {
179 this(new File(path));
180 }
181
182
183
184
185
186 public FreeEMSBin(final File f) {
187 startTime = System.currentTimeMillis();
188 logFile = f;
189 startFound = false;
190 packetBuffer = new short[6000];
191 packetLength = 0;
192
193
194
195
196 final String[] headers = new String[fields.length * 32];
197 int headersPosition = 0;
198 for (int i = 0; i < fields.length; i++) {
199 if ((fields[i].getType() == types.BITS8) || (fields[i].getType() == types.BITS16) || (fields[i].getType() == types.BITS32)) {
200 for (int j = 0; j < fields[i].getBitFieldNames().length; j++) {
201 final String flagID = getFlagName(fields[i], j);
202 headers[headersPosition] = flagID;
203 headersPosition++;
204 }
205 } else {
206 headers[headersPosition] = fields[i].getID();
207 headersPosition++;
208 }
209 }
210
211 decodedLog = new GenericLog(Arrays.copyOfRange(headers, 0, headersPosition), initialLength, loadFactor);
212
213 t = new Thread(this, "FreeEMSBin Loading");
214 t.setPriority(Thread.MAX_PRIORITY);
215 t.start();
216 }
217
218
219
220
221
222
223 @Override
224 public final void run() {
225 FileInputStream fis = null;
226 BufferedInputStream bis = null;
227 try {
228
229 final byte[] readByte = new byte[1];
230 short uByte = 0;
231
232
233 int escapePairMismatches = 0;
234 int startsInsideAPacket = 0;
235 int packetsOverLength = 0;
236 int packetsUnderLength = 0;
237 int checksumMismatches = 0;
238 int packetsParsedFully = 0;
239 int packetLengthWrong = 0;
240 int strayBytesLost = 0;
241 int payloadIDWrong = 0;
242
243 startFound = false;
244 fis = new FileInputStream(logFile);
245 bis = new BufferedInputStream(fis);
246 decodedLog.setLogStatus(GenericLog.LOG_LOADING);
247 while (bis.read(readByte) != -1) {
248 uByte = unsignedValueOf(readByte[0]);
249 if (uByte == START_BYTE) {
250 if (!startFound) {
251 startFound = true;
252 } else {
253 startsInsideAPacket++;
254 }
255 packetLength = 0;
256 } else if (startFound) {
257 if (uByte == STOP_BYTE) {
258 if (packetLength < MINIMUM_PACKET_LENGTH) {
259 packetsUnderLength++;
260 } else if (packetLength > MAXIMUM_PACKET_LENGTH) {
261 packetsOverLength++;
262 } else {
263 decodedLog.incrementPosition();
264 final short[] justThePacket = Arrays.copyOfRange(packetBuffer, 0, packetLength);
265 if (checksum(justThePacket)) {
266 if (decodeBasicLogPacket(justThePacket)) {
267 packetsParsedFully++;
268 } else {
269 packetLengthWrong++;
270 payloadIDWrong++;
271 }
272 } else {
273 checksumMismatches++;
274 }
275 }
276 startFound = false;
277 } else if (uByte == ESCAPE_BYTE) {
278 if (bis.read(readByte) != -1) {
279 uByte = unEscape(unsignedValueOf(readByte[0]));
280 if (uByte != (short) -1) {
281 packetBuffer[packetLength] = uByte;
282 packetLength++;
283 } else {
284 startFound = false;
285 escapePairMismatches++;
286 }
287 }
288 } else {
289 packetBuffer[packetLength] = uByte;
290 packetLength++;
291 }
292 } else {
293 strayBytesLost++;
294 }
295 }
296 decodedLog.setLogStatus(GenericLog.LOG_LOADED);
297
298 System.out.println(OpenLogViewer.NEWLINE + "Binary Parsing Statistics:" + OpenLogViewer.NEWLINE);
299
300 System.out.println("EscapePairMismatches: " + escapePairMismatches + " Incremented when an escape is found but not followed by an escapee");
301 System.out.println("StartsInsideAPacket: " + startsInsideAPacket + " Incremented when a start byte is found inside a packet");
302 System.out.println("PacketsOverLength: " + packetsOverLength + " Incremented when the buffer fills up before the end");
303 System.out.println("PacketsUnderLength: " + packetsUnderLength + " Incremented when a packet is found that is too short");
304 System.out.println("ChecksumMismatches: " + checksumMismatches + " Incremented when calculated checksum did not match the received one");
305 System.out.println("PacketsParsedFully: " + packetsParsedFully + " Number of packets that matched all requirements and got into the log");
306 System.out.println("PacketLengthWrong: " + packetLengthWrong + " The length should match the ID passed in, if not, fail.");
307 System.out.println("StrayBytesLost: " + strayBytesLost + " How many bytes were not in a packet! (should be low, ie, under one packet");
308 System.out.println("PayloadIDWrong: " + payloadIDWrong + " Requests to parse packet as A when packet was of type B");
309
310 System.out.println(OpenLogViewer.NEWLINE + "Thank you for choosing FreeEMS!");
311
312 } catch (Exception e) {
313 e.printStackTrace();
314 decodedLog.setLogStatusMessage(e.getMessage());
315 } finally {
316 OpenLogViewer.getInstance().getEntireGraphingPanel().setGraphSize(decodedLog.getRecordCount());
317 decodedLog.setLogStatus(GenericLog.LOG_LOADED);
318 System.out.println("Loaded " + (decodedLog.getRecordCount() + 1) + " records in " + (System.currentTimeMillis() - startTime) + " millis!");
319
320 try {
321 if (bis != null) {
322 bis.close();
323 }
324 } catch (IOException ioe) {
325 ioe.printStackTrace();
326 System.out.println("Failed To Close BIS Stream!");
327 }
328
329 try {
330 if (fis != null) {
331 fis.close();
332 }
333 } catch (IOException ioe) {
334 ioe.printStackTrace();
335 System.out.println("Failed To Close FIS Stream!");
336 }
337 }
338 }
339
340
341
342
343
344
345
346 private boolean decodeBasicLogPacket(final short[] packet) {
347 final int HEADER_HAS_LENGTH_INDEX = 0;
348
349 final int HEADER_HAS_SEQUENCE_INDEX = 2;
350
351
352
353
354
355
356
357
358 int position = 0;
359
360 final short flags = packet[position];
361 position++;
362
363 final short payloadIdUpper = packet[position];
364 position++;
365 final short payloadIdLower = packet[position];
366 position++;
367 final int payloadId = (payloadIdUpper * 256) + payloadIdLower;
368
369 if (firstPayloadIDFound < 0) {
370 firstPayloadIDFound = payloadId;
371 } else if (payloadId != firstPayloadIDFound) {
372 return false;
373 }
374
375
376
377 final int[] flagValues = processFlagBytes(flags, 8);
378
379 if (flagValues[HEADER_HAS_SEQUENCE_INDEX] == 1) {
380 position++;
381 }
382
383 if (flagValues[HEADER_HAS_LENGTH_INDEX] == 1) {
384 position += 2;
385 }
386
387 int lengthOfFields = 0;
388 for (int i = 0; i < fields.length; i++) {
389 lengthOfFields += fields[i].getType().getWidth();
390 }
391
392
393
394 final int payloadLength = ((packet.length - position) - 1);
395 if (payloadLength != lengthOfFields) {
396 System.out.print(" Fields length is: " + lengthOfFields);
397 System.out.print(" Packet length is: " + packet.length);
398 System.out.println(" Payload length is: " + payloadLength);
399 return false;
400 }
401
402 for (int i = 0; i < fields.length; i++) {
403 final LogField field = fields[i];
404
405 int rawValue = 0;
406 for (int j = 0; j < field.getType().getWidth(); j++) {
407 rawValue = (rawValue * 256) + packet[position];
408 position++;
409 }
410
411 if ((field.getType() == types.UINT8) || (field.getType() == types.UINT16) || (field.getType() == types.UINT32)) {
412 final double scaledValue = (double) rawValue / field.getDivBy();
413 final double finalValue = scaledValue + field.getAddTo();
414 decodedLog.addValue(field.getID(), finalValue);
415 } else if ((field.getType() == types.BITS8) || (field.getType() == types.BITS16) || (field.getType() == types.BITS32)) {
416 final int[] processedFlags = processFlagBytes(rawValue, (8 * field.getType().getWidth()));
417 for (int j = 0; j < processedFlags.length; j++) {
418 final String flagID = getFlagName(field, j);
419 decodedLog.addValue(flagID, processedFlags[j]);
420 }
421 } else if (field.getType() == types.SINT8) {
422 decodedLog.addValue(field.getID(), rawValue);
423 } else if (field.getType() == types.SINT16) {
424 decodedLog.addValue(field.getID(), rawValue);
425 } else if (field.getType() == types.SINT32) {
426 decodedLog.addValue(field.getID(), rawValue);
427 }
428
429 }
430 return true;
431 }
432
433 private final int[] processFlagBytes(final long valueOfFlags, final int numberOfFlags) {
434 if ((numberOfFlags != 8) && (numberOfFlags != 16) && (numberOfFlags != 32)) {
435 throw new IllegalArgumentException("Basic units of computer sciene apply, embedded flags are never " + numberOfFlags + " wide!");
436
437 }
438
439 final int[] flagValues = new int[numberOfFlags];
440 int comparison = 1;
441 long remainingValueOfFlags = valueOfFlags;
442 for (int i = 0; i < numberOfFlags; i++) {
443 if ((remainingValueOfFlags % (2 * comparison)) == comparison) {
444 flagValues[i] = 1;
445 remainingValueOfFlags -= comparison;
446 }
447 comparison *= 2;
448 }
449
450 return flagValues;
451 }
452
453
454
455
456
457
458
459 private boolean checksum(final short[] packet) {
460 if (packetLength > 0) {
461 final short includedSum = packet[packetLength - 1];
462 long veryBIGsum = 0;
463 for (int x = 0; x < packetLength - 1; x++) {
464 veryBIGsum += packet[x];
465 }
466 final short calculatedSum = (short) (veryBIGsum % 256);
467 return (calculatedSum == includedSum);
468 } else {
469 return false;
470 }
471 }
472
473
474
475
476
477
478
479 private short unEscape(final short uByte) {
480 if (uByte == ESCAPED_START_BYTE) {
481 return START_BYTE;
482 } else if (uByte == ESCAPED_STOP_BYTE) {
483 return STOP_BYTE;
484 } else if (uByte == ESCAPED_ESCAPE_BYTE) {
485 return ESCAPE_BYTE;
486 } else {
487 return (short) -1;
488 }
489 }
490
491
492
493
494
495
496
497 private final short unsignedValueOf(final byte uInt8) {
498 return (short) (0xFF & uInt8);
499 }
500
501
502
503
504
505
506 @Override
507 public final String toString() {
508 return super.toString();
509 }
510
511 private String getFlagName(final LogField field, final int flagIndex) {
512 return field.getBitFieldNames()[flagIndex] + "-" + field.getID() + "-B" + flagIndex;
513 }
514 }