From a6db0254bf9bc3f4ed1058c20cf294b66d455be4 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 12 Mar 2017 04:05:42 -0400 Subject: initial commit --- Makefile | 6 ++ dslog/DseventsReader.java | 56 +++++++++++++++++ dslog/DslogReader.java | 156 ++++++++++++++++++++++++++++++++++++++++++++++ dslog/Main.java | 65 +++++++++++++++++++ dslog/Read.java | 143 ++++++++++++++++++++++++++++++++++++++++++ dslog/Util.java | 51 +++++++++++++++ 6 files changed, 477 insertions(+) create mode 100644 Makefile create mode 100644 dslog/DseventsReader.java create mode 100644 dslog/DslogReader.java create mode 100644 dslog/Main.java create mode 100644 dslog/Read.java create mode 100644 dslog/Util.java diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7813a81 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +classes = $(patsubst %.java,%,$(notdir $(wildcard dslog/*.java))) + +all: $(foreach c,$(classes),dslog/$c.class) + +%.class: %.java + javac $< diff --git a/dslog/DseventsReader.java b/dslog/DseventsReader.java new file mode 100644 index 0000000..c3c7dc5 --- /dev/null +++ b/dslog/DseventsReader.java @@ -0,0 +1,56 @@ +package dslog; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.UnsupportedOperationException; +import java.time.Duration; +import java.time.Instant; +import java.util.stream.IntStream; + +class DseventsReader { + private final InputStream reader; + + public final int version; + public final Instant startTime; + + public DseventsReader(InputStream reader) throws IOException { + this.reader = reader; + this.version = Read.i32(reader); + switch (this.version) { + case 0: + throw new UnsupportedOperationException("DSEVENTS version 0 not implemented yet"); + case 1: + case 2: + case 3: // 2016-now + this.startTime = Read.LVTimestamp(reader); + break; + default: + throw new UnsupportedOperationException("DSEVENTS file version ("+this.version+") newer than log reader (3)"); + } + } + + public static class Event { + public Instant time; + public String message; + } + + public Event readEvent() throws IOException{ + switch (version) { + case 0: + throw new UnsupportedOperationException("DSEVENTS version 0 not implemented yet"); + case 1: + case 2: + case 3: + return new Event() {{ + time = Read.LVTimestamp(reader); + message = Read.LVString(reader); + }}; + default: + throw new UnsupportedOperationException("DSEVENTS file version newer than log reader"); + } + } + + public void close() throws IOException { + reader.close(); + } +} diff --git a/dslog/DslogReader.java b/dslog/DslogReader.java new file mode 100644 index 0000000..3a3b211 --- /dev/null +++ b/dslog/DslogReader.java @@ -0,0 +1,156 @@ +package dslog; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.UnsupportedOperationException; +import java.time.Duration; +import java.time.Instant; +import java.util.stream.IntStream; + +class DslogReader { + private final InputStream reader; + + public final int version; + public final Instant startTime; + private long i = 0; + + public DslogReader(InputStream reader) throws IOException { + this.reader = reader; + this.version = Read.i32(reader); + switch (this.version) { + case 0: // ????-???? 2012 + this.startTime = Read.LVTimestamp(reader); + break; + case 1: // ????-???? 2014 + this.startTime = Read.LVTimestamp(reader); + break; + case 2: // ????-2015 + throw new UnsupportedOperationException("DSLOG version 2 not implemented yet"); + case 3: // 2016-now + this.startTime = Read.LVTimestamp(reader); + break; + default: + throw new UnsupportedOperationException("DSLOG file version ("+this.version+") newer than log reader (3)"); + } + } + + public static class Entry { + public Instant time; + + public Duration tripTime; + public short lostPackets; + public double voltageVolts; + public double cpuPct; + public boolean + robotDisable, + robotAuto, + robotTele, + dsDisable, + dsAuto, + dsTele, + watchdog, + brownout; + public double canPct; + public double signalDB; + public double bandwidthMb; + public short pdpID; + public short pdpPad; + public double pdpValues[]; + public short pdpResistance; + public short pdpVoltage; + public short pdpTemperature; + } + + /** + * @see https://www.chiefdelphi.com/forums/showthread.php?t=104689 + */ + public Entry readEntry() throws IOException{ + switch (version) { + case 0: + return new Entry() {{ + time = startTime.plusMillis(20*i++); + + /* 0 = 0 */ + /* 0+4= 4 *///tripTime = Read.f32(reader); + /* 4+4= 8 */voltageVolts = Read.f32(reader); + /* 8+4=12 */cpuPct = Read.f32(reader); + /* 12+1=13 */short bits = Read.u8(reader); + robotDisable = (bits & (1<<2)) == 0; + robotAuto = (bits & (1<<3)) == 0; + robotTele = (bits & (1<<4)) == 0; + dsDisable = (bits & (1<<5)) == 0; + dsAuto = (bits & (1<<6)) == 0; + dsTele = (bits & (1<<7)) == 0; + /* 13+1=14 */lostPackets = Read.i8(reader); + }}; + case 1: + return new Entry() {{ + time = startTime.plusMillis(20*i++); + + time = startTime.plusMillis(20*i++); + + /* 0 = 0 */ + /* 0+4= 4 *///tripTime = Read.f32(reader); + /* 4+4= 8 */voltageVolts = Read.f32(reader); + /* 8+4=12 */cpuPct = Read.f32(reader); + /* 12+1=13 */short bits = Read.u8(reader); + robotDisable = (bits & (1<<0)) == 0; + robotAuto = (bits & (1<<1)) == 0; + robotTele = (bits & (1<<2)) == 0; + dsDisable = (bits & (1<<3)) == 0; + dsAuto = (bits & (1<<4)) == 0; + dsTele = (bits & (1<<5)) == 0; + /* 13+1=14 */lostPackets = Read.i8(reader); + }}; + case 2: + return new Entry(); + case 3: + return new Entry() {{ + time = startTime.plusMillis(20*i++); + // Read the 35-byte structure + + // Read the 10 bytes of non-PDP data + + /* 0 = 0 */ + /* 0+1= 1 */tripTime = Duration.ofNanos((long)(Util.unpack(Read.u8(reader), 7,1)*1_000_000)); + /* 1+1= 2 */lostPackets = Read.u8(reader); + /* 2+2= 4 */voltageVolts = Util.unpack(Read.u16(reader), 8,8); + /* 5+1= 5 */cpuPct = Util.unpack(Read.u8(reader), 7,1); + /* 5+1= 6 */short bits = Read.u8(reader); + robotDisable = (bits & (1<<0)) == 0; + robotAuto = (bits & (1<<1)) == 0; + robotTele = (bits & (1<<2)) == 0; + dsDisable = (bits & (1<<3)) == 0; + dsAuto = (bits & (1<<4)) == 0; + dsTele = (bits & (1<<5)) == 0; + watchdog = (bits & (1<<6)) == 0; + brownout = (bits & (1<<7)) == 0; + /* 6+1= 7 */canPct = Util.unpack(Read.u8(reader), 7,1); + /* 7+1= 8 */signalDB = Util.unpack(Read.u8(reader), 7,1); + /* 8+2=10 */bandwidthMb = Util.unpack(Read.u16(reader), 8,8); + + // Read the 25 bytes of PDP data + + /* 0 = 0 */ + /* 0+1= 1 */pdpPad = Read.u8(reader); + /* 1+1= 2 */pdpID = Read.u8(reader); + pdpValues = IntStream.of(Util.reverse(Util.concat( + /* 2+5= 7 */ Read.u10x4(reader), + /* 7+5=12 */ Read.u10x4(reader), + /* 12+5=17 */ Read.u10x4(reader), + /* 17+5=22 */ Read.u10x4(reader)))) + .mapToDouble(i -> Util.unpack((short)i, 7,3)) + .toArray(); + /* 22+1=23 */pdpResistance = Read.u8(reader); + /* 23+1=24 */pdpVoltage = Read.u8(reader); + /* 24+1=25 */pdpTemperature = Read.u8(reader); + }}; + default: + throw new UnsupportedOperationException("DSLOG file version newer than log reader"); + } + } + + public void close() throws IOException { + reader.close(); + } +} diff --git a/dslog/Main.java b/dslog/Main.java new file mode 100644 index 0000000..c8c93fb --- /dev/null +++ b/dslog/Main.java @@ -0,0 +1,65 @@ +package dslog; + +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; + +class Main { + public static String line(DslogReader.Entry e) { + return + e.time+","+ + e.tripTime+","+ + e.lostPackets+","+ + e.voltageVolts+","+ + e.cpuPct+","+ + e.robotDisable+","+ + e.robotAuto+","+ + e.robotTele+","+ + e.dsDisable+","+ + e.dsAuto+","+ + e.dsTele+","+ + e.watchdog+","+ + e.brownout+","+ + e.canPct+","+ + e.signalDB+","+ + e.bandwidthMb+","+ + e.pdpID+","+ + e.pdpPad+","+ + e.pdpValues[0]+","+ + e.pdpValues[1]+","+ + e.pdpValues[2]+","+ + e.pdpValues[3]+","+ + e.pdpValues[4]+","+ + e.pdpValues[5]+","+ + e.pdpValues[6]+","+ + e.pdpValues[7]+","+ + e.pdpValues[8]+","+ + e.pdpValues[9]+","+ + e.pdpValues[10]+","+ + e.pdpValues[11]+","+ + e.pdpValues[12]+","+ + e.pdpValues[13]+","+ + e.pdpValues[14]+","+ + e.pdpValues[15]+","+ + e.pdpResistance+","+ + e.pdpVoltage+","+ + e.pdpTemperature; + } + public static void main(String[] args) throws IOException { + for (String filename : args) { + System.out.println("Filename: "+filename); + DslogReader file = new DslogReader(new FileInputStream(filename)); + System.out.println("Format Version: "+file.version); + System.out.println("Start Time: "+file.startTime); + System.out.println("time,tripTime,lost (packets),voltage (V),cpu (%),robot disable,robot auto,robot tele,ds disable,ds auto,ds tele,watchdog,brownout,CAN (%),signal (dB),bandwidth (Mb),PDP ID,PDP pad,PDP-0,PDP-1,PDP-2,PDP-3,PDP-4,PDP-5,PDP-6,PDP-7,PDP-8,PDP-9,PDP-10,PDP-11,PDP-12,PDP-13,PDP-14,PDP-15,PDP Resistance,PDP Voltage,PDP Temperature"); + for (;;) { + try { + System.out.println(line(file.readEntry())); + } catch (EOFException e) { + break; + } + } + file.close(); + } + } +} diff --git a/dslog/Read.java b/dslog/Read.java new file mode 100644 index 0000000..04537d5 --- /dev/null +++ b/dslog/Read.java @@ -0,0 +1,143 @@ +package dslog; + +import java.math.BigInteger; +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; +import java.time.Instant; + +class Read { + public static void full(InputStream in, byte b[]) throws IOException { + int n = 0; + while (n < b.length) { + int count = in.read(b, n, b.length - n); + if (count < 0) + throw new EOFException(); + n += count; + } + } + + public static short u8(InputStream in) throws IOException { + int n = in.read(); + if (n < 0) + throw new EOFException(); + return (short)n; + } + + public static byte i8(InputStream in) throws IOException { + return (byte)u8(in); + } + + + public static int u16(InputStream in) throws IOException { + byte bytes[] = new byte[2]; + full(in, bytes); + return (((bytes[0] & 0xff) << 8) + + ((bytes[1] & 0xff) << 0)); + } + + public static short i16(InputStream in) throws IOException { + return (short)u16(in); + } + + public static int i32(InputStream in) throws IOException { + byte bytes[] = new byte[4]; + full(in, bytes); + return (((bytes[0] & 0xff) << 32) + + ((bytes[1] & 0xff) << 16) + + ((bytes[2] & 0xff) << 8) + + ((bytes[3] & 0xff) << 0)); + } + + public static long u32(InputStream in) throws IOException { + byte bytes[] = new byte[4]; + full(in, bytes); + return (((long)(bytes[0] & 0xff) << 32) + + ((long)(bytes[1] & 0xff) << 16) + + ((long)(bytes[2] & 0xff) << 8) + + ((long)(bytes[3] & 0xff) << 0)); + } + + public static long i64(InputStream in) throws IOException { + byte bytes[] = new byte[8]; + full(in, bytes); + return (((long)(bytes[0] & 0xff) << 56) + + ((long)(bytes[1] & 0xff) << 48) + + ((long)(bytes[2] & 0xff) << 40) + + ((long)(bytes[3] & 0xff) << 32) + + ((long)(bytes[4] & 0xff) << 24) + + ((long)(bytes[5] & 0xff) << 16) + + ((long)(bytes[6] & 0xff) << 8) + + ((long)(bytes[7] & 0xff) << 0)); + } + + public static BigInteger u64(InputStream in) throws IOException { + byte bytes[] = new byte[8]; + full(in, bytes); + return BigInteger.valueOf(0) + .add(BigInteger.valueOf(bytes[0] & 0xff).shiftLeft(56)) + .add(BigInteger.valueOf(bytes[1] & 0xff).shiftLeft(48)) + .add(BigInteger.valueOf(bytes[2] & 0xff).shiftLeft(40)) + .add(BigInteger.valueOf(bytes[3] & 0xff).shiftLeft(32)) + .add(BigInteger.valueOf(bytes[4] & 0xff).shiftLeft(24)) + .add(BigInteger.valueOf(bytes[5] & 0xff).shiftLeft(16)) + .add(BigInteger.valueOf(bytes[6] & 0xff).shiftLeft( 8)) + .add(BigInteger.valueOf(bytes[7] & 0xff).shiftLeft( 0)); + } + + public static float f32(InputStream in) throws IOException { + return Float.intBitsToFloat(i32(in)); + } + + public static double f64(InputStream in) throws IOException { + return Double.longBitsToDouble(i64(in)); + } + + public static int[] u10x4(InputStream in) throws IOException { + byte bytes[] = new byte[5]; + full(in, bytes); + return new int[]{ + ((bytes[0] & 0b1111_1111) << 2)+ + ((bytes[1] & 0b1100_0000) >> 6), + ((bytes[1] & 0b0011_1111) << 4)+ + ((bytes[2] & 0b1111_0000) >> 4), + ((bytes[2] & 0b0000_1111) << 6)+ + ((bytes[3] & 0b1111_1100) >> 2), + ((bytes[3] & 0b0000_1111) << 8)+ + ((bytes[4] & 0b1111_1111) >> 0), + }; + } + + public static final Instant LABVIEW_EPOCH = Instant.parse("1904-01-01T00:00:00.00Z"); + + /** + * @bug LabVIEW timestamps have greater precision + * (sub-attosecond) than Java time.Instant (nanosecond) + * or Java util.Date (millisecond), so converting to a + * native Java timestamp necessarily loses some + * precision. + * + * @see http://www.ni.com/tutorial/7900/en/ + */ + public static Instant LVTimestamp(InputStream in) throws IOException { + // seconds since the epoch 01/01/1904 00:00:00.00 UTC + // (using the Gregorian calendar and ignoring leap + // seconds), + long secs = i64(in); + // positive fractions of a second; 2¯⁶⁴ths of a second + BigInteger frac = u64(in); + // To get from (2¯⁶⁴s to ns, we need to divide by + // precisely 18446744073.709551616 (=10¯⁹/2¯⁶⁴)... but + // rounding up already gives us sub-nanosecond + // precision, so don't worry about the .709... + long nanos = frac.divide(BigInteger.valueOf(18446744074L)).longValue(); + return LABVIEW_EPOCH.plusSeconds(secs).plusNanos(nanos); + } + + public static String LVString(InputStream in) throws IOException { + int len = i32(in); + byte[] bytes = new byte[len]; + full(in, bytes); + return new String(bytes); + } +} diff --git a/dslog/Util.java b/dslog/Util.java new file mode 100644 index 0000000..b430af9 --- /dev/null +++ b/dslog/Util.java @@ -0,0 +1,51 @@ +package dslog; + +import java.io.InputStream; + +class Util { + public static int[] concat(int[]... lists) { + int len = 0; + for (int[] list : lists) { + len += list.length; + } + int[] ret = new int[len]; + int n = 0; + for (int[] list : lists) { + System.arraycopy(list, 0, ret, n, list.length); + n += list.length; + } + return ret; + } + + public static int[] reverse(int[] list) { + int mid = list.length / 2; + int tmp; + for (int i = 0; i < mid; i++) { + tmp = list[i]; + list[i] = list[list.length - i - 1]; + list[list.length - i - 1] = tmp; + } + return list; + } + + public static double unpack(short val, int whole, int frac) { + switch (whole + frac) { + case 8: + case 10: + break; + default: + throw new IllegalArgumentException(whole+"+"+frac+" != 8|10"); + } + return val / Math.pow(2, frac); + } + + public static double unpack(int val, int whole, int frac) { + switch (whole + frac) { + case 16: + break; + default: + throw new IllegalArgumentException(whole+"+"+frac+" != 16"); + } + return val / Math.pow(2, frac); + } +} -- cgit v1.2.3