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", types.UINT8),
145 new LogField("spareChar", types.UINT8),
146 new LogField("coreStatusA", types.BITS8, coreStatusAFlagNames),
147 new LogField("decoderFlags", types.BITS8, decoderFlagsFlagNames),
148 new LogField("flaggableFlags", types.BITS16, flaggableFlagsNames),
149 new LogField("currentEvent", types.UINT8),
150 new LogField("syncLostWithThisID", types.UINT8),
151 new LogField("syncLostOnThisEvent", types.UINT8),
152 new LogField("syncCaughtOnThisEvent", types.UINT8),
153 new LogField("syncResetCalls", types.UINT8),
154 new LogField("primaryTeethSeen", types.UINT8),
155 new LogField("secondaryTeethSeen", types.UINT8),
156 new LogField("serialOverrunErrors", types.UINT8),
157 new LogField("serialHardwareErrors", types.UINT8),
158 new LogField("serialAndCommsCodeErrors", types.UINT8),
159 new LogField("inputEventTimeTolerance"),
160 new LogField("zsp10"),
161 new LogField("zsp9"),
162 new LogField("zsp8"),
163 new LogField("zsp7"),
164 new LogField("zsp6"),
165 new LogField("zsp5"),
166 new LogField("zsp4"),
167 new LogField("zsp3"),
168 new LogField("clockInMilliSeconds"),
169 new LogField("clock8thMSsInMillis", 8)
170 };
171
172
173
174
175
176
177
178
179 public FreeEMSBin(final String path) {
180 this(new File(path));
181 }
182
183
184
185
186
187 public FreeEMSBin(final File f) {
188 startTime = System.currentTimeMillis();
189 logFile = f;
190 startFound = false;
191 packetBuffer = new short[6000];
192 packetLength = 0;
193
194
195
196
197 final String[] headers = new String[fields.length * 32];
198 int headersPosition = 0;
199 for (int i = 0; i < fields.length; i++) {
200 if ((fields[i].getType() == types.BITS8) || (fields[i].getType() == types.BITS16) || (fields[i].getType() == types.BITS32)) {
201 for (int j = 0; j < fields[i].getBitFieldNames().length; j++) {
202 final String flagID = getFlagName(fields[i], j);
203 headers[headersPosition] = flagID;
204 headersPosition++;
205 }
206 } else {
207 headers[headersPosition] = fields[i].getID();
208 headersPosition++;
209 }
210 }
211
212 decodedLog = new GenericLog(Arrays.copyOfRange(headers, 0, headersPosition), initialLength, loadFactor);
213
214 t = new Thread(this, "FreeEMSBin Loading");
215 t.setPriority(Thread.MAX_PRIORITY);
216 t.start();
217 }
218
219
220
221
222
223
224 @Override
225 public final void run() {
226 FileInputStream fis = null;
227 BufferedInputStream bis = null;
228 try {
229
230 final byte[] readByte = new byte[1];
231 short uByte = 0;
232
233
234 int escapePairMismatches = 0;
235 int startsInsideAPacket = 0;
236 int packetsOverLength = 0;
237 int packetsUnderLength = 0;
238 int checksumMismatches = 0;
239 int packetsParsedFully = 0;
240 int packetLengthWrong = 0;
241 int strayBytesLost = 0;
242 int payloadIDWrong = 0;
243
244 startFound = false;
245 fis = new FileInputStream(logFile);
246 bis = new BufferedInputStream(fis);
247 decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADING);
248 while (bis.read(readByte) != -1) {
249 uByte = unsignedValueOf(readByte[0]);
250 if (uByte == START_BYTE) {
251 if (!startFound) {
252 startFound = true;
253 } else {
254 startsInsideAPacket++;
255 }
256 packetLength = 0;
257 } else if (startFound) {
258 if (uByte == STOP_BYTE) {
259 if (packetLength < MINIMUM_PACKET_LENGTH) {
260 packetsUnderLength++;
261 } else if (packetLength > MAXIMUM_PACKET_LENGTH) {
262 packetsOverLength++;
263 } else {
264 decodedLog.incrementPosition();
265 final short[] justThePacket = Arrays.copyOfRange(packetBuffer, 0, packetLength);
266 if (checksum(justThePacket)) {
267 if (decodeBasicLogPacket(justThePacket)) {
268 packetsParsedFully++;
269 } else {
270 packetLengthWrong++;
271 payloadIDWrong++;
272 }
273 } else {
274 checksumMismatches++;
275 }
276 }
277 startFound = false;
278 } else if (uByte == ESCAPE_BYTE) {
279 if (bis.read(readByte) != -1) {
280 uByte = unEscape(unsignedValueOf(readByte[0]));
281 if (uByte != (short) -1) {
282 packetBuffer[packetLength] = uByte;
283 packetLength++;
284 } else {
285 startFound = false;
286 escapePairMismatches++;
287 }
288 }
289 } else {
290 packetBuffer[packetLength] = uByte;
291 packetLength++;
292 }
293 } else {
294 strayBytesLost++;
295 }
296 }
297 decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADED);
298
299 System.out.println(OpenLogViewer.NEWLINE + "Binary Parsing Statistics:" + OpenLogViewer.NEWLINE);
300
301 System.out.println("EscapePairMismatches: " + escapePairMismatches + " Incremented when an escape is found but not followed by an escapee");
302 System.out.println("StartsInsideAPacket: " + startsInsideAPacket + " Incremented when a start byte is found inside a packet");
303 System.out.println("PacketsOverLength: " + packetsOverLength + " Incremented when the buffer fills up before the end");
304 System.out.println("PacketsUnderLength: " + packetsUnderLength + " Incremented when a packet is found that is too short");
305 System.out.println("ChecksumMismatches: " + checksumMismatches + " Incremented when calculated checksum did not match the received one");
306 System.out.println("PacketsParsedFully: " + packetsParsedFully + " Number of packets that matched all requirements and got into the log");
307 System.out.println("PacketLengthWrong: " + packetLengthWrong + " The length should match the ID passed in, if not, fail.");
308 System.out.println("StrayBytesLost: " + strayBytesLost + " How many bytes were not in a packet! (should be low, ie, under one packet");
309 System.out.println("PayloadIDWrong: " + payloadIDWrong + " Requests to parse packet as A when packet was of type B");
310
311 System.out.println(OpenLogViewer.NEWLINE + "Thank you for choosing FreeEMS!");
312
313 } catch (Exception e) {
314 e.printStackTrace();
315 decodedLog.setLogStatusMessage(e.getMessage());
316 } finally {
317 OpenLogViewer.getInstance().getEntireGraphingPanel().setGraphSize(decodedLog.getRecordCount());
318 decodedLog.setLogStatus(GenericLog.LogState.LOG_LOADED);
319 System.out.println("Loaded " + (decodedLog.getRecordCount() + 1) + " records in " + (System.currentTimeMillis() - startTime) + " millis!");
320
321 try {
322 if (bis != null) {
323 bis.close();
324 }
325 } catch (IOException ioe) {
326 ioe.printStackTrace();
327 System.out.println("Failed To Close BIS Stream!");
328 }
329
330 try {
331 if (fis != null) {
332 fis.close();
333 }
334 } catch (IOException ioe) {
335 ioe.printStackTrace();
336 System.out.println("Failed To Close FIS Stream!");
337 }
338 }
339 }
340
341
342
343
344
345
346
347 private boolean decodeBasicLogPacket(final short[] packet) {
348 final int HEADER_HAS_LENGTH_INDEX = 0;
349
350 final int HEADER_HAS_SEQUENCE_INDEX = 2;
351
352
353
354
355
356
357
358
359 int position = 0;
360
361 final short flags = packet[position];
362 position++;
363
364 final short payloadIdUpper = packet[position];
365 position++;
366 final short payloadIdLower = packet[position];
367 position++;
368 final int payloadId = (payloadIdUpper * 256) + payloadIdLower;
369
370 if (firstPayloadIDFound < 0) {
371 firstPayloadIDFound = payloadId;
372 } else if (payloadId != firstPayloadIDFound) {
373 return false;
374 }
375
376
377
378 final int[] flagValues = processFlagBytes(flags, 8);
379
380 if (flagValues[HEADER_HAS_SEQUENCE_INDEX] == 1) {
381 position++;
382 }
383
384 if (flagValues[HEADER_HAS_LENGTH_INDEX] == 1) {
385 position += 2;
386 }
387
388 int lengthOfFields = 0;
389 for (int i = 0; i < fields.length; i++) {
390 lengthOfFields += fields[i].getType().getWidth();
391 }
392
393
394
395 final int payloadLength = ((packet.length - position) - 1);
396 if (payloadLength != lengthOfFields) {
397 System.out.print(" Fields length is: " + lengthOfFields);
398 System.out.print(" Packet length is: " + packet.length);
399 System.out.println(" Payload length is: " + payloadLength);
400 return false;
401 }
402
403 for (int i = 0; i < fields.length; i++) {
404 final LogField field = fields[i];
405
406 int rawValue = 0;
407 for (int j = 0; j < field.getType().getWidth(); j++) {
408 rawValue = (rawValue * 256) + packet[position];
409 position++;
410 }
411
412 if ((field.getType() == types.UINT8) || (field.getType() == types.UINT16) || (field.getType() == types.UINT32)) {
413 final double scaledValue = (double) rawValue / field.getDivBy();
414 final double finalValue = scaledValue + field.getAddTo();
415 decodedLog.addValue(field.getID(), finalValue);
416 } else if ((field.getType() == types.BITS8) || (field.getType() == types.BITS16) || (field.getType() == types.BITS32)) {
417 final int[] processedFlags = processFlagBytes(rawValue, (8 * field.getType().getWidth()));
418 for (int j = 0; j < processedFlags.length; j++) {
419 final String flagID = getFlagName(field, j);
420 decodedLog.addValue(flagID, processedFlags[j]);
421 }
422 } else if (field.getType() == types.SINT8) {
423 decodedLog.addValue(field.getID(), rawValue);
424 } else if (field.getType() == types.SINT16) {
425 decodedLog.addValue(field.getID(), rawValue);
426 } else if (field.getType() == types.SINT32) {
427 decodedLog.addValue(field.getID(), rawValue);
428 }
429
430 }
431 return true;
432 }
433
434 private int[] processFlagBytes(final long valueOfFlags, final int numberOfFlags) {
435 if ((numberOfFlags != 8) && (numberOfFlags != 16) && (numberOfFlags != 32)) {
436 throw new IllegalArgumentException("Basic units of computer sciene apply, embedded flags are never " + numberOfFlags + " wide!");
437
438 }
439
440 final int[] flagValues = new int[numberOfFlags];
441 int comparison = 1;
442 long remainingValueOfFlags = valueOfFlags;
443 for (int i = 0; i < numberOfFlags; i++) {
444 if ((remainingValueOfFlags % (2 * comparison)) == comparison) {
445 flagValues[i] = 1;
446 remainingValueOfFlags -= comparison;
447 }
448 comparison *= 2;
449 }
450
451 return flagValues;
452 }
453
454
455
456
457
458
459
460 private boolean checksum(final short[] packet) {
461 if (packetLength > 0) {
462 final short includedSum = packet[packetLength - 1];
463 long veryBIGsum = 0;
464 for (int x = 0; x < packetLength - 1; x++) {
465 veryBIGsum += packet[x];
466 }
467 final short calculatedSum = (short) (veryBIGsum % 256);
468 return (calculatedSum == includedSum);
469 } else {
470 return false;
471 }
472 }
473
474
475
476
477
478
479
480 private short unEscape(final short uByte) {
481 if (uByte == ESCAPED_START_BYTE) {
482 return START_BYTE;
483 } else if (uByte == ESCAPED_STOP_BYTE) {
484 return STOP_BYTE;
485 } else if (uByte == ESCAPED_ESCAPE_BYTE) {
486 return ESCAPE_BYTE;
487 } else {
488 return (short) -1;
489 }
490 }
491
492
493
494
495
496
497
498 private short unsignedValueOf(final byte uInt8) {
499 return (short) (0xFF & uInt8);
500 }
501
502
503
504
505
506
507 @Override
508 public final String toString() {
509 return super.toString();
510 }
511
512 private String getFlagName(final LogField field, final int flagIndex) {
513 return field.getBitFieldNames()[flagIndex] + "-" + field.getID() + "-B" + flagIndex;
514 }
515 }