Skip to content
Snippets Groups Projects
Verified Commit 9a20e7cb authored by Burak Hançerli's avatar Burak Hançerli :headphones:
Browse files

add: network connection between DS and DV

parent 7ea8da7f
No related branches found
No related tags found
2 merge requests!22QDS-11332 Implement better qmlproject handling,!21Design studio connector
Pipeline #64494 passed
......@@ -80,12 +80,6 @@ void Backend::initialize()
this,
&Backend::popupTextChanged);
connect(&m_designStudioConnector,
&DesignStudioConnector::networkStatusUpdated,
this,
&Backend::networkUpdated);
m_designStudioConnector.initialize();
qDebug("Initialization complete");
}
......@@ -103,6 +97,90 @@ void Backend::updatePopup(const QString &text, bool indeterminate)
QEventLoop().processEvents(QEventLoop::AllEvents, 100);
}
bool Backend::connectDesignStudio()
{
if (m_designStudioConnector) {
qDebug() << "Design Studio Connector is already initialized";
return true;
}
qDebug() << "Initializing Design Studio Connector";
connect(
&m_dsConnectorThread,
&QThread::started,
this,
[&] {
m_designStudioConnector.reset(new DesignStudioConnector);
connect(m_designStudioConnector.data(),
&DesignStudioConnector::networkStatusUpdated,
this,
&Backend::networkUpdated);
connect(m_designStudioConnector.data(),
&DesignStudioConnector::projectReceived,
&m_projectManager,
[this](const QByteArray &projectData) {
qDebug() << "Project received from Design Studio";
emit popupOpen();
updatePopup("Unpacking project...");
qDebug() << "Project data size: " << projectData.size();
const QString projectPath = m_projectManager.unpackProject(projectData);
if (projectPath.isEmpty()) {
showWarning("Could not unpack project. Please check the logs for more "
"information.");
emit popupClose();
return;
}
qDebug() << "Project unpacked to " << projectPath;
updatePopup("Running project...");
if (!m_projectManager.runProject(projectPath, "UntitledProject35")) {
showWarning("Could not run project. Please check the logs for more "
"information.");
} else {
m_projectManager.showAppWindow();
}
emit popupClose();
});
if (!m_designStudioConnector->initialize()) {
showWarning("Could initialize server. Please check the logs for more information.");
m_designStudioConnector.reset();
}
},
Qt::DirectConnection);
m_dsConnectorThread.start();
qDebug() << "Design Studio Connector is initialized";
return true;
}
void Backend::disconnectDesignStudio()
{
if (!m_designStudioConnector) {
qDebug() << "Design Studio Connector is not initialized";
return;
}
qDebug() << "Disconnecting from Design Studio";
connect(
&m_dsConnectorThread,
&QThread::finished,
this,
[&] {
qDebug() << "Design Studio Connector is disconnected";
m_designStudioConnector.reset();
},
Qt::DirectConnection);
m_dsConnectorThread.quit();
m_dsConnectorThread.wait();
}
void Backend::runDemoProject(const QString &projectName)
{
qDebug() << "Checking if demo project is cached for " << projectName;
......
......@@ -26,6 +26,8 @@
#ifndef DV_ANDROID_H
#define DV_ANDROID_H
#include <QThread>
#include "dsConnector.h"
#include "projectManager.h"
#include "serviceConnector.h"
......@@ -58,7 +60,8 @@ private:
QString m_userHash;
ServiceConnector m_serviceConnector;
ProjectManager m_projectManager;
DesignStudioConnector m_designStudioConnector;
QScopedPointer<DesignStudioConnector> m_designStudioConnector;
QThread m_dsConnectorThread;
// member functions
void showWarning(const QString &message);
......@@ -83,6 +86,8 @@ public slots:
void runDemoProject(const QString &projectName);
void clearDemoCaches();
void registerUser(const QUrl &url);
bool connectDesignStudio();
void disconnectDesignStudio();
};
#endif // DV_ANDROID_H
......@@ -27,57 +27,111 @@
#include <QNetworkInterface>
DesignStudioConnector::DesignStudioConnector(QObject *parent)
: QObject(parent)
, m_socket(this)
, m_broadcastTimer(this)
{}
void DesignStudioConnector::receiveProject()
{
qDebug() << "Reading data from Design Studio";
emit networkStatusUpdated("Reading data from Design Studio");
QByteArray data = m_tcpSocket->readAll();
qDebug() << "Data from Design Studio:" << data;
// if (data.startsWith(":qmlrc_project_starts:")) {
// qDebug() << "Received qmlrc_project";
// emit networkStatusUpdated("Received qmlrc_project");
// m_projectData.append(data.mid(21, data.size() - 21));
// m_receivingData = true;
// } else if (data.contains(":qmlrc_project_ends:")) {
// qDebug() << "Received qmlrc_project_ends";
// emit networkStatusUpdated("Received qmlrc_project_ends");
// m_projectData.append(data.mid(0, data.size() - 20));
// emit projectReceived(m_projectData);
// m_projectData.clear();
// m_receivingData = false;
// } else if (m_receivingData) {
// qDebug() << "Received data:" << data;
// emit networkStatusUpdated("Receiving project data");
// m_projectData.append(data);
// } else {
// qDebug() << "Received unknown data:" << data;
// emit networkStatusUpdated("Received unknown data");
// }
m_projectData.append(data);
}
bool DesignStudioConnector::initialize()
bool DesignStudioConnector::initTcpServer()
{
// const bool retVal = m_socket.bind(39000);
const bool retVal = m_tcpServer.listen(QHostAddress::Any, m_tcpPort);
// if (!retVal) {
// qDebug() << "Failed to bind socket";
// emit networkStatusUpdated("Failed to bind socket on port 39000");
// return false;
// }
if (!retVal) {
qDebug() << "Failed to listen on port " << m_tcpPort;
emit networkStatusUpdated("Failed to bind on port " + QString::number(m_tcpPort));
return false;
}
qDebug() << "Listening on port " << m_tcpPort;
connect(&m_tcpServer, &QTcpServer::newConnection, this, [this]() {
qDebug() << "New connection from Design Studio";
emit networkStatusUpdated("Design Studio is connected.\n Waiting for project...");
m_tcpSocket.reset(m_tcpServer.nextPendingConnection());
m_broadcastTimer.stop();
connect(m_tcpSocket.data(), &QTcpSocket::disconnected, this, [this]() {
qDebug() << "Disconnected from Design Studio";
emit networkStatusUpdated("\nLocal IP: " + m_ipv4Addr
+ "\nWaiting for Design Studio to connect...");
m_broadcastTimer.start();
m_receivingData = false;
emit projectReceived(m_projectData);
m_projectData.clear();
});
connect(m_tcpSocket.data(),
&QTcpSocket::readyRead,
this,
&DesignStudioConnector::receiveProject);
});
return true;
}
void DesignStudioConnector::initBroadcast()
{
// get ipv4 address
QString ipv4Addr;
const QList<QHostAddress> list = QNetworkInterface::allAddresses();
for (const QHostAddress &address : list) {
if (address.protocol() == QAbstractSocket::IPv4Protocol
&& address != QHostAddress::LocalHost) {
qDebug() << "Found IPv4 address:" << address.toString();
ipv4Addr = address.toString();
m_ipv4Addr = address.toString();
break;
}
}
// connect(&m_socket, &QUdpSocket::readyRead, this, &DesignStudioConnector::readPendingDatagrams);
qDebug() << "Advertising from" << ipv4Addr;
emit networkStatusUpdated("\nLocal IP: " + ipv4Addr
qDebug() << "Advertising from" << m_ipv4Addr;
emit networkStatusUpdated("\nLocal IP: " + m_ipv4Addr
+ "\nWaiting for Design Studio to connect...");
// Broadcast a message to the network in each 2 seconds
// to let the Design Studio know that we are here
connect(&m_broadcastTimer, &QTimer::timeout, this, [this]() {
QByteArray datagram = "Qt Design Viewer";
m_socket.writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, 39000);
QByteArray datagram = "qt_ui_viewer_at:" + m_ipv4Addr.toUtf8() + ":"
+ QByteArray::number(m_tcpPort);
m_udpSocket.writeDatagram(datagram.data(),
datagram.size(),
QHostAddress::Broadcast,
m_udpPort);
qDebug() << "Broadcasting datagram:" << datagram;
});
m_broadcastTimer.setSingleShot(false);
m_broadcastTimer.start(2000);
return true;
}
void DesignStudioConnector::readPendingDatagrams()
bool DesignStudioConnector::initialize()
{
while (m_socket.hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(m_socket.pendingDatagramSize());
m_socket.readDatagram(datagram.data(), datagram.size());
qDebug() << "Received datagram:" << datagram;
}
if (!initTcpServer())
return false;
initBroadcast();
return true;
}
......@@ -27,6 +27,8 @@
#define DSCONNECTOR_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QUdpSocket>
......@@ -34,18 +36,35 @@ class DesignStudioConnector : public QObject
{
Q_OBJECT
public:
explicit DesignStudioConnector(QObject *parent = nullptr);
explicit DesignStudioConnector(QObject *parent = nullptr)
: QObject(parent)
{}
bool initialize();
private slots:
void readPendingDatagrams();
private:
QUdpSocket m_socket;
// Tcp connection members
QTcpServer m_tcpServer;
QScopedPointer<QTcpSocket> m_tcpSocket;
const quint32 m_tcpPort = 40000;
// Udp connection members
QUdpSocket m_udpSocket;
QTimer m_broadcastTimer;
QString m_ipv4Addr;
const quint32 m_udpPort = 39000;
// Other members
QByteArray m_projectData;
bool m_receivingData;
// Member functions
bool initTcpServer();
void initBroadcast();
void receiveProject();
signals:
void networkStatusUpdated(QString);
void projectReceived(QByteArray);
};
#endif // DSCONNECTOR_H
......@@ -213,11 +213,16 @@ bool ProjectManager::runProject(const QString &projectPath, const QString &proje
QUrl mainQmlUrl = QUrl::fromUserInput(mainQmlFilePath);
QFile file(mainQmlUrl.path());
// QFile file(mainQmlUrl.path().prepend(":"));
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
return false;
qWarning() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
qWarning() << "Trying to open it as a resource file.";
file.setFileName(mainQmlUrl.path().prepend(":"));
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
return false;
}
}
qDebug() << "Looking for qtquickcontrols2File in " << projectPath;
......@@ -226,75 +231,76 @@ bool ProjectManager::runProject(const QString &projectPath, const QString &proje
if (!qtquickcontrols2File.isEmpty()) {
qDebug() << "Found qtquickcontrols2File: " << qtquickcontrols2File;
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1());
}
}
qDebug() << "Initializing the qmlEngine";
m_qmlEngine.reset(new QQmlEngine);
m_qmlEngine->clearComponentCache();
qDebug() << "Initializing the qmlEngine";
m_qmlEngine.reset(new QQmlEngine);
m_qmlEngine->clearComponentCache();
qDebug("Adding import paths");
for (const QString &importPath : importPaths) {
qDebug() << "-- Import path: " << importPath;
m_qmlEngine->addImportPath(importPath);
}
qDebug("Adding import paths");
for (const QString &importPath : importPaths) {
qDebug() << "-- Import path: " << importPath;
m_qmlEngine->addImportPath(importPath);
}
QObject::connect(m_qmlEngine.data(),
&QQmlEngine::warnings,
this,
[&](const QList<QQmlError> &warnings) {
for (const auto &warning : warnings) {
qWarning() << warning.toString();
}
});
emit projectStateChanged("Loading project...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
qDebug() << "Loading mainQmlUrl: " << mainQmlUrl.toString();
m_qmlComponent.reset(new QQmlComponent(m_qmlEngine.data()));
m_qmlComponent->loadUrl(mainQmlUrl);
qDebug() << "Waiting for qmlComponent to load";
while (m_qmlComponent->isLoading())
QCoreApplication::processEvents();
qDebug() << "Checking if m_qmlComponent is ready";
if (!m_qmlComponent->isReady()) {
qCritical() << "m_qmlComponent is not ready. Reason:" << m_qmlComponent->errorString();
return false;
}
qDebug() << "Creating top level object";
QObject *topLevel = m_qmlComponent->create();
if (!topLevel && m_qmlComponent->isError()) {
qCritical() << "Error while creating Qml m_qmlComponent:" << m_qmlComponent->errorString();
return false;
}
QObject::connect(m_qmlEngine.data(),
&QQmlEngine::warnings,
this,
[&](const QList<QQmlError> &warnings) {
for (const auto &warning : warnings) {
qWarning() << warning.toString();
}
});
emit projectStateChanged("Loading project...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
qDebug() << "Loading mainQmlUrl: " << mainQmlUrl.toString();
m_qmlComponent.reset(new QQmlComponent(m_qmlEngine.data()));
m_qmlComponent->loadUrl(mainQmlUrl);
qDebug() << "Waiting for qmlComponent to load";
while (m_qmlComponent->isLoading())
QCoreApplication::processEvents();
qDebug() << "Checking if m_qmlComponent is ready";
if (!m_qmlComponent->isReady()) {
qCritical() << "m_qmlComponent is not ready. Reason:" << m_qmlComponent->errorString();
return false;
}
qDebug() << "Creating top level object";
QObject *topLevel = m_qmlComponent->create();
if (!topLevel && m_qmlComponent->isError()) {
qCritical() << "Error while creating Qml m_qmlComponent:"
<< m_qmlComponent->errorString();
return false;
}
emit projectStateChanged("Setting up the quickWindow...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
emit projectStateChanged("Setting up the quickWindow...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
qDebug() << "Setting up the quickWindow";
m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel));
if (m_quickWindow) {
qDebug() << "Running with incubator controller";
m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
} else {
qWarning() << "Top level object is not a QQuickWindow. Trying QQuickView...";
qDebug() << "Setting up the quickWindow";
m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel));
if (m_quickWindow) {
qDebug() << "Running with incubator controller";
m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
} else {
qWarning() << "Top level object is not a QQuickWindow. Trying QQuickView...";
QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
if (!contentItem) {
qCritical() << "Top level object cannot be casted to QQuickItem. Aborting.";
return false;
}
QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
if (!contentItem) {
qCritical() << "Top level object cannot be casted to QQuickItem. Aborting.";
return false;
}
qDebug() << "Initializing QQuickView";
QQuickView *view = new QQuickView(m_qmlEngine.data(), nullptr);
m_quickWindow.reset(view);
view->setContent(mainQmlUrl, m_qmlComponent.data(), contentItem);
view->setResizeMode(QQuickView::SizeViewToRootObject);
m_quickWindow->setBaseSize(QSize(contentItem->width(), contentItem->height()));
}
return true;
qDebug() << "Initializing QQuickView";
QQuickView *view = new QQuickView(m_qmlEngine.data(), nullptr);
m_quickWindow.reset(view);
view->setContent(mainQmlUrl, m_qmlComponent.data(), contentItem);
view->setResizeMode(QQuickView::SizeViewToRootObject);
m_quickWindow->setBaseSize(QSize(contentItem->width(), contentItem->height()));
}
return true;
}
bool ProjectManager::cacheProject(const QByteArray &projectData, const QJsonObject &projectInfo)
......
......@@ -22,17 +22,18 @@ Item {
id: infoHeader
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
horizontalAlignment: "AlignHCenter"
text: qsTr("Design Studio connection")
text: qsTr("Design Studio Connection Menu")
}
Label {
id: status
id: statusLabel
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
horizontalAlignment: "AlignHCenter"
text: "Click the button below to start\nadvertising this device on the network"
Connections {
target: backend
function onNetworkUpdated(newStatus){
status.text = newStatus
statusLabel.text = newStatus
}
}
}
......@@ -42,5 +43,28 @@ Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
Button {
property bool isRunning: false
id: startConnection
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.fillWidth: true
height: 100
text: qsTr("Start Connection")
onClicked: {
if (isRunning) {
backend.disconnectDesignStudio()
isRunning = false
text = qsTr("Start Connection")
statusLabel.text = "Click the button below to start\nadvertising this device on the network"
} else {
let retval = backend.connectDesignStudio()
if (retval) {
text = qsTr("Stop Connection")
isRunning = true
}
}
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment