2024-01-05 16:24:31 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
|
|
|
|
|
|
#include "glass/support/DataLogReaderThread.h"
|
|
|
|
|
|
2024-09-20 17:43:39 -07:00
|
|
|
#include <string>
|
2024-01-05 16:24:31 -08:00
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <wpi/StringExtras.h>
|
2024-05-12 06:25:42 -07:00
|
|
|
#include <wpi/print.h>
|
2024-01-05 16:24:31 -08:00
|
|
|
|
|
|
|
|
using namespace glass;
|
|
|
|
|
|
|
|
|
|
DataLogReaderThread::~DataLogReaderThread() {
|
|
|
|
|
if (m_thread.joinable()) {
|
|
|
|
|
m_active = false;
|
|
|
|
|
m_thread.join();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DataLogReaderThread::ReadMain() {
|
|
|
|
|
wpi::SmallDenseMap<
|
|
|
|
|
int, std::pair<DataLogReaderEntry*, std::span<const uint8_t>>, 8>
|
|
|
|
|
schemaEntries;
|
|
|
|
|
|
|
|
|
|
for (auto recordIt = m_reader.begin(), recordEnd = m_reader.end();
|
|
|
|
|
recordIt != recordEnd; ++recordIt) {
|
|
|
|
|
auto& record = *recordIt;
|
|
|
|
|
if (!m_active) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++m_numRecords;
|
|
|
|
|
if (record.IsStart()) {
|
|
|
|
|
DataLogReaderEntry data;
|
|
|
|
|
if (record.GetStartData(&data)) {
|
|
|
|
|
std::scoped_lock lock{m_mutex};
|
|
|
|
|
auto& entryPtr = m_entriesById[data.entry];
|
|
|
|
|
if (entryPtr) {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("...DUPLICATE entry ID, overriding\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
|
|
|
|
auto [it, isNew] = m_entriesByName.emplace(data.name, data);
|
|
|
|
|
if (isNew) {
|
|
|
|
|
it->second.ranges.emplace_back(recordIt, recordEnd);
|
|
|
|
|
}
|
|
|
|
|
entryPtr = &it->second;
|
|
|
|
|
if (data.type == "structschema" ||
|
|
|
|
|
data.type == "proto:FileDescriptorProto") {
|
|
|
|
|
schemaEntries.try_emplace(data.entry, entryPtr,
|
|
|
|
|
std::span<const uint8_t>{});
|
|
|
|
|
}
|
|
|
|
|
sigEntryAdded(data);
|
|
|
|
|
} else {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("Start(INVALID)\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
|
|
|
|
} else if (record.IsFinish()) {
|
|
|
|
|
int entry;
|
|
|
|
|
if (record.GetFinishEntry(&entry)) {
|
|
|
|
|
std::scoped_lock lock{m_mutex};
|
|
|
|
|
auto it = m_entriesById.find(entry);
|
|
|
|
|
if (it == m_entriesById.end()) {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("...ID not found\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
} else {
|
|
|
|
|
it->second->ranges.back().m_end = recordIt;
|
|
|
|
|
m_entriesById.erase(it);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("Finish(INVALID)\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
|
|
|
|
} else if (record.IsSetMetadata()) {
|
|
|
|
|
wpi::log::MetadataRecordData data;
|
|
|
|
|
if (record.GetSetMetadataData(&data)) {
|
|
|
|
|
std::scoped_lock lock{m_mutex};
|
|
|
|
|
auto it = m_entriesById.find(data.entry);
|
|
|
|
|
if (it == m_entriesById.end()) {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("...ID not found\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
} else {
|
|
|
|
|
it->second->metadata = data.metadata;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("SetMetadata(INVALID)\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
|
|
|
|
} else if (record.IsControl()) {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("Unrecognized control record\n");
|
2024-01-05 16:24:31 -08:00
|
|
|
} else {
|
|
|
|
|
auto it = schemaEntries.find(record.GetEntry());
|
|
|
|
|
if (it != schemaEntries.end()) {
|
|
|
|
|
it->second.second = record.GetRaw();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// build schema databases
|
|
|
|
|
for (auto&& schemaPair : schemaEntries) {
|
|
|
|
|
auto name = schemaPair.second.first->name;
|
|
|
|
|
auto data = schemaPair.second.second;
|
|
|
|
|
if (data.empty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-06-04 21:01:52 -07:00
|
|
|
if (auto strippedName = wpi::remove_prefix(name, "NT:")) {
|
|
|
|
|
name = *strippedName;
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
2024-06-04 21:01:52 -07:00
|
|
|
if (auto typeStr = wpi::remove_prefix(name, "/.schema/struct:")) {
|
2024-01-05 16:24:31 -08:00
|
|
|
std::string_view schema{reinterpret_cast<const char*>(data.data()),
|
|
|
|
|
data.size()};
|
|
|
|
|
std::string err;
|
2024-06-04 21:01:52 -07:00
|
|
|
auto desc = m_structDb.Add(*typeStr, schema, &err);
|
2024-01-05 16:24:31 -08:00
|
|
|
if (!desc) {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("could not decode struct '{}' schema '{}': {}\n", name,
|
2024-01-05 16:24:31 -08:00
|
|
|
schema, err);
|
|
|
|
|
}
|
2024-06-04 21:01:52 -07:00
|
|
|
} else if (auto filename = wpi::remove_prefix(name, "/.schema/proto:")) {
|
2024-09-12 23:44:19 -07:00
|
|
|
#ifndef NO_PROTOBUF
|
2024-01-05 16:24:31 -08:00
|
|
|
// protobuf descriptor handling
|
2024-06-04 21:01:52 -07:00
|
|
|
if (!m_protoDb.Add(*filename, data)) {
|
2024-05-12 06:25:42 -07:00
|
|
|
wpi::print("could not decode protobuf '{}' filename '{}'\n", name,
|
2024-06-04 21:01:52 -07:00
|
|
|
*filename);
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
2024-09-12 23:44:19 -07:00
|
|
|
#endif
|
2024-01-05 16:24:31 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sigDone();
|
|
|
|
|
m_done = true;
|
|
|
|
|
}
|