From 937b65cd095daa489b3f39128be9761eafd28824 Mon Sep 17 00:00:00 2001 From: Jason N Date: Wed, 15 Nov 2023 20:21:43 +1100 Subject: [PATCH] Update visualisation server --- MiniZincIDE/mainwindow.cpp | 26 +- MiniZincIDE/mainwindow.h | 2 +- MiniZincIDE/server.cpp | 45 ++- MiniZincIDE/server.h | 9 +- MiniZincIDE/server/index.html | 535 +++++++++++++++++----------------- 5 files changed, 315 insertions(+), 302 deletions(-) diff --git a/MiniZincIDE/mainwindow.cpp b/MiniZincIDE/mainwindow.cpp index 752c5a0..d1b2eb0 100644 --- a/MiniZincIDE/mainwindow.cpp +++ b/MiniZincIDE/mainwindow.cpp @@ -1334,10 +1334,13 @@ void MainWindow::run(const SolverConfiguration& sc, const QString& model, const } ui->outputWidget->addText("\n", ui->outputWidget->infoCharFormat(), "trace"); } else { - ui->outputWidget->addTextToSection(section, message.toString(), ui->outputWidget->commentCharFormat()); - if (vis_connector == nullptr && section == "vis_json") { + auto text = message.toString(); + if (!text.isEmpty()) { + ui->outputWidget->addTextToSection(section, text, ui->outputWidget->commentCharFormat()); + } + if (vis_connector == nullptr && section.startsWith("mzn_vis_")) { auto obj = message.toJsonObject(); - startVisualisation(model, data, obj["url"].toString(), obj["userData"], proc); + startVisualisation(model, data, section, obj["url"].toString(), obj["userData"], proc); } } }); @@ -3076,7 +3079,7 @@ QString MainWindow::locationToLink(const QString& file, int firstLine, int first .arg(position); } -void MainWindow::startVisualisation(const QString& model, const QStringList& data, const QString& url, const QJsonValue& userData, MznProcess* proc) +void MainWindow::startVisualisation(const QString& model, const QStringList& data, const QString& key, const QString& url, const QJsonValue& userData, MznProcess* proc) { if (server == nullptr) { server = new Server(this); @@ -3129,22 +3132,25 @@ void MainWindow::startVisualisation(const QString& model, const QStringList& dat run(rsc, m, df); }); connect(proc, &MznProcess::traceOutput, this, [=] (const QString& section, const QVariant& message) { - if (section == "vis_json") { + if (section.startsWith("mzn_vis_")) { auto obj = message.toJsonObject(); - vis_connector->addWindow(obj["url"].toString(), obj["userData"]); + vis_connector->addWindow(section, obj["url"].toString(), obj["userData"]); } }); connect(proc, &MznProcess::solutionOutput, [=](const QVariantMap& output, const QStringList& sections, qint64 time) { - if (output.contains("vis_json")) { - auto items = output["vis_json"].toJsonArray(); - vis_connector->addSolution(items, time == -1 ? proc->elapsedTime() : time); + QJsonObject solution; + for (auto it = output.begin(); it != output.end(); it++) { + if (it.key().startsWith("mzn_vis_")) { + solution[it.key()] = it.value().toJsonValue(); + } } + vis_connector->addSolution(solution, time == -1 ? proc->elapsedTime() : time); }); connect(proc, &MznProcess::finalStatus, [=](const QString& status, qint64 time) { vis_connector->setFinalStatus(status, time == -1 ? proc->elapsedTime() : time); }); connect(proc, &MznProcess::finished, vis_connector, &VisConnector::setFinished); - vis_connector->addWindow(url, userData); + vis_connector->addWindow(key, url, userData); ui->outputWidget->associateServerUrl(vis_connector->url().toString()); diff --git a/MiniZincIDE/mainwindow.h b/MiniZincIDE/mainwindow.h index 0081ef9..e6b4052 100755 --- a/MiniZincIDE/mainwindow.h +++ b/MiniZincIDE/mainwindow.h @@ -333,7 +333,7 @@ private slots: QString locationToLink(const QString& filename, int firstLine, int firstColumn, int lastLine, int lastColumn, const QColor& color); - void startVisualisation(const QString& model, const QStringList& data, const QString& url, const QJsonValue& userData, MznProcess* proc); + void startVisualisation(const QString& model, const QStringList& data, const QString& key, const QString& url, const QJsonValue& userData, MznProcess* proc); public: void addOutput(const QString& s, bool html=true); void openProject(const QString& fileName); diff --git a/MiniZincIDE/server.cpp b/MiniZincIDE/server.cpp index 5330bb0..73f8fdc 100644 --- a/MiniZincIDE/server.cpp +++ b/MiniZincIDE/server.cpp @@ -35,32 +35,31 @@ void VisConnector::webSocketClientDisconnected() } } -void VisConnector::addWindow(const QString& url, const QJsonValue& userData) +void VisConnector::addWindow(const QString& key, const QString& url, const QJsonValue& userData) { - _windows << QJsonObject({{"url", url}, {"userData", userData}}); - _solutions.append(QJsonArray()); + _windows[key] = QJsonObject({{"url", url}, {"userData", userData}}); + _solutions[key] = QJsonArray(); QJsonObject obj({{"event", "window"}, + {"windowId", key}, {"url", url}, {"userData", userData}}); broadcastMessage(QJsonDocument(obj)); } -void VisConnector::addSolution(const QJsonArray& items, qint64 time) +void VisConnector::addSolution(const QJsonObject& solution, qint64 time) { - if (_solutions.isEmpty() || items.size() != _solutions.size()) { - assert(false); - return; - } - auto solIndex = _solutions.first().size(); - for (auto i = 0; i < items.size(); i++) { - QJsonObject it({{"time", time}, - {"data", items[i]}, + auto solIndex = _solutionCount++; + for (auto it = solution.begin(); it != solution.end(); it++) { + QJsonObject sol({{"time", time}, + {"data", it.value()}, {"index", solIndex}}); - _solutions[i].append(it); + if (_solutions.contains(it.key())) { + _solutions[it.key()].append(sol); + } } QJsonObject obj({{"event", "solution"}, {"time", time}, - {"items", items}, + {"solution", solution}, {"index", solIndex}}); broadcastMessage(QJsonDocument(obj)); } @@ -117,8 +116,8 @@ void VisConnector::webSocketMessageReceived(const QString& message) {"payload", _solutions.isEmpty() ? 0 : _solutions.first().size()}}); socket->sendTextMessage(QString::fromUtf8(QJsonDocument(obj).toJson())); } else if (event == "getSolution") { - auto windowIndex = msg["window"].toInt(); - if (windowIndex < 0 || windowIndex >= _solutions.size()) { + auto window_id = msg["window"].toString(); + if (!_solutions.contains(window_id)) { QJsonObject obj({{"event", "error"}, {"id", msg["id"]}, {"window", msg["window"]}, @@ -128,9 +127,9 @@ void VisConnector::webSocketMessageReceived(const QString& message) } auto idx = msg["index"].toInt(); if (idx < 0) { - idx += _solutions[windowIndex].size(); + idx += _solutions[window_id].count(); } - if (idx < 0 || idx >= _solutions[windowIndex].size()) { + if (idx < 0 || idx >= _solutions[window_id].count()) { QJsonObject obj({{"event", "error"}, {"id", msg["id"]}, {"window", msg["window"]}, @@ -141,11 +140,11 @@ void VisConnector::webSocketMessageReceived(const QString& message) QJsonObject obj({{"event", "response"}, {"id", msg["id"]}, {"window", msg["window"]}, - {"payload", _solutions[windowIndex][idx]}}); + {"payload", _solutions[window_id][idx]}}); socket->sendTextMessage(QString::fromUtf8(QJsonDocument(obj).toJson())); } else if (event == "getAllSolutions") { - auto windowIndex = msg["window"].toInt(); - if (windowIndex < 0 || windowIndex >= _solutions.size()) { + auto window_id = msg["window"].toString(); + if (!_solutions.contains(window_id)) { QJsonObject obj({{"event", "error"}, {"id", msg["id"]}, {"window", msg["window"]}, @@ -155,8 +154,8 @@ void VisConnector::webSocketMessageReceived(const QString& message) } QJsonObject obj({{"event", "response"}, {"id", msg["id"]}, - {"window", windowIndex}, - {"payload", _solutions[windowIndex]}}); + {"window", window_id}, + {"payload", _solutions[window_id]}}); socket->sendTextMessage(QString::fromUtf8(QJsonDocument(obj).toJson())); } else if (event == "getStatus") { QJsonObject obj({{"event", "response"}, diff --git a/MiniZincIDE/server.h b/MiniZincIDE/server.h index 36969bf..2cc29d0 100644 --- a/MiniZincIDE/server.h +++ b/MiniZincIDE/server.h @@ -15,9 +15,10 @@ class VisConnector : public QObject { QStringList _roots; QList _clients; - QJsonArray _windows; - QVector _solutions; + QJsonObject _windows; + QMap _solutions; QJsonValue _finalStatus; + int _solutionCount = 0; qint64 _finishTime = -1; QUrl _url; @@ -33,8 +34,8 @@ class VisConnector : public QObject { void solveRequested(const QString& modelFile, bool dataFilesGiven, const QStringList& dataFiles, const QVariantMap& options); public slots: - void addWindow(const QString& url, const QJsonValue& userData); - void addSolution(const QJsonArray& items, qint64 time); + void addWindow(const QString& key, const QString& url, const QJsonValue& userData); + void addSolution(const QJsonObject& solution, qint64 time); void setFinalStatus(const QString& status, qint64 time); void setFinished(qint64 time); diff --git a/MiniZincIDE/server/index.html b/MiniZincIDE/server/index.html index 45c8cb5..b66b77f 100644 --- a/MiniZincIDE/server/index.html +++ b/MiniZincIDE/server/index.html @@ -1,294 +1,301 @@ - - - %3 | MiniZincIDE - - - -
-
- - - - - -
-
-
-
+ #wrapper { + flex: 1 0 auto; + } + + #windows { + width: 100%; + height: 100%; + display: grid; + grid-auto-rows: auto; + gap: 1px; + background: #CCC; + } + + #windows>* { + background: #FFF; + border: 0; + width: 100%; + height: 100%; + } + + #notice { + width: 100vw; + position: absolute; + top: 20vw; + left: 0; + text-align: center; + } + + #notice:empty { + display: none; + } + + .hidden { + display: none !important; + } + + + + +
+
+ + + + +
-
Connecting...
- - - + } + }, 1); + + + + \ No newline at end of file