Skip to content

Commit

Permalink
[wpiutil] DataLog: Add last value and change detection (wpilibsuite#6674
Browse files Browse the repository at this point in the history
)

Update() checks/updates the last value and appends only if changed.
GetLastValue() gets the last value.

Also add OutputStream support to Java DataLogWriter.
  • Loading branch information
PeterJohnson authored Jul 21, 2024
1 parent 57205c8 commit 4c7fe73
Show file tree
Hide file tree
Showing 23 changed files with 3,398 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package edu.wpi.first.util.datalog;

import java.util.Arrays;

/** Log array of boolean values. */
public class BooleanArrayLogEntry extends DataLogEntry {
/** The data type for boolean array values. */
Expand Down Expand Up @@ -71,4 +73,79 @@ public void append(boolean[] value, long timestamp) {
public void append(boolean[] value) {
m_log.appendBooleanArray(m_entry, value, 0);
}

/**
* Updates the last value and appends a record to the log if it has changed.
*
* <p>Note: the last value is local to this class instance; using update() with two instances
* pointing to the same underlying log entry name will likely result in unexpected results.
*
* @param value Value to record
* @param timestamp Time stamp (0 to indicate now)
*/
public synchronized void update(boolean[] value, long timestamp) {
if (!equalsLast(value)) {
copyToLast(value);
append(value, timestamp);
}
}

/**
* Updates the last value and appends a record to the log if it has changed.
*
* <p>Note: the last value is local to this class instance; using update() with two instances
* pointing to the same underlying log entry name will likely result in unexpected results.
*
* @param value Value to record
*/
public void update(boolean[] value) {
update(value, 0);
}

/**
* Gets whether there is a last value.
*
* <p>Note: the last value is local to this class instance and updated only with update(), not
* append().
*
* @return True if last value exists, false otherwise.
*/
public synchronized boolean hasLastValue() {
return m_lastValue != null;
}

/**
* Gets the last value.
*
* <p>Note: the last value is local to this class instance and updated only with update(), not
* append().
*
* @return Last value, or null if none.
*/
@SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull")
public synchronized boolean[] getLastValue() {
if (m_lastValue == null) {
return null;
}
return Arrays.copyOf(m_lastValue, m_lastValueLen);
}

private boolean equalsLast(boolean[] value) {
if (m_lastValue == null || m_lastValueLen != value.length) {
return false;
}
return Arrays.equals(m_lastValue, 0, value.length, value, 0, value.length);
}

private void copyToLast(boolean[] value) {
if (m_lastValue == null || m_lastValue.length < value.length) {
m_lastValue = Arrays.copyOf(value, value.length);
} else {
System.arraycopy(value, 0, m_lastValue, 0, value.length);
}
m_lastValueLen = value.length;
}

private boolean[] m_lastValue;
private int m_lastValueLen;
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,60 @@ public void append(boolean value, long timestamp) {
public void append(boolean value) {
m_log.appendBoolean(m_entry, value, 0);
}

/**
* Updates the last value and appends a record to the log if it has changed.
*
* <p>Note: the last value is local to this class instance; using update() with two instances
* pointing to the same underlying log entry name will likely result in unexpected results.
*
* @param value Value to record
* @param timestamp Time stamp (0 to indicate now)
*/
public synchronized void update(boolean value, long timestamp) {
if (!m_hasLastValue || m_lastValue != value) {
m_lastValue = value;
m_hasLastValue = true;
append(value, timestamp);
}
}

/**
* Updates the last value and appends a record to the log if it has changed.
*
* <p>Note: the last value is local to this class instance; using update() with two instances
* pointing to the same underlying log entry name will likely result in unexpected results.
*
* @param value Value to record
*/
public void update(boolean value) {
update(value, 0);
}

/**
* Gets whether there is a last value.
*
* <p>Note: the last value is local to this class instance and updated only with update(), not
* append().
*
* @return True if last value exists, false otherwise.
*/
public synchronized boolean hasLastValue() {
return m_hasLastValue;
}

/**
* Gets the last value.
*
* <p>Note: the last value is local to this class instance and updated only with update(), not
* append().
*
* @return Last value, or false if none.
*/
public synchronized boolean getLastValue() {
return m_lastValue;
}

private boolean m_hasLastValue;
private boolean m_lastValue;
}
12 changes: 6 additions & 6 deletions wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ public void setMetadata(int entry, String metadata) {
setMetadata(entry, metadata, 0);
}

@Override
public void close() {
DataLogJNI.close(m_impl);
m_impl = 0;
}

/**
* Appends a raw record to the log.
*
Expand Down Expand Up @@ -318,12 +324,6 @@ public void appendRaw(int entry, ByteBuffer data, int start, int len, long times
DataLogJNI.appendRaw(m_impl, entry, data, start, len, timestamp);
}

@Override
public void close() {
DataLogJNI.close(m_impl);
m_impl = 0;
}

/**
* Appends a boolean record to the log.
*
Expand Down
18 changes: 18 additions & 0 deletions wpiutil/src/main/java/edu/wpi/first/util/datalog/DataLogJNI.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,31 @@ public class DataLogJNI extends WPIUtilJNI {
*/
static native long fgCreate(String filename, String extraHeader) throws IOException;

/**
* Create a new Data Log foreground writer to a memory buffer.
*
* @param extraHeader extra header data
* @return data log writer implementation handle
*/
static native long fgCreateMemory(String extraHeader);

/**
* Explicitly flushes the log data to disk.
*
* @param impl data log background writer implementation handle
*/
static native void flush(long impl);

/**
* Flushes the log data to a memory buffer (only valid with fgCreateMemory data logs).
*
* @param impl data log background writer implementation handle
* @param buf output data buffer
* @param pos position in write buffer to start copying from
* @return Number of bytes written to buffer; 0 if no more to copy
*/
static native int copyWriteBuffer(long impl, byte[] buf, int pos);

/**
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package edu.wpi.first.util.datalog;

import java.io.IOException;
import java.io.OutputStream;

/** A data log writer that flushes the data log to a file when flush() is called. */
public class DataLogWriter extends DataLog {
Expand All @@ -17,6 +18,8 @@ public class DataLogWriter extends DataLog {
*/
public DataLogWriter(String filename, String extraHeader) throws IOException {
super(DataLogJNI.fgCreate(filename, extraHeader));
m_os = null;
m_buf = null;
}

/**
Expand All @@ -28,4 +31,54 @@ public DataLogWriter(String filename, String extraHeader) throws IOException {
public DataLogWriter(String filename) throws IOException {
this(filename, "");
}

/**
* Construct a new Data Log with an output stream. Prefer the filename version if possible; this
* is much slower!
*
* @param os output stream
* @param extraHeader extra header data
*/
public DataLogWriter(OutputStream os, String extraHeader) {
super(DataLogJNI.fgCreateMemory(extraHeader));
m_os = os;
m_buf = new byte[kBufferSize];
}

/**
* Construct a new Data Log with an output stream.
*
* @param os output stream
*/
public DataLogWriter(OutputStream os) {
this(os, "");
}

/** Explicitly flushes the log data to disk. */
@Override
public void flush() {
DataLogJNI.flush(m_impl);
if (m_os == null) {
return;
}
try {
int pos = 0;
for (; ; ) {
int qty = DataLogJNI.copyWriteBuffer(m_impl, m_buf, pos);
if (qty == 0) {
break;
}
pos += qty;
m_os.write(m_buf, 0, qty);
}
m_os.flush();
} catch (IOException e) {
// ignore
}
}

private static final int kBufferSize = 16 * 1024;

private final OutputStream m_os;
private final byte[] m_buf;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package edu.wpi.first.util.datalog;

import java.util.Arrays;

/** Log array of double values. */
public class DoubleArrayLogEntry extends DataLogEntry {
/** The data type for double array values. */
Expand Down Expand Up @@ -71,4 +73,79 @@ public void append(double[] value, long timestamp) {
public void append(double[] value) {
m_log.appendDoubleArray(m_entry, value, 0);
}

/**
* Updates the last value and appends a record to the log if it has changed.
*
* <p>Note: the last value is local to this class instance; using update() with two instances
* pointing to the same underlying log entry name will likely result in unexpected results.
*
* @param value Value to record
* @param timestamp Time stamp (0 to indicate now)
*/
public synchronized void update(double[] value, long timestamp) {
if (!equalsLast(value)) {
copyToLast(value);
append(value, timestamp);
}
}

/**
* Updates the last value and appends a record to the log if it has changed.
*
* <p>Note: the last value is local to this class instance; using update() with two instances
* pointing to the same underlying log entry name will likely result in unexpected results.
*
* @param value Value to record
*/
public void update(double[] value) {
update(value, 0);
}

/**
* Gets whether there is a last value.
*
* <p>Note: the last value is local to this class instance and updated only with update(), not
* append().
*
* @return True if last value exists, false otherwise.
*/
public synchronized boolean hasLastValue() {
return m_lastValue != null;
}

/**
* Gets the last value.
*
* <p>Note: the last value is local to this class instance and updated only with update(), not
* append().
*
* @return Last value, or false if none.
*/
@SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull")
public synchronized double[] getLastValue() {
if (m_lastValue == null) {
return null;
}
return Arrays.copyOf(m_lastValue, m_lastValueLen);
}

private boolean equalsLast(double[] value) {
if (m_lastValue == null || m_lastValueLen != value.length) {
return false;
}
return Arrays.equals(m_lastValue, 0, value.length, value, 0, value.length);
}

private void copyToLast(double[] value) {
if (m_lastValue == null || m_lastValue.length < value.length) {
m_lastValue = Arrays.copyOf(value, value.length);
} else {
System.arraycopy(value, 0, m_lastValue, 0, value.length);
}
m_lastValueLen = value.length;
}

private double[] m_lastValue;
private int m_lastValueLen;
}
Loading

0 comments on commit 4c7fe73

Please sign in to comment.