[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

@@ -16,6 +16,10 @@ struct ThingA {
int x = 0;
};
inline bool operator==(const ThingA& a, const ThingA& b) {
return a.x == b.x;
}
struct ThingB {
int x = 0;
};
@@ -130,10 +134,376 @@ class DataLogTest : public ::testing::Test {
};
TEST_F(DataLogTest, SimpleInt) {
int entry = log.Start("test", "int64");
log.AppendInteger(entry, 1, 0);
int entry = log.Start("test", "int64", "", 1);
log.AppendInteger(entry, 1, 2);
log.Flush();
ASSERT_EQ(data.size(), 54u);
}
TEST_F(DataLogTest, BooleanAppend) {
wpi::log::BooleanLogEntry entry{log, "a", 5};
entry.Append(false, 7);
log.Flush();
ASSERT_EQ(data.size(), 46u);
}
TEST_F(DataLogTest, BooleanUpdate) {
wpi::log::BooleanLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(false, 7);
log.Flush();
ASSERT_EQ(data.size(), 46u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), false);
entry.Update(false, 8);
log.Flush();
ASSERT_EQ(data.size(), 46u);
entry.Update(true, 9);
log.Flush();
ASSERT_EQ(data.size(), 51u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), true);
}
TEST_F(DataLogTest, IntegerAppend) {
wpi::log::IntegerLogEntry entry{log, "a", 5};
entry.Append(5, 7);
log.Flush();
ASSERT_EQ(data.size(), 51u);
}
TEST_F(DataLogTest, IntegerUpdate) {
wpi::log::IntegerLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(0, 7);
log.Flush();
ASSERT_EQ(data.size(), 51u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0);
entry.Update(0, 8);
log.Flush();
ASSERT_EQ(data.size(), 51u);
entry.Update(2, 9);
log.Flush();
ASSERT_EQ(data.size(), 63u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 2);
}
TEST_F(DataLogTest, FloatAppend) {
wpi::log::FloatLogEntry entry{log, "a", 5};
entry.Append(5.0, 7);
log.Flush();
ASSERT_EQ(data.size(), 47u);
}
TEST_F(DataLogTest, FloatUpdate) {
wpi::log::FloatLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(0.0f, 7);
log.Flush();
ASSERT_EQ(data.size(), 47u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.0f);
entry.Update(0.0f, 8);
log.Flush();
ASSERT_EQ(data.size(), 47u);
entry.Update(0.1f, 9);
log.Flush();
ASSERT_EQ(data.size(), 55u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.1f);
}
TEST_F(DataLogTest, DoubleAppend) {
wpi::log::DoubleLogEntry entry{log, "a", 5};
entry.Append(5.0, 7);
log.Flush();
ASSERT_EQ(data.size(), 52u);
}
TEST_F(DataLogTest, DoubleUpdate) {
wpi::log::DoubleLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(0.0, 7);
log.Flush();
ASSERT_EQ(data.size(), 52u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.0);
entry.Update(0.0, 8);
log.Flush();
ASSERT_EQ(data.size(), 52u);
entry.Update(0.1, 9);
log.Flush();
ASSERT_EQ(data.size(), 64u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.1);
}
TEST_F(DataLogTest, StringAppend) {
wpi::log::StringLogEntry entry{log, "a", 5};
entry.Append("x", 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
}
TEST_F(DataLogTest, StringUpdate) {
wpi::log::StringLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.HasLastValue());
entry.Update("x", 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "x");
entry.Update("x", 8);
log.Flush();
ASSERT_EQ(data.size(), 45u);
entry.Update("y", 9);
log.Flush();
ASSERT_EQ(data.size(), 50u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "y");
entry.Update("yy", 10);
log.Flush();
ASSERT_EQ(data.size(), 56u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "yy");
entry.Update("", 11);
log.Flush();
ASSERT_EQ(data.size(), 60u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "");
}
TEST_F(DataLogTest, RawAppend) {
wpi::log::RawLogEntry entry{log, "a", 5};
entry.Append({{5}}, 7);
log.Flush();
ASSERT_EQ(data.size(), 42u);
}
TEST_F(DataLogTest, RawUpdate) {
wpi::log::RawLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.HasLastValue());
entry.Update({{5}}, 7);
log.Flush();
ASSERT_EQ(data.size(), 42u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<uint8_t>{5});
entry.Update({{5}}, 8);
log.Flush();
ASSERT_EQ(data.size(), 42u);
entry.Update({{6}}, 9);
log.Flush();
ASSERT_EQ(data.size(), 47u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<uint8_t>{6});
entry.Update({{6, 6}}, 9);
log.Flush();
ASSERT_EQ(data.size(), 53u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), (std::vector<uint8_t>{6, 6}));
entry.Update(std::span<const uint8_t>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 57u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<uint8_t>{});
}
TEST_F(DataLogTest, BooleanArrayAppendEmpty) {
wpi::log::BooleanArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const bool>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 47u);
}
TEST_F(DataLogTest, BooleanArrayAppend) {
wpi::log::BooleanArrayLogEntry entry{log, "a", 5};
entry.Append({false}, 7);
log.Flush();
ASSERT_EQ(data.size(), 48u);
}
TEST_F(DataLogTest, BooleanArrayUpdate) {
wpi::log::BooleanArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({false}, 7);
log.Flush();
ASSERT_EQ(data.size(), 48u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int>{false});
entry.Update({false}, 8);
log.Flush();
ASSERT_EQ(data.size(), 48u);
entry.Update({true}, 9);
log.Flush();
ASSERT_EQ(data.size(), 53u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int>{true});
entry.Update(std::span<const bool>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 57u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int>{});
}
TEST_F(DataLogTest, IntegerArrayAppendEmpty) {
wpi::log::IntegerArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const int64_t>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
}
TEST_F(DataLogTest, IntegerArrayAppend) {
wpi::log::IntegerArrayLogEntry entry{log, "a", 5};
entry.Append({1}, 7);
log.Flush();
ASSERT_EQ(data.size(), 53u);
}
TEST_F(DataLogTest, IntegerArrayUpdate) {
wpi::log::IntegerArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({1}, 7);
log.Flush();
ASSERT_EQ(data.size(), 53u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int64_t>{1});
entry.Update({1}, 8);
log.Flush();
ASSERT_EQ(data.size(), 53u);
entry.Update({2}, 9);
log.Flush();
ASSERT_EQ(data.size(), 65u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int64_t>{2});
entry.Update(std::span<const int64_t>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 69u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int64_t>{});
}
TEST_F(DataLogTest, DoubleArrayAppendEmpty) {
wpi::log::DoubleArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const double>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 46u);
}
TEST_F(DataLogTest, DoubleArrayAppend) {
wpi::log::DoubleArrayLogEntry entry{log, "a", 5};
entry.Append({1.0}, 7);
log.Flush();
ASSERT_EQ(data.size(), 54u);
}
TEST_F(DataLogTest, DoubleArrayUpdate) {
wpi::log::DoubleArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({1.0}, 7);
log.Flush();
ASSERT_EQ(data.size(), 54u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<double>{1.0});
entry.Update({1.0}, 8);
log.Flush();
ASSERT_EQ(data.size(), 54u);
entry.Update({2.0}, 9);
log.Flush();
ASSERT_EQ(data.size(), 66u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<double>{2});
entry.Update(std::span<const double>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 70u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<double>{});
}
TEST_F(DataLogTest, FloatArrayAppendEmpty) {
wpi::log::FloatArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const float>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
}
TEST_F(DataLogTest, FloatArrayAppend) {
wpi::log::FloatArrayLogEntry entry{log, "a", 5};
entry.Append({1.0f}, 7);
log.Flush();
ASSERT_EQ(data.size(), 49u);
}
TEST_F(DataLogTest, FloatArrayUpdate) {
wpi::log::FloatArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({1.0f}, 7);
log.Flush();
ASSERT_EQ(data.size(), 49u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<float>{1.0f});
entry.Update({1.0f}, 8);
log.Flush();
ASSERT_EQ(data.size(), 49u);
entry.Update({2.0f}, 9);
log.Flush();
ASSERT_EQ(data.size(), 57u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<float>{2.0f});
entry.Update(std::span<const float>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 61u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<float>{});
}
TEST_F(DataLogTest, StringArrayAppendEmpty) {
wpi::log::StringArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const std::string>{}, 7);
entry.Append(std::span<const std::string_view>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 58u);
}
TEST_F(DataLogTest, StringArrayAppend) {
wpi::log::StringArrayLogEntry entry{log, "a", 5};
entry.Append({"x"}, 7);
log.Flush();
ASSERT_EQ(data.size(), 55u);
}
TEST_F(DataLogTest, StringArrayUpdate) {
wpi::log::StringArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({"x"}, 7);
log.Flush();
ASSERT_EQ(data.size(), 55u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<std::string>{"x"});
entry.Update({"x"}, 8);
log.Flush();
ASSERT_EQ(data.size(), 55u);
entry.Update({"y"}, 9);
log.Flush();
ASSERT_EQ(data.size(), 68u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<std::string>{"y"});
entry.Update(std::span<const std::string_view>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 76u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<std::string>{});
}
TEST_F(DataLogTest, StructA) {
@@ -144,6 +514,27 @@ TEST_F(DataLogTest, StructA) {
entry.Append(ThingA{}, 7);
}
TEST_F(DataLogTest, StructUpdate) {
wpi::log::StructLogEntry<ThingA> entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(ThingA{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 122u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), ThingA{});
entry.Update(ThingA{}, 8);
log.Flush();
ASSERT_EQ(data.size(), 122u);
entry.Update(ThingA{.x = 1}, 9);
log.Flush();
ASSERT_EQ(data.size(), 127u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), ThingA{.x = 1});
}
TEST_F(DataLogTest, StructArrayA) {
[[maybe_unused]]
wpi::log::StructArrayLogEntry<ThingA> entry0;
@@ -152,6 +543,35 @@ TEST_F(DataLogTest, StructArrayA) {
entry.Append({{ThingA{}, ThingA{}}}, 7);
}
TEST_F(DataLogTest, StructArrayUpdate) {
wpi::log::StructArrayLogEntry<ThingA> entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({{ThingA{}, ThingA{.x = 1}}}, 7);
log.Flush();
ASSERT_EQ(data.size(), 125u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(),
(std::vector<ThingA>{ThingA{}, ThingA{.x = 1}}));
entry.Update({{ThingA{}, ThingA{.x = 1}}}, 8);
log.Flush();
ASSERT_EQ(data.size(), 125u);
entry.Update({{ThingA{}, ThingA{.x = 2}}}, 9);
log.Flush();
ASSERT_EQ(data.size(), 131u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(),
(std::vector<ThingA>{ThingA{}, ThingA{.x = 2}}));
entry.Update(std::span<const ThingA>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 135u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<ThingA>{});
}
TEST_F(DataLogTest, StructFixedArrayA) {
[[maybe_unused]]
wpi::log::StructArrayLogEntry<std::array<ThingA, 2>> entry0;