[wpiutil] Add high speed data logging

This commit is contained in:
Peter Johnson
2020-03-21 20:43:34 -07:00
parent 5a89575b3a
commit 9b500df0d9
31 changed files with 4963 additions and 19 deletions

View File

@@ -7,7 +7,7 @@ package edu.wpi.first.util;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
public final class WPIUtilJNI {
public class WPIUtilJNI {
static boolean libraryLoaded = false;
static RuntimeLoader<WPIUtilJNI> loader = null;

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log array of boolean values. */
public class BooleanArrayLogEntry extends DataLogEntry {
public static final String kDataType = "boolean[]";
public BooleanArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public BooleanArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public BooleanArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public BooleanArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(boolean[] value, long timestamp) {
m_log.appendBooleanArray(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(boolean[] value) {
m_log.appendBooleanArray(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log boolean values. */
public class BooleanLogEntry extends DataLogEntry {
public static final String kDataType = "boolean";
public BooleanLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public BooleanLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public BooleanLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public BooleanLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(boolean value, long timestamp) {
m_log.appendBoolean(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(boolean value) {
m_log.appendBoolean(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,242 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/**
* A data log. The log file is created immediately upon construction with a temporary filename. The
* file may be renamed at any time using the setFilename() function.
*
* <p>The data log is periodically flushed to disk. It can also be explicitly flushed to disk by
* using the flush() function.
*/
@SuppressWarnings({"PMD.TooManyMethods", "PMD.ExcessivePublicCount"})
public final class DataLog implements AutoCloseable {
/**
* Construct a new Data Log. The log will be initially created with a temporary filename.
*
* @param dir directory to store the log
* @param filename filename to use; if none provided, a random filename is generated of the form
* "wpilog_{}.wpilog"
* @param period time between automatic flushes to disk, in seconds; this is a time/storage
* tradeoff
* @param extraHeader extra header data
*/
public DataLog(String dir, String filename, double period, String extraHeader) {
m_impl = DataLogJNI.create(dir, filename, period, extraHeader);
}
/**
* Construct a new Data Log. The log will be initially created with a temporary filename.
*
* @param dir directory to store the log
* @param filename filename to use; if none provided, a random filename is generated of the form
* "wpilog_{}.wpilog"
* @param period time between automatic flushes to disk, in seconds; this is a time/storage
* tradeoff
*/
public DataLog(String dir, String filename, double period) {
this(dir, filename, period, "");
}
/**
* Construct a new Data Log. The log will be initially created with a temporary filename.
*
* @param dir directory to store the log
* @param filename filename to use; if none provided, a random filename is generated of the form
* "wpilog_{}.wpilog"
*/
public DataLog(String dir, String filename) {
this(dir, filename, 0.25);
}
/**
* Construct a new Data Log. The log will be initially created with a temporary filename.
*
* @param dir directory to store the log
*/
public DataLog(String dir) {
this(dir, "", 0.25);
}
/** Construct a new Data Log. The log will be initially created with a temporary filename. */
public DataLog() {
this("");
}
/**
* Change log filename.
*
* @param filename filename
*/
public void setFilename(String filename) {
DataLogJNI.setFilename(m_impl, filename);
}
/** Explicitly flushes the log data to disk. */
public void flush() {
DataLogJNI.flush(m_impl);
}
/**
* Pauses appending of data records to the log. While paused, no data records are saved (e.g.
* AppendX is a no-op). Has no effect on entry starts / finishes / metadata changes.
*/
public void pause() {
DataLogJNI.pause(m_impl);
}
/** Resumes appending of data records to the log. */
public void resume() {
DataLogJNI.resume(m_impl);
}
/**
* Start an entry. Duplicate names are allowed (with the same type), and result in the same index
* being returned (start/finish are reference counted). A duplicate name with a different type
* will result in an error message being printed to the console and 0 being returned (which will
* be ignored by the append functions).
*
* @param name Name
* @param type Data type
* @param metadata Initial metadata (e.g. data properties)
* @param timestamp Time stamp (may be 0 to indicate now)
* @return Entry index
*/
public int start(String name, String type, String metadata, long timestamp) {
return DataLogJNI.start(m_impl, name, type, metadata, timestamp);
}
/**
* Start an entry. Duplicate names are allowed (with the same type), and result in the same index
* being returned (start/finish are reference counted). A duplicate name with a different type
* will result in an error message being printed to the console and 0 being returned (which will
* be ignored by the append functions).
*
* @param name Name
* @param type Data type
* @param metadata Initial metadata (e.g. data properties)
* @return Entry index
*/
public int start(String name, String type, String metadata) {
return start(name, type, metadata, 0);
}
/**
* Start an entry. Duplicate names are allowed (with the same type), and result in the same index
* being returned (start/finish are reference counted). A duplicate name with a different type
* will result in an error message being printed to the console and 0 being returned (which will
* be ignored by the append functions).
*
* @param name Name
* @param type Data type
* @return Entry index
*/
public int start(String name, String type) {
return start(name, type, "");
}
/**
* Finish an entry.
*
* @param entry Entry index
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void finish(int entry, long timestamp) {
DataLogJNI.finish(m_impl, entry, timestamp);
}
/**
* Finish an entry.
*
* @param entry Entry index
*/
public void finish(int entry) {
finish(entry, 0);
}
/**
* Updates the metadata for an entry.
*
* @param entry Entry index
* @param metadata New metadata for the entry
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void setMetadata(int entry, String metadata, long timestamp) {
DataLogJNI.setMetadata(m_impl, entry, metadata, timestamp);
}
/**
* Updates the metadata for an entry.
*
* @param entry Entry index
* @param metadata New metadata for the entry
*/
public void setMetadata(int entry, String metadata) {
setMetadata(entry, metadata, 0);
}
/**
* Appends a record to the log.
*
* @param entry Entry index, as returned by Start()
* @param data Data to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void appendRaw(int entry, byte[] data, long timestamp) {
DataLogJNI.appendRaw(m_impl, entry, data, timestamp);
}
@Override
public void close() {
DataLogJNI.close(m_impl);
m_impl = 0;
}
public void appendBoolean(int entry, boolean value, long timestamp) {
DataLogJNI.appendBoolean(m_impl, entry, value, timestamp);
}
public void appendInteger(int entry, long value, long timestamp) {
DataLogJNI.appendInteger(m_impl, entry, value, timestamp);
}
public void appendFloat(int entry, float value, long timestamp) {
DataLogJNI.appendFloat(m_impl, entry, value, timestamp);
}
public void appendDouble(int entry, double value, long timestamp) {
DataLogJNI.appendDouble(m_impl, entry, value, timestamp);
}
public void appendString(int entry, String value, long timestamp) {
DataLogJNI.appendString(m_impl, entry, value, timestamp);
}
public void appendBooleanArray(int entry, boolean[] arr, long timestamp) {
DataLogJNI.appendBooleanArray(m_impl, entry, arr, timestamp);
}
public void appendIntegerArray(int entry, long[] arr, long timestamp) {
DataLogJNI.appendIntegerArray(m_impl, entry, arr, timestamp);
}
public void appendFloatArray(int entry, float[] arr, long timestamp) {
DataLogJNI.appendFloatArray(m_impl, entry, arr, timestamp);
}
public void appendDoubleArray(int entry, double[] arr, long timestamp) {
DataLogJNI.appendDoubleArray(m_impl, entry, arr, timestamp);
}
public void appendStringArray(int entry, String[] arr, long timestamp) {
DataLogJNI.appendStringArray(m_impl, entry, arr, timestamp);
}
public long getImpl() {
return m_impl;
}
private long m_impl;
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log entry base class. */
public class DataLogEntry {
protected DataLogEntry(DataLog log, String name, String type, String metadata, long timestamp) {
m_log = log;
m_entry = log.start(name, type, metadata, timestamp);
}
protected DataLogEntry(DataLog log, String name, String type, String metadata) {
this(log, name, type, metadata, 0);
}
protected DataLogEntry(DataLog log, String name, String type) {
this(log, name, type, "");
}
/**
* Updates the metadata for the entry.
*
* @param metadata New metadata for the entry
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void setMetadata(String metadata, long timestamp) {
m_log.setMetadata(m_entry, metadata, timestamp);
}
/**
* Updates the metadata for the entry.
*
* @param metadata New metadata for the entry
*/
public void setMetadata(String metadata) {
setMetadata(metadata, 0);
}
/**
* Finishes the entry.
*
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void finish(long timestamp) {
m_log.finish(m_entry, timestamp);
}
/** Finishes the entry. */
public void finish() {
finish(0);
}
protected final DataLog m_log;
protected final int m_entry;
}

View File

@@ -0,0 +1,46 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
/** DataLogReader iterator. */
public class DataLogIterator implements Iterator<DataLogRecord> {
DataLogIterator(DataLogReader reader, int pos) {
m_reader = reader;
m_pos = pos;
}
@Override
public void forEachRemaining(Consumer<? super DataLogRecord> action) {
int size = m_reader.size();
for (; m_pos < size; m_pos = m_reader.getNextRecord(m_pos)) {
DataLogRecord record;
try {
record = m_reader.getRecord(m_pos);
} catch (NoSuchElementException ex) {
break;
}
action.accept(record);
}
}
@Override
public boolean hasNext() {
return (m_pos + 16) <= m_reader.size();
}
@Override
public DataLogRecord next() {
DataLogRecord record = m_reader.getRecord(m_pos);
m_pos = m_reader.getNextRecord(m_pos);
return record;
}
private final DataLogReader m_reader;
private int m_pos;
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
import edu.wpi.first.util.WPIUtilJNI;
public class DataLogJNI extends WPIUtilJNI {
static native long create(String dir, String filename, double period, String extraHeader);
static native void setFilename(long impl, String filename);
static native void flush(long impl);
static native void pause(long impl);
static native void resume(long impl);
static native int start(long impl, String name, String type, String metadata, long timestamp);
static native void finish(long impl, int entry, long timestamp);
static native void setMetadata(long impl, int entry, String metadata, long timestamp);
static native void close(long impl);
static native void appendRaw(long impl, int entry, byte[] data, long timestamp);
static native void appendBoolean(long impl, int entry, boolean value, long timestamp);
static native void appendInteger(long impl, int entry, long value, long timestamp);
static native void appendFloat(long impl, int entry, float value, long timestamp);
static native void appendDouble(long impl, int entry, double value, long timestamp);
static native void appendString(long impl, int entry, String value, long timestamp);
static native void appendBooleanArray(long impl, int entry, boolean[] value, long timestamp);
static native void appendIntegerArray(long impl, int entry, long[] value, long timestamp);
static native void appendFloatArray(long impl, int entry, float[] value, long timestamp);
static native void appendDoubleArray(long impl, int entry, double[] value, long timestamp);
static native void appendStringArray(long impl, int entry, String[] value, long timestamp);
}

View File

@@ -0,0 +1,157 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
/** Data log reader (reads logs written by the DataLog class). */
public class DataLogReader implements Iterable<DataLogRecord> {
/**
* Constructs from a byte buffer.
*
* @param buffer byte buffer
*/
public DataLogReader(ByteBuffer buffer) {
m_buf = buffer;
m_buf.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Constructs from a file.
*
* @param filename filename
* @throws IOException if could not open/read file
*/
public DataLogReader(String filename) throws IOException {
RandomAccessFile f = new RandomAccessFile(filename, "r");
FileChannel channel = f.getChannel();
MappedByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
m_buf = buf;
m_buf.order(ByteOrder.LITTLE_ENDIAN);
channel.close();
f.close();
}
/**
* Returns true if the data log is valid (e.g. has a valid header).
*
* @return True if valid, false otherwise
*/
public boolean isValid() {
return m_buf.remaining() >= 12
&& m_buf.get(0) == 'W'
&& m_buf.get(1) == 'P'
&& m_buf.get(2) == 'I'
&& m_buf.get(3) == 'L'
&& m_buf.get(4) == 'O'
&& m_buf.get(5) == 'G'
&& m_buf.getShort(6) >= 0x0100;
}
/**
* Gets the data log version. Returns 0 if data log is invalid.
*
* @return Version number; most significant byte is major, least significant is minor (so version
* 1.0 will be 0x0100)
*/
public short getVersion() {
if (m_buf.remaining() < 12) {
return 0;
}
return m_buf.getShort(6);
}
/**
* Gets the extra header data.
*
* @return Extra header data
*/
public String getExtraHeader() {
ByteBuffer buf = m_buf.duplicate();
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.position(8);
int size = buf.getInt();
byte[] arr = new byte[size];
buf.get(arr);
return new String(arr, StandardCharsets.UTF_8);
}
@Override
public void forEach(Consumer<? super DataLogRecord> action) {
int size = m_buf.remaining();
for (int pos = 12 + m_buf.getInt(8); pos < size; pos = getNextRecord(pos)) {
DataLogRecord record;
try {
record = getRecord(pos);
} catch (NoSuchElementException ex) {
break;
}
action.accept(record);
}
}
@Override
public DataLogIterator iterator() {
return new DataLogIterator(this, 12 + m_buf.getInt(8));
}
private long readVarInt(int pos, int len) {
long val = 0;
for (int i = 0; i < len; i++) {
val |= ((long) (m_buf.get(pos + i) & 0xff)) << (i * 8);
}
return val;
}
@SuppressWarnings("PMD.PreserveStackTrace")
DataLogRecord getRecord(int pos) {
try {
int lenbyte = m_buf.get(pos) & 0xff;
int entryLen = (lenbyte & 0x3) + 1;
int sizeLen = ((lenbyte >> 2) & 0x3) + 1;
int timestampLen = ((lenbyte >> 4) & 0x7) + 1;
int headerLen = 1 + entryLen + sizeLen + timestampLen;
int entry = (int) readVarInt(pos + 1, entryLen);
int size = (int) readVarInt(pos + 1 + entryLen, sizeLen);
long timestamp = readVarInt(pos + 1 + entryLen + sizeLen, timestampLen);
// build a slice of the data contents
ByteBuffer data = m_buf.duplicate();
data.position(pos + headerLen);
data.limit(pos + headerLen + size);
return new DataLogRecord(entry, timestamp, data.slice());
} catch (BufferUnderflowException | IndexOutOfBoundsException ex) {
throw new NoSuchElementException();
}
}
int getNextRecord(int pos) {
int lenbyte = m_buf.get(pos) & 0xff;
int entryLen = (lenbyte & 0x3) + 1;
int sizeLen = ((lenbyte >> 2) & 0x3) + 1;
int timestampLen = ((lenbyte >> 4) & 0x7) + 1;
int headerLen = 1 + entryLen + sizeLen + timestampLen;
int size = 0;
for (int i = 0; i < sizeLen; i++) {
size |= ((int) (m_buf.get(pos + 1 + entryLen + i) & 0xff)) << (i * 8);
}
return pos + headerLen + size;
}
int size() {
return m_buf.remaining();
}
private final ByteBuffer m_buf;
}

View File

@@ -0,0 +1,435 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.LongBuffer;
import java.nio.charset.StandardCharsets;
import java.util.InputMismatchException;
/**
* A record in the data log. May represent either a control record (entry == 0) or a data record.
* Used only for reading (e.g. with DataLogReader).
*/
@SuppressWarnings("PMD.PreserveStackTrace")
public class DataLogRecord {
private static final int kControlStart = 0;
private static final int kControlFinish = 1;
private static final int kControlSetMetadata = 2;
DataLogRecord(int entry, long timestamp, ByteBuffer data) {
m_entry = entry;
m_timestamp = timestamp;
m_data = data;
m_data.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Gets the entry ID.
*
* @return entry ID
*/
public int getEntry() {
return m_entry;
}
/**
* Gets the record timestamp.
*
* @return Timestamp, in integer microseconds
*/
public long getTimestamp() {
return m_timestamp;
}
/**
* Gets the size of the raw data.
*
* @return size
*/
public int getSize() {
return m_data.remaining();
}
/**
* Gets the raw data. Use the GetX functions to decode based on the data type in the entry's start
* record.
*
* @return byte array
*/
public byte[] getRaw() {
ByteBuffer buf = getRawBuffer();
byte[] arr = new byte[buf.remaining()];
buf.get(arr);
return arr;
}
/**
* Gets the raw data. Use the GetX functions to decode based on the data type in the entry's start
* record.
*
* @return byte buffer
*/
public ByteBuffer getRawBuffer() {
ByteBuffer buf = m_data.duplicate();
buf.order(ByteOrder.LITTLE_ENDIAN);
return buf;
}
/**
* Returns true if the record is a control record.
*
* @return True if control record, false if normal data record.
*/
public boolean isControl() {
return m_entry == 0;
}
/**
* Returns true if the record is a start control record. Use GetStartData() to decode the
* contents.
*
* @return True if start control record, false otherwise.
*/
public boolean isStart() {
return m_entry == 0 && m_data.remaining() >= 17 && m_data.get(0) == kControlStart;
}
/**
* Returns true if the record is a finish control record. Use GetFinishEntry() to decode the
* contents.
*
* @return True if finish control record, false otherwise.
*/
public boolean isFinish() {
return m_entry == 0 && m_data.remaining() == 5 && m_data.get(0) == kControlFinish;
}
/**
* Returns true if the record is a set metadata control record. Use GetSetMetadataData() to decode
* the contents.
*
* @return True if set metadata control record, false otherwise.
*/
public boolean isSetMetadata() {
return m_entry == 0 && m_data.remaining() >= 9 && m_data.get(0) == kControlSetMetadata;
}
/**
* Data contained in a start control record as created by DataLog.start() when writing the log.
* This can be read by calling getStartData().
*/
public static class StartRecordData {
StartRecordData(int entry, String name, String type, String metadata) {
this.entry = entry;
this.name = name;
this.type = type;
this.metadata = metadata;
}
/** Entry ID; this will be used for this entry in future records. */
@SuppressWarnings("MemberName")
public final int entry;
/** Entry name. */
@SuppressWarnings("MemberName")
public final String name;
/** Type of the stored data for this entry, as a string, e.g. "double". */
@SuppressWarnings("MemberName")
public final String type;
/** Initial metadata. */
@SuppressWarnings("MemberName")
public final String metadata;
}
/**
* Decodes a start control record.
*
* @return start record decoded data
* @throws InputMismatchException on error
*/
public StartRecordData getStartData() {
if (!isStart()) {
throw new InputMismatchException("not a start record");
}
ByteBuffer buf = getRawBuffer();
buf.position(1); // skip over control type
int entry = buf.getInt();
String name = readInnerString(buf);
String type = readInnerString(buf);
String metadata = readInnerString(buf);
return new StartRecordData(entry, name, type, metadata);
}
/**
* Data contained in a set metadata control record as created by DataLog.setMetadata(). This can
* be read by calling getSetMetadataData().
*/
public static class MetadataRecordData {
MetadataRecordData(int entry, String metadata) {
this.entry = entry;
this.metadata = metadata;
}
/** Entry ID. */
@SuppressWarnings("MemberName")
public final int entry;
/** New metadata for the entry. */
@SuppressWarnings("MemberName")
public final String metadata;
}
/**
* Decodes a finish control record.
*
* @return finish record entry ID
* @throws InputMismatchException on error
*/
public int getFinishEntry() {
if (!isFinish()) {
throw new InputMismatchException("not a finish record");
}
return m_data.getInt(1);
}
/**
* Decodes a set metadata control record.
*
* @return set metadata record decoded data
* @throws InputMismatchException on error
*/
public MetadataRecordData getSetMetadataData() {
if (!isSetMetadata()) {
throw new InputMismatchException("not a set metadata record");
}
ByteBuffer buf = getRawBuffer();
buf.position(1); // skip over control type
int entry = buf.getInt();
String metadata = readInnerString(buf);
return new MetadataRecordData(entry, metadata);
}
/**
* Decodes a data record as a boolean. Note if the data type (as indicated in the corresponding
* start control record for this entry) is not "boolean", invalid results may be returned.
*
* @return boolean value
* @throws InputMismatchException on error
*/
public boolean getBoolean() {
try {
return m_data.get(0) != 0;
} catch (IndexOutOfBoundsException ex) {
throw new InputMismatchException();
}
}
/**
* Decodes a data record as an integer. Note if the data type (as indicated in the corresponding
* start control record for this entry) is not "int64", invalid results may be returned.
*
* @return integer value
* @throws InputMismatchException on error
*/
public long getInteger() {
try {
return m_data.getLong(0);
} catch (BufferUnderflowException | IndexOutOfBoundsException ex) {
throw new InputMismatchException();
}
}
/**
* Decodes a data record as a float. Note if the data type (as indicated in the corresponding
* start control record for this entry) is not "float", invalid results may be returned.
*
* @return float value
* @throws InputMismatchException on error
*/
public float getFloat() {
try {
return m_data.getFloat(0);
} catch (BufferUnderflowException | IndexOutOfBoundsException ex) {
throw new InputMismatchException();
}
}
/**
* Decodes a data record as a double. Note if the data type (as indicated in the corresponding
* start control record for this entry) is not "double", invalid results may be returned.
*
* @return double value
* @throws InputMismatchException on error
*/
public double getDouble() {
try {
return m_data.getDouble(0);
} catch (BufferUnderflowException | IndexOutOfBoundsException ex) {
throw new InputMismatchException();
}
}
/**
* Decodes a data record as a string. Note if the data type (as indicated in the corresponding
* start control record for this entry) is not "string", invalid results may be returned.
*
* @return string value
*/
public String getString() {
return new String(getRaw(), StandardCharsets.UTF_8);
}
/**
* Decodes a data record as a boolean array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "boolean[]", invalid results may be
* returned.
*
* @return boolean array
*/
public boolean[] getBooleanArray() {
boolean[] arr = new boolean[m_data.remaining()];
for (int i = 0; i < m_data.remaining(); i++) {
arr[i] = m_data.get(i) != 0;
}
return arr;
}
/**
* Decodes a data record as an integer array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "int64[]", invalid results may be
* returned.
*
* @return integer array
* @throws InputMismatchException on error
*/
public long[] getIntegerArray() {
LongBuffer buf = getIntegerBuffer();
long[] arr = new long[buf.remaining()];
buf.get(arr);
return arr;
}
/**
* Decodes a data record as an integer array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "int64[]", invalid results may be
* returned.
*
* @return integer buffer
* @throws InputMismatchException on error
*/
public LongBuffer getIntegerBuffer() {
if ((m_data.limit() % 8) != 0) {
throw new InputMismatchException("data size is not a multiple of 8");
}
return m_data.asLongBuffer();
}
/**
* Decodes a data record as a float array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "float[]", invalid results may be
* returned.
*
* @return float array
* @throws InputMismatchException on error
*/
public float[] getFloatArray() {
FloatBuffer buf = getFloatBuffer();
float[] arr = new float[buf.remaining()];
buf.get(arr);
return arr;
}
/**
* Decodes a data record as a float array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "float[]", invalid results may be
* returned.
*
* @return float buffer
* @throws InputMismatchException on error
*/
public FloatBuffer getFloatBuffer() {
if ((m_data.limit() % 4) != 0) {
throw new InputMismatchException("data size is not a multiple of 4");
}
return m_data.asFloatBuffer();
}
/**
* Decodes a data record as a double array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "double[]", invalid results may be
* returned.
*
* @return double array
* @throws InputMismatchException on error
*/
public double[] getDoubleArray() {
DoubleBuffer buf = getDoubleBuffer();
double[] arr = new double[buf.remaining()];
buf.get(arr);
return arr;
}
/**
* Decodes a data record as a double array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "double[]", invalid results may be
* returned.
*
* @return double buffer
* @throws InputMismatchException on error
*/
public DoubleBuffer getDoubleBuffer() {
if ((m_data.limit() % 8) != 0) {
throw new InputMismatchException("data size is not a multiple of 8");
}
return m_data.asDoubleBuffer();
}
/**
* Decodes a data record as a string array. Note if the data type (as indicated in the
* corresponding start control record for this entry) is not "string[]", invalid results may be
* returned.
*
* @return string array
* @throws InputMismatchException on error
*/
public String[] getStringArray() {
ByteBuffer buf = getRawBuffer();
try {
int size = buf.getInt();
// sanity check size
if (size > (buf.remaining() / 4)) {
throw new InputMismatchException("invalid size");
}
String[] arr = new String[size];
for (int i = 0; i < size; i++) {
arr[i] = readInnerString(buf);
}
return arr;
} catch (BufferUnderflowException | IndexOutOfBoundsException ex) {
throw new InputMismatchException();
}
}
private String readInnerString(ByteBuffer buf) {
int size = buf.getInt();
if (size > buf.remaining()) {
throw new InputMismatchException("invalid string size");
}
byte[] arr = new byte[size];
buf.get(arr);
return new String(arr, StandardCharsets.UTF_8);
}
private final int m_entry;
private final long m_timestamp;
private final ByteBuffer m_data;
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log array of double values. */
public class DoubleArrayLogEntry extends DataLogEntry {
public static final String kDataType = "double[]";
public DoubleArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public DoubleArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public DoubleArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public DoubleArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(double[] value, long timestamp) {
m_log.appendDoubleArray(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(double[] value) {
m_log.appendDoubleArray(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log double values. */
public class DoubleLogEntry extends DataLogEntry {
public static final String kDataType = "double";
public DoubleLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public DoubleLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public DoubleLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public DoubleLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(double value, long timestamp) {
m_log.appendDouble(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(double value) {
m_log.appendDouble(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log array of float values. */
public class FloatArrayLogEntry extends DataLogEntry {
public static final String kDataType = "float[]";
public FloatArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public FloatArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public FloatArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public FloatArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(float[] value, long timestamp) {
m_log.appendFloatArray(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(float[] value) {
m_log.appendFloatArray(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log float values. */
public class FloatLogEntry extends DataLogEntry {
public static final String kDataType = "float";
public FloatLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public FloatLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public FloatLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public FloatLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(float value, long timestamp) {
m_log.appendFloat(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(float value) {
m_log.appendFloat(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log array of integer values. */
public class IntegerArrayLogEntry extends DataLogEntry {
public static final String kDataType = "int64[]";
public IntegerArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public IntegerArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public IntegerArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public IntegerArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(long[] value, long timestamp) {
m_log.appendIntegerArray(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(long[] value) {
m_log.appendIntegerArray(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log integer values. */
public class IntegerLogEntry extends DataLogEntry {
public static final String kDataType = "int64";
public IntegerLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public IntegerLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public IntegerLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public IntegerLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(long value, long timestamp) {
m_log.appendInteger(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(long value) {
m_log.appendInteger(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log raw byte array values. */
public class RawLogEntry extends DataLogEntry {
public static final String kDataType = "raw";
public RawLogEntry(DataLog log, String name, String metadata, String type, long timestamp) {
super(log, name, type, metadata, timestamp);
}
public RawLogEntry(DataLog log, String name, String metadata, String type) {
this(log, name, metadata, type, 0);
}
public RawLogEntry(DataLog log, String name, String metadata, long timestamp) {
this(log, name, metadata, kDataType, timestamp);
}
public RawLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public RawLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public RawLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(byte[] value, long timestamp) {
m_log.appendRaw(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(byte[] value) {
m_log.appendRaw(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log array of string values. */
public class StringArrayLogEntry extends DataLogEntry {
public static final String kDataType = "string[]";
public StringArrayLogEntry(DataLog log, String name, String metadata, long timestamp) {
super(log, name, kDataType, metadata, timestamp);
}
public StringArrayLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public StringArrayLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public StringArrayLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(String[] value, long timestamp) {
m_log.appendStringArray(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(String[] value) {
m_log.appendStringArray(m_entry, value, 0);
}
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.datalog;
/** Log string values. */
public class StringLogEntry extends DataLogEntry {
public static final String kDataType = "string";
public StringLogEntry(DataLog log, String name, String metadata, String type, long timestamp) {
super(log, name, type, metadata, timestamp);
}
public StringLogEntry(DataLog log, String name, String metadata, String type) {
this(log, name, metadata, type, 0);
}
public StringLogEntry(DataLog log, String name, String metadata, long timestamp) {
this(log, name, metadata, kDataType, timestamp);
}
public StringLogEntry(DataLog log, String name, String metadata) {
this(log, name, metadata, 0);
}
public StringLogEntry(DataLog log, String name, long timestamp) {
this(log, name, "", timestamp);
}
public StringLogEntry(DataLog log, String name) {
this(log, name, 0);
}
/**
* Appends a record to the log.
*
* @param value Value to record
* @param timestamp Time stamp (may be 0 to indicate now)
*/
public void append(String value, long timestamp) {
m_log.appendString(m_entry, value, timestamp);
}
/**
* Appends a record to the log.
*
* @param value Value to record
*/
public void append(String value) {
m_log.appendString(m_entry, value, 0);
}
}