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),
ui(new Ui::OverlayFrame)
{
// TODO: forward the scroll/key events to the window below
ui->setupUi(this);
}

View File

@ -43,36 +43,57 @@ RemoteTable::RemoteTable(QWidget* parent)
setRowCount(0x2000ull);
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)
{
// TODO: get data from a container that's aware of the outgoing requests
// (perhaps this should be done transparently in the request/response handling?)
auto relativeRow = row - getTableOffset();
// Show the real row number for debugging purposes
QString base;
if(col == 0)
{
base = QString("[row: %1] ").arg(row, 5);
}
if(relativeRow < mRemoteData.size())
{
const auto & remoteRow = mRemoteData[relativeRow];
QString data;
if(col < remoteRow.size())
{
data = QString::fromStdString(remoteRow[col]);
}
else
{
data = "(BAD SERVER)";
}
return base + data;
}
else
// Ignore out of range data
dsint relativeRow = row - mRemoteTableOffset;
if(relativeRow < 0 || relativeRow >= mRemoteData.size())
{
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)
@ -94,6 +115,14 @@ void RemoteTable::sortRows(duint column, bool ascending)
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]()
{
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 some basic prediction heuristics (scroll direction etc) to anticipate
// the scroll (request a bigger range)
// TODO: measure the ping
// TODO: implement partial reuse of the data
TableRequest r;
r.offset = offset;
r.lines = linesToPrint;
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)
{
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)
{
// 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;
// TODO: use these statistics to send a deferred request a bit earlier
// Handle deferred request
if(mNextRequired)
{
@ -269,6 +317,7 @@ void RemoteTable::handleTableResponse(const TableResponse & response)
mOverlay->setVisible(false);
// Update the displayed data with the final response
mRemoteTableOffset = mCurrentRequest.offset;
mRemoteData = std::move(response.rows);
updateViewport();
}

View File

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