Play around with debouncing slow events in the remote table

This commit is contained in:
Duncan Ogilvie 2024-10-11 22:43:00 +02:00
parent bc7cae2f28
commit 2c3c3416f2
3 changed files with 82 additions and 22 deletions

View File

@ -20,6 +20,7 @@ OverlayFrame::OverlayFrame(QWidget* parent) :
QFrame(parent), QFrame(parent),
ui(new Ui::OverlayFrame) ui(new Ui::OverlayFrame)
{ {
// TODO: forward the scroll/key events to the window below
ui->setupUi(this); ui->setupUi(this);
} }

View File

@ -43,36 +43,57 @@ RemoteTable::RemoteTable(QWidget* parent)
setRowCount(0x2000ull); setRowCount(0x2000ull);
mOverlay = OverlayFrame::embed(this, false); mOverlay = OverlayFrame::embed(this, false);
mScrollTimer = new QTimer(this);
mScrollTimer->setSingleShot(true);
mScrollTimer->setInterval(500); // TODO: this interval should probably be the latency
connect(mScrollTimer, &QTimer::timeout, this, [this]
{
qDebug() << "Total scroll for debounce interval: " << mScrollDelta;
uint64_t minTime = UINT64_MAX;
uint64_t maxTime = 0;
dsint totalDelta = 0;
for(const auto&[time,delta] : mScrollEvents) {
uint64_t timeMs = std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count();
minTime = std::min(minTime, timeMs);
maxTime = std::max(maxTime, timeMs);
totalDelta += delta;
}
auto totalMs = maxTime - minTime;
auto div = totalMs / 1000.0;
qDebug() << "time:" << totalMs << "total:" << totalDelta << "velocity:" << totalDelta / div << "(lines/second)";
mScrollEvents.clear();
mScrollDelta = 0;
});
} }
QString RemoteTable::getCellContent(duint row, duint col) QString RemoteTable::getCellContent(duint row, duint col)
{ {
// TODO: get data from a container that's aware of the outgoing requests // Show the real row number for debugging purposes
// (perhaps this should be done transparently in the request/response handling?)
auto relativeRow = row - getTableOffset();
QString base; QString base;
if(col == 0) if(col == 0)
{ {
base = QString("[row: %1] ").arg(row, 5); base = QString("[row: %1] ").arg(row, 5);
} }
if(relativeRow < mRemoteData.size())
{ // Ignore out of range data
const auto & remoteRow = mRemoteData[relativeRow]; dsint relativeRow = row - mRemoteTableOffset;
QString data; if(relativeRow < 0 || relativeRow >= mRemoteData.size())
if(col < remoteRow.size())
{
data = QString::fromStdString(remoteRow[col]);
}
else
{
data = "(BAD SERVER)";
}
return base + data;
}
else
{ {
return base + "(FETCHING DATA...)"; return base + "(FETCHING DATA...)";
} }
// Display the data we do have
const auto & remoteRow = mRemoteData[relativeRow];
QString data;
if(col < remoteRow.size())
{
data = QString::fromStdString(remoteRow[col]);
}
else
{
data = "(BAD SERVER)";
}
return base + data;
} }
duint RemoteTable::getCellUserdata(duint row, duint column) duint RemoteTable::getCellUserdata(duint row, duint column)
@ -94,6 +115,14 @@ void RemoteTable::sortRows(duint column, bool ascending)
duint RemoteTable::sliderMovedHook(QScrollBar::SliderAction action, duint value, dsint delta) duint RemoteTable::sliderMovedHook(QScrollBar::SliderAction action, duint value, dsint delta)
{ {
// Use a debounce timer to predict the scrolling
if(!mScrollTimer->isActive())
{
mScrollStart = std::chrono::system_clock::now();
}
mScrollDelta += delta;
mScrollTimer->start();
auto actionName = [action]() auto actionName = [action]()
{ {
switch(action) switch(action)
@ -166,14 +195,23 @@ void RemoteTable::prepareData()
// TODO: use the average/median/last latency of the connection as a debouncing timer // TODO: use the average/median/last latency of the connection as a debouncing timer
// TODO: use some basic prediction heuristics (scroll direction etc) to anticipate // TODO: use some basic prediction heuristics (scroll direction etc) to anticipate
// the scroll (request a bigger range) // the scroll (request a bigger range)
// TODO: measure the ping
// TODO: implement partial reuse of the data
TableRequest r; TableRequest r;
r.offset = offset; r.offset = offset;
r.lines = linesToPrint; r.lines = linesToPrint;
r.scroll = 0; // TODO: unused for now, used to scroll up in disassembly (variable size) r.scroll = 0; // TODO: unused for now, used to scroll up in disassembly (variable size)
if(mScrollDelta < 0)
{
// TODO: set scroll to -100 to get 100 extra line up
}
else if(mScrollDelta > 0)
{
// TODO: increase the lines based on scroll velocity
//r.lines *= 2;
}
//qDebug() << "scrollDelta:" << mScrollDelta;
if(!mCurrentSent) if(!mCurrentSent)
{ {
assert(!mNextRequired); assert(!mNextRequired);
@ -193,6 +231,14 @@ void RemoteTable::prepareData()
} }
} }
void RemoteTable::wheelEvent(QWheelEvent* event)
{
dsint prevScrollDelta = mScrollDelta;
AbstractStdTable::wheelEvent(event);
auto now = std::chrono::system_clock::now();
mScrollEvents.emplace_back(now, mScrollDelta - prevScrollDelta);
}
void RemoteTable::handleTableResponse(const TableResponse & response) void RemoteTable::handleTableResponse(const TableResponse & response)
{ {
// Calculate response time statistics // Calculate response time statistics
@ -230,6 +276,8 @@ void RemoteTable::handleTableResponse(const TableResponse & response)
qDebug() << "[response time] now:" << elapsedMs << "min:" << mMinResponseTime << "max:" << mMaxResponseTime << "avg:" << mAvgResponseTime << "med:" << mMedResponseTime; qDebug() << "[response time] now:" << elapsedMs << "min:" << mMinResponseTime << "max:" << mMaxResponseTime << "avg:" << mAvgResponseTime << "med:" << mMedResponseTime;
// TODO: use these statistics to send a deferred request a bit earlier
// Handle deferred request // Handle deferred request
if(mNextRequired) if(mNextRequired)
{ {
@ -269,6 +317,7 @@ void RemoteTable::handleTableResponse(const TableResponse & response)
mOverlay->setVisible(false); mOverlay->setVisible(false);
// Update the displayed data with the final response // Update the displayed data with the final response
mRemoteTableOffset = mCurrentRequest.offset;
mRemoteData = std::move(response.rows); mRemoteData = std::move(response.rows);
updateViewport(); updateViewport();
} }

View File

@ -8,6 +8,7 @@
#include <QWebSocket> #include <QWebSocket>
#include <QWidget> #include <QWidget>
#include <QTimer>
#include <deque> #include <deque>
class RemoteTable : public AbstractStdTable, public MagicMenu<RemoteTable> class RemoteTable : public AbstractStdTable, public MagicMenu<RemoteTable>
@ -24,6 +25,7 @@ protected:
void sortRows(duint column, bool ascending) override; void sortRows(duint column, bool ascending) override;
duint sliderMovedHook(QScrollBar::SliderAction action, duint value, dsint delta) override; duint sliderMovedHook(QScrollBar::SliderAction action, duint value, dsint delta) override;
void prepareData() override; void prepareData() override;
void wheelEvent(QWheelEvent* event) override;
private: private:
enum ColumnIndex enum ColumnIndex
@ -32,15 +34,18 @@ private:
ColData, ColData,
}; };
using Epoch = std::chrono::time_point<std::chrono::system_clock>;
duint mRemoteTableOffset = 0;
std::vector<std::vector<std::string>> mRemoteData; std::vector<std::vector<std::string>> mRemoteData;
std::chrono::time_point<std::chrono::system_clock> mLastPrepare; Epoch mLastPrepare;
QWebSocket mSocket; QWebSocket mSocket;
JsonRpcClient mRpc; JsonRpcClient mRpc;
TableRequest mCurrentRequest; TableRequest mCurrentRequest;
bool mCurrentSent = false; bool mCurrentSent = false;
std::chrono::time_point<std::chrono::system_clock> mCurrentSentTime; Epoch mCurrentSentTime;
TableRequest mNextRequest; TableRequest mNextRequest;
bool mNextRequired = false; bool mNextRequired = false;
@ -53,6 +58,11 @@ private:
uint64_t mAvgResponseTime = 100; uint64_t mAvgResponseTime = 100;
uint64_t mMedResponseTime = 100; uint64_t mMedResponseTime = 100;
QTimer* mScrollTimer = nullptr;
dsint mScrollDelta = 0;
Epoch mScrollStart;
std::deque<std::pair<Epoch, dsint>> mScrollEvents;
void handleTableResponse(const TableResponse & response); void handleTableResponse(const TableResponse & response);
void setupMenu(); void setupMenu();
void setupConnection(); void setupConnection();