[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:
Peter Johnson
2024-07-21 13:08:15 -07:00
committed by GitHub
parent 57205c8d15
commit 4c7fe73f69
23 changed files with 3398 additions and 40 deletions

View File

@@ -4,6 +4,7 @@
#include "wpi/DataLog.h"
#include <algorithm>
#include <bit>
#include <cstdio>
#include <cstdlib>
@@ -618,6 +619,107 @@ void DataLog::AppendStringArray(int entry,
}
}
template <typename V1, typename V2>
inline bool UpdateImpl(std::optional<std::vector<V1>>& lastValue,
std::span<const V2> data) {
if (!lastValue || !std::equal(data.begin(), data.end(), lastValue->begin(),
lastValue->end())) {
if (lastValue) {
lastValue->assign(data.begin(), data.end());
} else {
lastValue = std::vector<V1>{data.begin(), data.end()};
}
return true;
}
return false;
}
template <typename V1>
inline bool UpdateImpl(std::optional<std::vector<V1>>& lastValue,
std::span<const bool> data) {
if (!lastValue || !std::equal(data.begin(), data.end(), lastValue->begin(),
lastValue->end(), [](auto a, auto b) {
return a == static_cast<bool>(b);
})) {
if (lastValue) {
lastValue->assign(data.begin(), data.end());
} else {
lastValue = std::vector<V1>{data.begin(), data.end()};
}
return true;
}
return false;
}
void RawLogEntry::Update(std::span<const uint8_t> data, int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, data)) {
Append(data, timestamp);
}
}
void BooleanArrayLogEntry::Update(std::span<const bool> arr,
int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void BooleanArrayLogEntry::Update(std::span<const int> arr, int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void BooleanArrayLogEntry::Update(std::span<const uint8_t> arr,
int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void IntegerArrayLogEntry::Update(std::span<const int64_t> arr,
int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void FloatArrayLogEntry::Update(std::span<const float> arr, int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void DoubleArrayLogEntry::Update(std::span<const double> arr,
int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void StringArrayLogEntry::Update(std::span<const std::string> arr,
int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
void StringArrayLogEntry::Update(std::span<const std::string_view> arr,
int64_t timestamp) {
std::scoped_lock lock{m_mutex};
if (UpdateImpl(m_lastValue, arr)) {
Append(arr, timestamp);
}
}
extern "C" {
void WPI_DataLog_Release(struct WPI_DataLog* datalog) {

View File

@@ -4,6 +4,7 @@
#include <jni.h>
#include <algorithm>
#include <vector>
#include <fmt/format.h>
@@ -18,6 +19,18 @@
using namespace wpi::java;
using namespace wpi::log;
namespace {
class buf_ostream : public wpi::raw_uvector_ostream {
private:
std::vector<uint8_t> data;
public:
buf_ostream() : raw_uvector_ostream{data} {}
void clear() { data.clear(); }
};
} // namespace
extern "C" {
/*
@@ -96,6 +109,24 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_fgCreate
return reinterpret_cast<jlong>(writer);
}
/*
* Class: edu_wpi_first_util_datalog_DataLogJNI
* Method: fgCreateMemory
* Signature: (Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_util_datalog_DataLogJNI_fgCreateMemory
(JNIEnv* env, jclass, jstring extraHeader)
{
if (!extraHeader) {
wpi::ThrowNullPointerException(env, "extraHeader is null");
return 0;
}
auto writer = new DataLogWriter{std::make_unique<buf_ostream>(),
JStringRef{env, extraHeader}};
return reinterpret_cast<jlong>(writer);
}
/*
* Class: edu_wpi_first_util_datalog_DataLogJNI
* Method: flush
@@ -109,7 +140,34 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_flush
wpi::ThrowNullPointerException(env, "impl is null");
return;
}
reinterpret_cast<DataLogBackgroundWriter*>(impl)->Flush();
reinterpret_cast<DataLog*>(impl)->Flush();
}
/*
* Class: edu_wpi_first_util_datalog_DataLogJNI
* Method: copyWriteBuffer
* Signature: (J[BI)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_util_datalog_DataLogJNI_copyWriteBuffer
(JNIEnv* env, jclass, jlong impl, jbyteArray buf, jint start)
{
if (impl == 0) {
wpi::ThrowNullPointerException(env, "impl is null");
return 0;
}
auto writer = reinterpret_cast<DataLogWriter*>(impl);
writer->Flush();
auto& stream = static_cast<buf_ostream&>(writer->GetStream());
JSpan<jbyte> jbuf{env, buf};
auto arr = stream.array();
if (start < 0 || static_cast<size_t>(start) >= arr.size()) {
stream.clear();
return 0;
}
size_t qty = (std::min)(jbuf.size(), arr.size() - start);
std::copy(arr.begin(), arr.begin() + qty, jbuf.begin());
return qty;
}
/*
@@ -125,7 +183,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_pause
wpi::ThrowNullPointerException(env, "impl is null");
return;
}
reinterpret_cast<DataLogBackgroundWriter*>(impl)->Pause();
reinterpret_cast<DataLog*>(impl)->Pause();
}
/*
@@ -141,7 +199,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_resume
wpi::ThrowNullPointerException(env, "impl is null");
return;
}
reinterpret_cast<DataLogBackgroundWriter*>(impl)->Resume();
reinterpret_cast<DataLog*>(impl)->Resume();
}
/*
@@ -157,7 +215,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_stop
wpi::ThrowNullPointerException(env, "impl is null");
return;
}
reinterpret_cast<DataLogBackgroundWriter*>(impl)->Stop();
reinterpret_cast<DataLog*>(impl)->Stop();
}
/*