package dslog; import java.io.BufferedInputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.time.Duration; import java.time.Instant; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.stream.IntStream; public class DslogReader implements Closeable, Iterator { 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; } /*============================================================*\ || Primitive IO || \*============================================================*/ private final InputStream reader; public DslogReader(InputStream reader) throws IOException { if (!reader.markSupported()) { reader = new BufferedInputStream(reader); } this.reader = reader; } private boolean atEOF() throws IOException { reader.mark(2); boolean eof = (reader.read() < 0); reader.reset(); return eof; } /*============================================================*\ || Parsing || \*============================================================*/ private int m_version; private boolean m_haveVersion = false; public int version() throws IOException { if (!m_haveVersion) { m_version = Read.i32(reader); m_haveVersion = true; } return m_version; } private Instant m_startTime; public Instant startTime() throws IOException { if (m_startTime == null) { switch (version()) { case 0: // ????-???? 2012 m_startTime = Read.LVTimestamp(reader); break; case 1: // ????-???? 2014 m_startTime = Read.LVTimestamp(reader); break; case 2: // ????-2015 throw new UnsupportedVersionException("DSLOG (TODO)", 2); case 3: // 2016-now m_startTime = Read.LVTimestamp(reader); break; default: throw new UnsupportedVersionException("DSLOG", version(), 3); } } return m_startTime; } private long i = 0; public Entry readEntry() throws IOException { if (atEOF()) { return null; } switch (version()) { case 0: return new Entry() {{ time = startTime().plusMillis(20*i++); /* 0 = 0 */ /* 0+4= 4 */tripTime = Duration.ofNanos((long)(Read.f32(reader)*1000)); /* 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++); /* 0 = 0 */ /* 0+4= 4 */tripTime = Duration.ofNanos((long)(Read.f32(reader)*1000)); /* 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: throw new UnsupportedVersionException("DSLOG (TODO)", 2); 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 UnsupportedVersionException("DSLOG", version(), 3); } } /*============================================================*\ || Interfaces || \*============================================================*/ private IOException m_err; private Entry m_next; public boolean hasNext() { if (m_next == null) { try { m_next = readEntry(); } catch (IOException e) { m_err = e; } } return m_next != null; } public Entry next() { if (!hasNext()) { throw new NoSuchElementException(); } return m_next; } public IOException err() { return m_err; } public void close() throws IOException { reader.close(); } }