mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-26 01:51:41 +00:00
[wpiutil] DataLog: Add last value and change detection (#6674)
Update() checks/updates the last value and appends only if changed. GetLastValue() gets the last value. Also add OutputStream support to Java DataLogWriter.
This commit is contained in:
@@ -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. */
|
||||
@@ -71,4 +73,79 @@ public class BooleanArrayLogEntry extends DataLogEntry {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -71,4 +71,60 @@ public class BooleanLogEntry extends DataLogEntry {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -269,6 +269,12 @@ public class DataLog implements AutoCloseable {
|
||||
setMetadata(entry, metadata, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
DataLogJNI.close(m_impl);
|
||||
m_impl = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a raw record to the log.
|
||||
*
|
||||
@@ -318,12 +324,6 @@ public class DataLog implements AutoCloseable {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -46,6 +46,14 @@ 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.
|
||||
*
|
||||
@@ -53,6 +61,16 @@ public class DataLogJNI extends WPIUtilJNI {
|
||||
*/
|
||||
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.
|
||||
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,4 +31,54 @@ public class DataLogWriter extends DataLog {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
@@ -71,4 +73,79 @@ public class DoubleArrayLogEntry extends DataLogEntry {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -71,4 +71,60 @@ public class DoubleLogEntry extends DataLogEntry {
|
||||
public void append(double value) {
|
||||
m_log.appendDouble(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 (!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(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_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 0 if none.
|
||||
*/
|
||||
public synchronized double getLastValue() {
|
||||
return m_lastValue;
|
||||
}
|
||||
|
||||
boolean m_hasLastValue;
|
||||
double m_lastValue;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package edu.wpi.first.util.datalog;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Log array of float values. */
|
||||
public class FloatArrayLogEntry extends DataLogEntry {
|
||||
/** The data type for float array values. */
|
||||
@@ -71,4 +73,79 @@ public class FloatArrayLogEntry extends DataLogEntry {
|
||||
public void append(float[] value) {
|
||||
m_log.appendFloatArray(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(float[] 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(float[] 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 float[] getLastValue() {
|
||||
if (m_lastValue == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.copyOf(m_lastValue, m_lastValueLen);
|
||||
}
|
||||
|
||||
private boolean equalsLast(float[] 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(float[] 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 float[] m_lastValue;
|
||||
private int m_lastValueLen;
|
||||
}
|
||||
|
||||
@@ -71,4 +71,60 @@ public class FloatLogEntry extends DataLogEntry {
|
||||
public void append(float value) {
|
||||
m_log.appendFloat(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(float 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(float 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 0 if none.
|
||||
*/
|
||||
public synchronized float getLastValue() {
|
||||
return m_lastValue;
|
||||
}
|
||||
|
||||
boolean m_hasLastValue;
|
||||
float m_lastValue;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package edu.wpi.first.util.datalog;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Log array of integer values. */
|
||||
public class IntegerArrayLogEntry extends DataLogEntry {
|
||||
/** The data type for integer array values. */
|
||||
@@ -71,4 +73,79 @@ public class IntegerArrayLogEntry extends DataLogEntry {
|
||||
public void append(long[] value) {
|
||||
m_log.appendIntegerArray(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(long[] 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(long[] 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 long[] getLastValue() {
|
||||
if (m_lastValue == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.copyOf(m_lastValue, m_lastValueLen);
|
||||
}
|
||||
|
||||
private boolean equalsLast(long[] 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(long[] 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 long[] m_lastValue;
|
||||
private int m_lastValueLen;
|
||||
}
|
||||
|
||||
@@ -71,4 +71,60 @@ public class IntegerLogEntry extends DataLogEntry {
|
||||
public void append(long value) {
|
||||
m_log.appendInteger(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(long 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(long 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 0 if none.
|
||||
*/
|
||||
public synchronized long getLastValue() {
|
||||
return m_lastValue;
|
||||
}
|
||||
|
||||
boolean m_hasLastValue;
|
||||
long m_lastValue;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ public final class ProtobufLogEntry<T> extends DataLogEntry {
|
||||
DataLog log, String name, Protobuf<T, ?> proto, String metadata, long timestamp) {
|
||||
super(log, name, proto.getTypeString(), metadata, timestamp);
|
||||
m_buf = ProtobufBuffer.create(proto);
|
||||
m_immutable = proto.isImmutable();
|
||||
m_cloneable = proto.isCloneable();
|
||||
log.addSchema(proto, timestamp);
|
||||
}
|
||||
|
||||
@@ -113,5 +115,126 @@ public final class ProtobufLogEntry<T> extends DataLogEntry {
|
||||
append(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 void update(T value, long timestamp) {
|
||||
try {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable || m_cloneable) {
|
||||
if (value.equals(m_lastValue)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (m_immutable) {
|
||||
m_lastValue = value;
|
||||
} else {
|
||||
m_lastValue = m_buf.getProto().clone(value);
|
||||
}
|
||||
ByteBuffer bb = m_buf.write(value);
|
||||
m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
|
||||
return;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
doUpdate(m_buf.write(value), timestamp);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(T 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 boolean hasLastValue() {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable) {
|
||||
return m_lastValue != null;
|
||||
} else if (m_cloneable && m_lastValue != null) {
|
||||
return true;
|
||||
}
|
||||
return m_lastValueBuf != 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.
|
||||
*/
|
||||
public T getLastValue() {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable) {
|
||||
return m_lastValue;
|
||||
} else if (m_cloneable && m_lastValue != null) {
|
||||
try {
|
||||
return m_buf.getProto().clone(m_lastValue);
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
if (m_lastValueBuf == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
T val = m_buf.read(m_lastValueBuf);
|
||||
m_lastValueBuf.position(0);
|
||||
return val;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doUpdate(ByteBuffer bb, long timestamp) {
|
||||
int len = bb.position();
|
||||
bb.limit(len);
|
||||
bb.position(0);
|
||||
if (m_lastValueBuf == null || !bb.equals(m_lastValueBuf)) {
|
||||
// update last value
|
||||
if (m_lastValueBuf == null || m_lastValueBuf.limit() < len) {
|
||||
m_lastValueBuf = ByteBuffer.allocate(len);
|
||||
}
|
||||
bb.get(m_lastValueBuf.array(), 0, len);
|
||||
bb.position(0);
|
||||
m_lastValueBuf.limit(len);
|
||||
|
||||
// append to log
|
||||
m_log.appendRaw(m_entry, bb, 0, len, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
private final ProtobufBuffer<T, ?> m_buf;
|
||||
private ByteBuffer m_lastValueBuf;
|
||||
private final boolean m_immutable;
|
||||
private final boolean m_cloneable;
|
||||
private T m_lastValue;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package edu.wpi.first.util.datalog;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Log raw byte array values. */
|
||||
public class RawLogEntry extends DataLogEntry {
|
||||
@@ -125,7 +126,7 @@ public class RawLogEntry extends DataLogEntry {
|
||||
/**
|
||||
* Appends a record to the log.
|
||||
*
|
||||
* @param value Data to record; will send from value.position() to value.capacity()
|
||||
* @param value Data to record; will send from value.position() to value.limit()
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public void append(ByteBuffer value, long timestamp) {
|
||||
@@ -135,7 +136,7 @@ public class RawLogEntry extends DataLogEntry {
|
||||
/**
|
||||
* Appends a record to the log.
|
||||
*
|
||||
* @param value Data to record; will send from value.position() to value.capacity()
|
||||
* @param value Data to record; will send from value.position() to value.limit()
|
||||
*/
|
||||
public void append(ByteBuffer value) {
|
||||
append(value, 0);
|
||||
@@ -163,4 +164,204 @@ public class RawLogEntry extends DataLogEntry {
|
||||
public void append(ByteBuffer value, int start, int len) {
|
||||
append(value, start, len, 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; will send entire array contents
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public synchronized void update(byte[] value, long timestamp) {
|
||||
if (!equalsLast(value, 0, value.length)) {
|
||||
copyToLast(value, 0, value.length);
|
||||
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; will send entire array contents
|
||||
*/
|
||||
public void update(byte[] value) {
|
||||
update(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 Data to record
|
||||
* @param start Start position of data (in byte array)
|
||||
* @param len Length of data (must be less than or equal to value.length - offset)
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public synchronized void update(byte[] value, int start, int len, long timestamp) {
|
||||
if (!equalsLast(value, start, len)) {
|
||||
copyToLast(value, start, len);
|
||||
append(value, start, len, 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 Data to record
|
||||
* @param start Start position of data (in byte array)
|
||||
* @param len Length of data (must be less than or equal to value.length - offset)
|
||||
*/
|
||||
public void update(byte[] value, int start, int len) {
|
||||
update(value, start, len, 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 Data to record; will send from value.position() to value.limit()
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public synchronized void update(ByteBuffer value, long timestamp) {
|
||||
if (!equalsLast(value)) {
|
||||
int start = value.position();
|
||||
int len = value.limit() - start;
|
||||
copyToLast(value, start, len);
|
||||
append(value, start, len, 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 Data to record; will send from value.position() to value.limit()
|
||||
*/
|
||||
public void update(ByteBuffer value) {
|
||||
update(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 Data to record
|
||||
* @param start Start position of data (in value buffer)
|
||||
* @param len Length of data (must be less than or equal to value.length - offset)
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public synchronized void update(ByteBuffer value, int start, int len, long timestamp) {
|
||||
if (!equalsLast(value, start, len)) {
|
||||
copyToLast(value, start, len);
|
||||
append(value, start, len, 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 Data to record
|
||||
* @param start Start position of data (in value buffer)
|
||||
* @param len Length of data (must be less than or equal to value.length - offset)
|
||||
*/
|
||||
public void update(ByteBuffer value, int start, int len) {
|
||||
update(value, start, len, 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 byte[] getLastValue() {
|
||||
if (m_lastValue == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.copyOf(m_lastValue.array(), m_lastValue.limit());
|
||||
}
|
||||
|
||||
private boolean equalsLast(byte[] value, int start, int len) {
|
||||
if (m_lastValue == null || m_lastValue.limit() != len) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(m_lastValue.array(), 0, len, value, start, start + len);
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.SimplifyBooleanReturns")
|
||||
private boolean equalsLast(ByteBuffer value) {
|
||||
if (m_lastValue == null) {
|
||||
return false;
|
||||
}
|
||||
return value.equals(m_lastValue);
|
||||
}
|
||||
|
||||
private boolean equalsLast(ByteBuffer value, int start, int len) {
|
||||
if (m_lastValue == null || m_lastValue.limit() != len) {
|
||||
return false;
|
||||
}
|
||||
int origpos = value.position();
|
||||
value.position(start);
|
||||
int origlimit = value.limit();
|
||||
value.limit(start + len);
|
||||
boolean eq = value.equals(m_lastValue);
|
||||
value.position(origpos);
|
||||
value.limit(origlimit);
|
||||
return eq;
|
||||
}
|
||||
|
||||
private void copyToLast(byte[] value, int start, int len) {
|
||||
if (m_lastValue == null || m_lastValue.limit() < len) {
|
||||
m_lastValue = ByteBuffer.allocate(len);
|
||||
}
|
||||
System.arraycopy(value, start, m_lastValue.array(), 0, len);
|
||||
m_lastValue.limit(len);
|
||||
}
|
||||
|
||||
private void copyToLast(ByteBuffer value, int start, int len) {
|
||||
if (m_lastValue == null || m_lastValue.limit() < len) {
|
||||
m_lastValue = ByteBuffer.allocate(len);
|
||||
}
|
||||
int origpos = value.position();
|
||||
value.position(start);
|
||||
value.get(m_lastValue.array(), 0, len);
|
||||
value.position(origpos);
|
||||
m_lastValue.limit(len);
|
||||
}
|
||||
|
||||
private ByteBuffer m_lastValue;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package edu.wpi.first.util.datalog;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Log array of string values. */
|
||||
public class StringArrayLogEntry extends DataLogEntry {
|
||||
/** The data type for string array values. */
|
||||
@@ -71,4 +73,79 @@ public class StringArrayLogEntry extends DataLogEntry {
|
||||
public void append(String[] value) {
|
||||
m_log.appendStringArray(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(String[] 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(String[] 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 String[] getLastValue() {
|
||||
if (m_lastValue == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.copyOf(m_lastValue, m_lastValueLen);
|
||||
}
|
||||
|
||||
private boolean equalsLast(String[] 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(String[] 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 String[] m_lastValue;
|
||||
private int m_lastValueLen;
|
||||
}
|
||||
|
||||
@@ -96,4 +96,58 @@ public class StringLogEntry extends DataLogEntry {
|
||||
public void append(String value) {
|
||||
m_log.appendString(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(String value, long timestamp) {
|
||||
if (m_lastValue == null || !value.equals(m_lastValue)) {
|
||||
m_lastValue = 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(String 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.
|
||||
*/
|
||||
public synchronized String getLastValue() {
|
||||
return m_lastValue;
|
||||
}
|
||||
|
||||
private String m_lastValue;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ package edu.wpi.first.util.datalog;
|
||||
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import edu.wpi.first.util.struct.StructBuffer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -19,6 +21,8 @@ public final class StructArrayLogEntry<T> extends DataLogEntry {
|
||||
DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
|
||||
super(log, name, struct.getTypeString() + "[]", metadata, timestamp);
|
||||
m_buf = StructBuffer.create(struct);
|
||||
m_immutable = struct.isImmutable();
|
||||
m_cloneable = struct.isCloneable();
|
||||
log.addSchema(struct, timestamp);
|
||||
}
|
||||
|
||||
@@ -87,7 +91,7 @@ public final class StructArrayLogEntry<T> extends DataLogEntry {
|
||||
* @param nelem number of elements
|
||||
*/
|
||||
public void reserve(int nelem) {
|
||||
synchronized (this) {
|
||||
synchronized (m_buf) {
|
||||
m_buf.reserve(nelem);
|
||||
}
|
||||
}
|
||||
@@ -99,7 +103,7 @@ public final class StructArrayLogEntry<T> extends DataLogEntry {
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public void append(T[] value, long timestamp) {
|
||||
synchronized (this) {
|
||||
synchronized (m_buf) {
|
||||
ByteBuffer bb = m_buf.writeArray(value);
|
||||
m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
|
||||
}
|
||||
@@ -121,7 +125,7 @@ public final class StructArrayLogEntry<T> extends DataLogEntry {
|
||||
* @param timestamp Time stamp (0 to indicate now)
|
||||
*/
|
||||
public void append(Collection<T> value, long timestamp) {
|
||||
synchronized (this) {
|
||||
synchronized (m_buf) {
|
||||
ByteBuffer bb = m_buf.writeArray(value);
|
||||
m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
|
||||
}
|
||||
@@ -136,5 +140,242 @@ public final class StructArrayLogEntry<T> extends DataLogEntry {
|
||||
append(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 void update(T[] value, long timestamp) {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable || m_cloneable) {
|
||||
if (m_lastValue != null
|
||||
&& m_lastValueLen == value.length
|
||||
&& Arrays.equals(m_lastValue, 0, value.length, value, 0, value.length)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
copyToLast(value);
|
||||
ByteBuffer bb = m_buf.writeArray(value);
|
||||
m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
|
||||
return;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
doUpdate(m_buf.writeArray(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(T[] value) {
|
||||
update(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 void update(Collection<T> value, long timestamp) {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable || m_cloneable) {
|
||||
if (equalsLast(value)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
copyToLast(value);
|
||||
ByteBuffer bb = m_buf.writeArray(value);
|
||||
m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
|
||||
return;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
doUpdate(m_buf.writeArray(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(Collection<T> 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 boolean hasLastValue() {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable) {
|
||||
return m_lastValue != null;
|
||||
} else if (m_cloneable && m_lastValue != null) {
|
||||
return true;
|
||||
}
|
||||
return m_lastValueBuf != 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 T[] getLastValue() {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable) {
|
||||
if (m_lastValue == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.copyOf(m_lastValue, m_lastValueLen);
|
||||
} else if (m_cloneable && m_lastValue != null) {
|
||||
try {
|
||||
return cloneArray(m_lastValue, m_lastValueLen);
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
if (m_lastValueBuf == null) {
|
||||
return null;
|
||||
}
|
||||
T[] val = m_buf.readArray(m_lastValueBuf);
|
||||
m_lastValueBuf.position(0);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
private void doUpdate(ByteBuffer bb, long timestamp) {
|
||||
int len = bb.position();
|
||||
bb.limit(len);
|
||||
bb.position(0);
|
||||
if (m_lastValueBuf == null || !bb.equals(m_lastValueBuf)) {
|
||||
// update last value
|
||||
if (m_lastValueBuf == null || m_lastValueBuf.limit() < len) {
|
||||
m_lastValueBuf = ByteBuffer.allocate(len);
|
||||
}
|
||||
bb.get(m_lastValueBuf.array(), 0, len);
|
||||
bb.position(0);
|
||||
m_lastValueBuf.limit(len);
|
||||
|
||||
// append to log
|
||||
m_log.appendRaw(m_entry, bb, 0, len, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean equalsLast(Collection<T> arr) {
|
||||
if (m_lastValue == null) {
|
||||
return false;
|
||||
}
|
||||
if (m_lastValueLen != arr.size()) {
|
||||
return false;
|
||||
}
|
||||
int i = 0;
|
||||
for (T elem : arr) {
|
||||
if (!m_lastValue[i].equals(elem)) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private T[] cloneArray(T[] in, int len) throws CloneNotSupportedException {
|
||||
Struct<T> s = m_buf.getStruct();
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] arr = (T[]) Array.newInstance(s.getTypeClass(), len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
arr[i] = s.clone(in[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private T[] cloneArray(Collection<T> in) throws CloneNotSupportedException {
|
||||
Struct<T> s = m_buf.getStruct();
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] arr = (T[]) Array.newInstance(s.getTypeClass(), in.size());
|
||||
int i = 0;
|
||||
for (T elem : in) {
|
||||
arr[i++] = s.clone(elem);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private void copyToLast(T[] value) throws CloneNotSupportedException {
|
||||
if (m_lastValue == null || m_lastValue.length < value.length) {
|
||||
if (m_immutable) {
|
||||
m_lastValue = Arrays.copyOf(value, value.length);
|
||||
} else {
|
||||
m_lastValue = cloneArray(value, value.length);
|
||||
}
|
||||
} else {
|
||||
if (m_immutable) {
|
||||
System.arraycopy(value, 0, m_lastValue, 0, value.length);
|
||||
} else {
|
||||
Struct<T> s = m_buf.getStruct();
|
||||
for (int i = 0; i < value.length; i++) {
|
||||
m_lastValue[i] = s.clone(value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_lastValueLen = value.length;
|
||||
}
|
||||
|
||||
private void copyToLast(Collection<T> value) throws CloneNotSupportedException {
|
||||
if (m_lastValue == null || m_lastValue.length < value.size()) {
|
||||
if (m_immutable) {
|
||||
Struct<T> s = m_buf.getStruct();
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] arr = (T[]) Array.newInstance(s.getTypeClass(), value.size());
|
||||
int i = 0;
|
||||
for (T elem : value) {
|
||||
arr[i++] = elem;
|
||||
}
|
||||
} else {
|
||||
m_lastValue = cloneArray(value);
|
||||
}
|
||||
} else {
|
||||
Struct<T> s = m_buf.getStruct();
|
||||
int i = 0;
|
||||
for (T elem : value) {
|
||||
m_lastValue[i++] = m_immutable ? elem : s.clone(elem);
|
||||
}
|
||||
}
|
||||
m_lastValueLen = value.size();
|
||||
}
|
||||
|
||||
private final StructBuffer<T> m_buf;
|
||||
private ByteBuffer m_lastValueBuf;
|
||||
private final boolean m_immutable;
|
||||
private final boolean m_cloneable;
|
||||
private T[] m_lastValue;
|
||||
private int m_lastValueLen;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ public final class StructLogEntry<T> extends DataLogEntry {
|
||||
DataLog log, String name, Struct<T> struct, String metadata, long timestamp) {
|
||||
super(log, name, struct.getTypeString(), metadata, timestamp);
|
||||
m_buf = StructBuffer.create(struct);
|
||||
m_immutable = struct.isImmutable();
|
||||
m_cloneable = struct.isCloneable();
|
||||
log.addSchema(struct, timestamp);
|
||||
}
|
||||
|
||||
@@ -102,5 +104,118 @@ public final class StructLogEntry<T> extends DataLogEntry {
|
||||
append(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 void update(T value, long timestamp) {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable || m_cloneable) {
|
||||
if (value.equals(m_lastValue)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (m_immutable) {
|
||||
m_lastValue = value;
|
||||
} else {
|
||||
m_lastValue = m_buf.getStruct().clone(value);
|
||||
}
|
||||
ByteBuffer bb = m_buf.write(value);
|
||||
m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp);
|
||||
return;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
doUpdate(m_buf.write(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(T 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 boolean hasLastValue() {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable) {
|
||||
return m_lastValue != null;
|
||||
} else if (m_cloneable && m_lastValue != null) {
|
||||
return true;
|
||||
}
|
||||
return m_lastValueBuf != 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.
|
||||
*/
|
||||
public T getLastValue() {
|
||||
synchronized (m_buf) {
|
||||
if (m_immutable) {
|
||||
return m_lastValue;
|
||||
} else if (m_cloneable && m_lastValue != null) {
|
||||
try {
|
||||
return m_buf.getStruct().clone(m_lastValue);
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
if (m_lastValueBuf == null) {
|
||||
return null;
|
||||
}
|
||||
T val = m_buf.read(m_lastValueBuf);
|
||||
m_lastValueBuf.position(0);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
private void doUpdate(ByteBuffer bb, long timestamp) {
|
||||
int len = bb.position();
|
||||
bb.limit(len);
|
||||
bb.position(0);
|
||||
if (m_lastValueBuf == null || !bb.equals(m_lastValueBuf)) {
|
||||
// update last value
|
||||
if (m_lastValueBuf == null || m_lastValueBuf.limit() < len) {
|
||||
m_lastValueBuf = ByteBuffer.allocate(len);
|
||||
}
|
||||
bb.get(m_lastValueBuf.array(), 0, len);
|
||||
bb.position(0);
|
||||
m_lastValueBuf.limit(len);
|
||||
|
||||
// append to log
|
||||
m_log.appendRaw(m_entry, bb, 0, len, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
private final StructBuffer<T> m_buf;
|
||||
private ByteBuffer m_lastValueBuf;
|
||||
private final boolean m_immutable;
|
||||
private final boolean m_cloneable;
|
||||
private T m_lastValue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user