Skip to content

Commit

Permalink
WIP loading
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterJohnson committed Jan 2, 2024
1 parent 33a7427 commit 819c968
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 45 deletions.
26 changes: 14 additions & 12 deletions glass/src/lib/native/include/glass/support/DataLogReaderThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@

namespace glass {

class DataLogReaderRange {
public:
DataLogReaderRange(wpi::log::DataLogReader::iterator begin,
wpi::log::DataLogReader::iterator end)
: m_begin{begin}, m_end{end} {}

wpi::log::DataLogReader::iterator begin() const { return m_begin; }
wpi::log::DataLogReader::iterator end() const { return m_end; }

wpi::log::DataLogReader::iterator m_begin;
wpi::log::DataLogReader::iterator m_end;
};

class DataLogReaderEntry : public wpi::log::StartRecordData {
public:
struct Range {
Range(wpi::log::DataLogReader::iterator begin,
wpi::log::DataLogReader::iterator end)
: m_begin{begin}, m_end{end} {}

wpi::log::DataLogReader::iterator begin() const { return m_begin; }
wpi::log::DataLogReader::iterator end() const { return m_end; }

wpi::log::DataLogReader::iterator m_begin;
wpi::log::DataLogReader::iterator m_end;
};
std::vector<Range> ranges; // ranges where this entry is valid
std::vector<DataLogReaderRange> ranges; // ranges where this entry is valid
};

class DataLogReaderThread {
Expand Down
20 changes: 18 additions & 2 deletions sysid/src/main/native/cpp/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <wpigui_openurl.h>

#include "sysid/view/Analyzer.h"
#include "sysid/view/DataSelector.h"
#include "sysid/view/LogLoader.h"
#include "sysid/view/UILayout.h"

Expand All @@ -32,6 +33,7 @@ namespace gui = wpi::gui;
static std::unique_ptr<glass::WindowManager> gWindowManager;

glass::Window* gLogLoaderWindow;
glass::Window* gDataSelectorWindow;
glass::Window* gAnalyzerWindow;
glass::Window* gProgramLogWindow;
static glass::MainMenuBar gMainMenu;
Expand Down Expand Up @@ -100,8 +102,16 @@ void Application(std::string_view saveDir) {
gWindowManager = std::make_unique<glass::WindowManager>(storage);
gWindowManager->GlobalInit();

gLogLoaderWindow = gWindowManager->AddWindow(
"Log Loader", std::make_unique<sysid::LogLoader>(storage, gLogger));
auto logLoader = std::make_unique<sysid::LogLoader>(storage, gLogger);
auto dataSelector = std::make_unique<sysid::DataSelector>(storage, gLogger);

logLoader->unload.connect([ds = dataSelector.get()] { ds->Reset(); });

gLogLoaderWindow =
gWindowManager->AddWindow("Log Loader", std::move(logLoader));

gDataSelectorWindow =
gWindowManager->AddWindow("Data Selector", std::move(dataSelector));

gAnalyzerWindow = gWindowManager->AddWindow(
"Analyzer", std::make_unique<sysid::Analyzer>(storage, gLogger));
Expand All @@ -117,6 +127,12 @@ void Application(std::string_view saveDir) {
gLogLoaderWindow->SetDefaultSize(sysid::kLogLoaderWindowSize.x,
sysid::kLogLoaderWindowSize.y);

// Data selector window position/size
gDataSelectorWindow->SetDefaultPos(sysid::kDataSelectorWindowPos.x,
sysid::kDataSelectorWindowPos.y);
gDataSelectorWindow->SetDefaultSize(sysid::kDataSelectorWindowSize.x,
sysid::kDataSelectorWindowSize.y);

// Analyzer window position/size
gAnalyzerWindow->SetDefaultPos(sysid::kAnalyzerWindowPos.x,
sysid::kAnalyzerWindowPos.y);
Expand Down
118 changes: 115 additions & 3 deletions sysid/src/main/native/cpp/view/DataSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,127 @@
#include "sysid/view/DataSelector.h"

#include <glass/support/DataLogReaderThread.h>
#include <fmt/format.h>
#include <imgui.h>
#include <wpi/Logger.h>
#include <wpi/StringExtras.h>

using namespace sysid;

void DataSelector::Display() {}
static bool EmitEntryTarget(const char* name, bool isString,
const glass::DataLogReaderEntry** entry) {
if (*entry) {
auto text =
fmt::format("{}: {} ({})", name, (*entry)->name, (*entry)->type);
ImGui::TextUnformatted(text.c_str());
} else {
ImGui::Text("%s: <none (DROP HERE)> (%s)", name,
isString ? "string" : "number");
}
bool rv = false;
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(
isString ? "DataLogEntryString" : "DataLogEntry")) {
assert(payload->DataSize == sizeof(const glass::DataLogReaderEntry*));
*entry = *static_cast<const glass::DataLogReaderEntry**>(payload->Data);
rv = true;
}
ImGui::EndDragDropTarget();
}
return rv;
}

void DataSelector::Reset() {
void DataSelector::Display() {
if (EmitEntryTarget("Test State", true, &m_testStateEntry)) {
LoadTests(*m_testStateEntry);
}

if (!m_testStateEntry) {
return;
}

using namespace std::chrono_literals;
if (m_testsFuture.valid() &&
m_testsFuture.wait_for(0s) == std::future_status::ready) {
m_tests = m_testsFuture.get();
}

if (m_tests.empty()) {
if (m_testsFuture.valid()) {
ImGui::TextUnformatted("Reading tests...");
} else {
ImGui::TextUnformatted("No tests found");
}
return;
}

// Test filtering
if (ImGui::BeginCombo("Test", m_selectedTest.c_str())) {
for (auto&& test : m_tests) {
if (ImGui::Selectable(test.first.c_str(), test.first == m_selectedTest)) {
m_selectedTest = test.first;
}
}
ImGui::EndCombo();
}

// DND targets
EmitEntryTarget("Velocity", false, &m_velocityEntry);
EmitEntryTarget("Position", false, &m_positionEntry);
EmitEntryTarget("Voltage", false, &m_voltageEntry);

if (!m_selectedTest.empty() && m_velocityEntry && m_positionEntry &&
m_voltageEntry) {
if (ImGui::Button("Load")) {
}
}
}

void DataSelector::SetRunStateEntry(const glass::DataLogReaderEntry& entry) {
void DataSelector::Reset() {
m_tests = {};
m_selectedTest.clear();
m_testStateEntry = nullptr;
m_velocityEntry = nullptr;
m_positionEntry = nullptr;
m_voltageEntry = nullptr;
}

void DataSelector::LoadTests(const glass::DataLogReaderEntry& testStateEntry) {
m_testsFuture = std::async(std::launch::async, [&] {
Tests rv;
for (auto&& range : testStateEntry.ranges) {
Runs* curRuns = nullptr;
wpi::log::DataLogReader::iterator lastStart = range.begin();
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
std::string_view testState;
if (it->GetEntry() != testStateEntry.entry ||
!it->GetString(&testState)) {
continue;
}

if (testState == "none") {
continue;
}

auto [testName, direction] = wpi::rsplit(testState, '-');

// track runs as iterator ranges of the same test
if (curRuns) {
curRuns->emplace_back(lastStart, it);
}
lastStart = it;
auto testIt = rv.find(testName);
if (testIt == rv.end()) {
testIt = rv.emplace(std::string{testName}, State{}).first;
}
auto stateIt = testIt->second.find(testState);
if (stateIt == testIt->second.end()) {
stateIt =
testIt->second.emplace(std::string{testState}, Runs{}).first;
}
curRuns = &stateIt->second;
}
}
return rv;
});
}
22 changes: 11 additions & 11 deletions sysid/src/main/native/cpp/view/LogLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,22 @@ void LogLoader::Display() {
}

ImGui::BeginTable(
"Entries", 1,
"Entries", 2,
ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp);
#if 0
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Type");
ImGui::TableSetupColumn("Metadata");
// ImGui::TableSetupColumn("Metadata");
ImGui::TableHeadersRow();
#endif
DisplayEntryTree(m_entryTree);
ImGui::EndTable();
}

void LogLoader::RebuildEntryTree() {
wpi::SmallVector<std::string_view, 16> parts;
m_reader->ForEachEntryName([&](const glass::DataLogReaderEntry& entry) {
// only show double/float entries (TODO: support struct/protobuf)
if (entry.type != "double" && entry.type != "float") {
// only show double/float/string entries (TODO: support struct/protobuf)
if (entry.type != "double" && entry.type != "float" &&
entry.type != "string") {
return;
}

Expand Down Expand Up @@ -160,17 +159,18 @@ static void EmitEntry(const std::string& name,
ImGui::Selectable(name.c_str());
if (ImGui::BeginDragDropSource()) {
auto entryPtr = &entry;
ImGui::SetDragDropPayload("DataLogEntry", &entryPtr,
sizeof(entryPtr)); // NOLINT
ImGui::SetDragDropPayload(
entry.type == "string" ? "DataLogEntryString" : "DataLogEntry",
&entryPtr,
sizeof(entryPtr)); // NOLINT
ImGui::TextUnformatted(entry.name.data(),
entry.name.data() + entry.name.size());
ImGui::EndDragDropSource();
}
#if 0
ImGui::TableNextColumn();
ImGui::TextUnformatted(entry.type.data(),
entry.type.data() + entry.type.size());

#if 0
ImGui::TableNextColumn();
ImGui::TextUnformatted(entry.metadata.data(),
entry.metadata.data() + entry.metadata.size());
Expand All @@ -187,8 +187,8 @@ void LogLoader::DisplayEntryTree(const std::vector<EntryTreeNode>& tree) {
ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(node.name.c_str(),
ImGuiTreeNodeFlags_SpanFullWidth);
#if 0
ImGui::TableNextColumn();
#if 0
ImGui::TableNextColumn();
#endif
if (open) {
Expand Down
30 changes: 21 additions & 9 deletions sysid/src/main/native/include/sysid/view/DataSelector.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

#pragma once

#include <future>
#include <map>
#include <string>
#include <vector>

#include <glass/View.h>
#include <glass/support/DataLogReaderThread.h>
#include <wpi/StringMap.h>

namespace glass {
class DataLogReaderEntry;
Expand All @@ -27,7 +34,7 @@ class DataSelector : public glass::View {
* @param logger The program logger
*/
explicit DataSelector(glass::Storage& storage, wpi::Logger& logger)
: m_logger(logger) {}
/*: m_logger{logger}*/ {}

/**
* Displays the log loader window.
Expand All @@ -40,14 +47,19 @@ class DataSelector : public glass::View {
*/
void Reset();

/**
* Sets the run state entry.
*
* @param entry
*/
void SetRunStateEntry(const glass::DataLogReaderEntry& entry);

private:
wpi::Logger& m_logger;
void LoadTests(const glass::DataLogReaderEntry& testStateEntry);

// wpi::Logger& m_logger;
using Runs = std::vector<glass::DataLogReaderRange>;
using State = std::map<std::string, Runs, std::less<>>; // full name
using Tests = std::map<std::string, State, std::less<>>; // e.g. "dynamic"
std::future<Tests> m_testsFuture;
std::string m_selectedTest;
Tests m_tests;
const glass::DataLogReaderEntry* m_testStateEntry = nullptr;
const glass::DataLogReaderEntry* m_velocityEntry = nullptr;
const glass::DataLogReaderEntry* m_positionEntry = nullptr;
const glass::DataLogReaderEntry* m_voltageEntry = nullptr;
};
} // namespace sysid
6 changes: 0 additions & 6 deletions sysid/src/main/native/include/sysid/view/LogLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,6 @@ class LogLoader : public glass::View {
*/
wpi::sig::Signal<> unload;

/**
* Signal called when a new file is fully loaded.
* The parameter is the LogEntry for the analysis state string entry.
*/
wpi::sig::Signal<const glass::DataLogReaderEntry&> loaded;

private:
wpi::Logger& m_logger;

Expand Down
7 changes: 5 additions & 2 deletions sysid/src/main/native/include/sysid/view/UILayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ inline constexpr Vector2d kLeftColSize{

// Left column contents
inline constexpr Vector2d kLogLoaderWindowPos = kLeftColPos;
inline constexpr Vector2d kLogLoaderWindowSize{
kLeftColSize.x, kAppWindowSize.y - kWindowGap - kLogLoaderWindowPos.y};
inline constexpr Vector2d kLogLoaderWindowSize{kLeftColSize.x, 550};
inline constexpr Vector2d kDataSelectorWindowPos =
kLogLoaderWindowPos + Vector2d{0, kLogLoaderWindowSize.y + kWindowGap};
inline constexpr Vector2d kDataSelectorWindowSize{
kLeftColSize.x, kAppWindowSize.y - kWindowGap - kDataSelectorWindowPos.y};

// Center column position and size
inline constexpr Vector2d kCenterColPos =
Expand Down

0 comments on commit 819c968

Please sign in to comment.