summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Holland <dominik.holland@pelagicore.com>2019-03-21 15:09:49 +0100
committerDominik Holland <dominik.holland@pelagicore.com>2019-04-01 12:44:20 +0000
commit77d182798fa45a7dc16b65aefd35639a77c2fed2 (patch)
tree31e5ad882b493dc06a79da499f2e7f2d6fa52b25
parentbdf25ced227fbd0b5ec7ec36fe658e62bd763838 (diff)
downloadqtivi-77d182798fa45a7dc16b65aefd35639a77c2fed2.tar.gz
ivigenerator: Add support for pending server replies
By default QtRemoteObjects always wants to return a result for a function call right away. For function calls which take longer this doesn't work very well as the Qt main event loop is blocked until the result is ready and in the meantime no other communication can take place. This commit introduces a workaround for this limitation by generating a interface specific PendingResult object which can be returned as a placeholder to inform the client that the result is not yet ready. The actual result is emitted by the server using the pendingResultAvailable signal with the call-id and the result. To also support returning the result right away the return value always needs to be of type QVariant. This also has the good side effect that the client side always gets informed of whether a call has been finished or not as QtRemoteObjects doesn't provide QRemoteObjectPendingReply<void> for this. Change-Id: I68bf29b84b95042b69b5e1a18096f48ee9da7c7c Reviewed-by: Robert Griebl <robert.griebl@pelagicore.com>
-rw-r--r--examples/ivicore/qface-ivi-remote/server_qtro/processingservice.cpp2
-rw-r--r--examples/ivicore/qface-ivi-remote/server_qtro/processingservice.h2
-rw-r--r--src/tools/ivigenerator/common/interface.rep.tpl5
-rw-r--r--src/tools/ivigenerator/templates_backend_qtro/backend.cpp.tpl51
-rw-r--r--src/tools/ivigenerator/templates_backend_qtro/backend.h.tpl2
-rw-r--r--tests/auto/core/ivigenerator/org.example.echo.qtro.qface3
-rw-r--r--tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.cpp25
-rw-r--r--tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.h11
-rw-r--r--tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.cpp20
-rw-r--r--tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.h7
-rw-r--r--tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.cpp65
-rw-r--r--tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.h1
12 files changed, 166 insertions, 28 deletions
diff --git a/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.cpp b/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.cpp
index 3f20ec9..e266407 100644
--- a/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.cpp
+++ b/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.cpp
@@ -57,7 +57,7 @@ ProcessingService::ProcessingService()
setLastMessage(QStringLiteral("Service online."));
}
-int ProcessingService::process(const QString & data)
+QVariant ProcessingService::process(const QString & data)
{
setLastMessage(QStringLiteral("Processed data \'%1\'").arg(data));
return data.length();
diff --git a/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.h b/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.h
index 8c7f6b2..1f65e82 100644
--- a/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.h
+++ b/examples/ivicore/qface-ivi-remote/server_qtro/processingservice.h
@@ -60,7 +60,7 @@ class ProcessingService : public ProcessingServiceSimpleSource
public:
ProcessingService();
- int process(const QString & data) override;
+ QVariant process(const QString & data) override;
};
//! [0]
#endif // PROCESSINGSERVICE_H
diff --git a/src/tools/ivigenerator/common/interface.rep.tpl b/src/tools/ivigenerator/common/interface.rep.tpl
index 7297971..46dcc02 100644
--- a/src/tools/ivigenerator/common/interface.rep.tpl
+++ b/src/tools/ivigenerator/common/interface.rep.tpl
@@ -56,6 +56,8 @@
{{inc}}
{% endfor %}
+POD {{interface}}PendingResult(quint64 id, bool failed)
+
class {{class}}
{
{% for property in interface.properties %}
@@ -82,9 +84,10 @@ class {{class}}
{% endif %}
{% for operation in interface.operations %}
- SLOT({{operation|return_type}} {{operation}}({{ivi.join_params(operation, zoned = interface_zoned)}}))
+ SLOT(QVariant {{operation}}({{ivi.join_params(operation, zoned = interface_zoned)}}))
{% endfor %}
+ SIGNAL(pendingResultAvailable(quint64 id, bool isSuccess, const QVariant &value))
{% for signal in interface.signals %}
SIGNAL({{signal}}({{ivi.join_params(signal, zoned = interface_zoned)}}))
{% endfor %}
diff --git a/src/tools/ivigenerator/templates_backend_qtro/backend.cpp.tpl b/src/tools/ivigenerator/templates_backend_qtro/backend.cpp.tpl
index a2a5e5f..9d4fddf 100644
--- a/src/tools/ivigenerator/templates_backend_qtro/backend.cpp.tpl
+++ b/src/tools/ivigenerator/templates_backend_qtro/backend.cpp.tpl
@@ -48,6 +48,8 @@
#include <QSettings>
#include "{{module.module_name|lower}}module.h"
+Q_LOGGING_CATEGORY(qLcRO{{interface}}, "{{module|qml_type|lower}}.{{interface|lower}}backend.remoteobjects", QtInfoMsg)
+
{% for property in interface.properties %}
{% if property.type.is_model %}
{% include "pagingmodel.cpp.tpl" %}
@@ -245,22 +247,40 @@ QStringList {{class}}::availableZones() const
{% endif %}
{% set function_parameters = function_parameters + 'zone' %}
{% endif%}
-{% if not operation.type.is_void %}
- QRemoteObjectPendingReply<{{operation|return_type}}> reply = m_replica->{{operation}}({{function_parameters}});
+ qCDebug(qLcRO{{interface}}) << "{{operation}} called";
+ QRemoteObjectPendingReply<QVariant> reply = m_replica->{{operation}}({{function_parameters}});
auto watcher = new QRemoteObjectPendingCallWatcher(reply);
connect(watcher, &QRemoteObjectPendingCallWatcher::finished, this, [this, iviReply](QRemoteObjectPendingCallWatcher *self) mutable {
if (self->error() == QRemoteObjectPendingCallWatcher::NoError) {
- iviReply.setSuccess(self->returnValue().value<{{operation|return_type}}>());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1)
+ QVariant value = self->returnValue();
+#else
+ QVariant value = self->returnValue().value<QVariant>();
+#endif
+ if (value.canConvert<{{interface}}PendingResult>()) {
+ {{interface}}PendingResult result = value.value<{{interface}}PendingResult>();
+ if (result.failed()) {
+ qCDebug(qLcRO{{interface}}) << "Pending Result with id:" << result.id() << "failed";
+ iviReply.setFailed();
+ } else {
+ qCDebug(qLcRO{{interface}}) << "Result not available yet. Waiting for id:" << result.id();
+ m_pendingReplies.insert(result.id(), iviReply);
+ }
+ } else {
+{% if operation.type.is_void %}
+ qCDebug(qLcRO{{interface}}) << "Got the value right away: void";
+ iviReply.setSuccess();
+{% else %}
+ qCDebug(qLcRO{{interface}}) << "Got the value right away:" << value.value<{{operation|return_type}}>();
+ iviReply.setSuccess(value.value<{{operation|return_type}}>());
+{% endif %}
+ }
} else {
iviReply.setFailed();
emit errorChanged(QIviAbstractFeature::InvalidOperation, QStringLiteral("{{class}}, remote call of method {{operation}} failed"));
}
self->deleteLater();
});
-{% else %}
- m_replica->{{operation}}({{function_parameters}});
- iviReply.setSuccess();
-{% endif %}
return iviReply;
}
@@ -268,6 +288,7 @@ QStringList {{class}}::availableZones() const
void {{class}}::setupConnections()
{
+ connect(m_replica.data(), &{{interface}}Replica::pendingResultAvailable, this, &{{class}}::onPendingResultAvailable);
{% if interface_zoned %}
connect(m_replica.data(), &QRemoteObjectReplica::initialized, this, &{{class}}::syncZones);
{% else %}
@@ -318,6 +339,22 @@ void {{class}}::onNodeError(QRemoteObjectNode::ErrorCode code)
emit errorChanged(QIviAbstractFeature::Unknown, "QRemoteObjectNode error, code: " + code);
}
+void {{class}}::onPendingResultAvailable(quint64 id, bool isSuccess, const QVariant &value)
+{
+ qCDebug(qLcRO{{interface}}) << "pending result available" << id;
+ if (!m_pendingReplies.contains(id)) {
+ qCDebug(qLcRO{{interface}}) << "Received a result for an unexpected id:" << id << ". Ignoring!";
+ return;
+ }
+
+ QIviPendingReplyBase iviReply = m_pendingReplies.take(id);
+
+ if (isSuccess)
+ iviReply.setSuccess(value);
+ else
+ iviReply.setFailed();
+}
+
{% if interface_zoned %}
void {{class}}::onZoneSyncDone()
{
diff --git a/src/tools/ivigenerator/templates_backend_qtro/backend.h.tpl b/src/tools/ivigenerator/templates_backend_qtro/backend.h.tpl
index c0ebb61..04a0642 100644
--- a/src/tools/ivigenerator/templates_backend_qtro/backend.h.tpl
+++ b/src/tools/ivigenerator/templates_backend_qtro/backend.h.tpl
@@ -131,6 +131,7 @@ protected Q_SLOTS:
void onReplicaStateChanged(QRemoteObjectReplica::State newState,
QRemoteObjectReplica::State oldState);
void onNodeError(QRemoteObjectNode::ErrorCode code);
+ void onPendingResultAvailable(quint64 id, bool isSuccess, const QVariant &value);
{% if interface_zoned %}
void syncZones();
void onZoneSyncDone();
@@ -142,6 +143,7 @@ protected:
QSharedPointer<{{interface}}Replica> m_replica;
QRemoteObjectNode* m_node= nullptr;
QUrl m_url;
+ QHash<quint64, QIviPendingReplyBase> m_pendingReplies;
{% for property in interface.properties %}
{% if property.type.is_model %}
QIviPagingModelInterface *m_{{property}};
diff --git a/tests/auto/core/ivigenerator/org.example.echo.qtro.qface b/tests/auto/core/ivigenerator/org.example.echo.qtro.qface
index edbcd24..82f7f82 100644
--- a/tests/auto/core/ivigenerator/org.example.echo.qtro.qface
+++ b/tests/auto/core/ivigenerator/org.example.echo.qtro.qface
@@ -34,6 +34,8 @@ interface Echo {
Combo getCombo();
void voidSlot();
void voidSlot2(int param);
+ void timer(int interval);
+
signal anotherChanged(AnotherStruct another);
signal foobar(string foo);
signal somethingHappened();
@@ -103,6 +105,7 @@ interface EchoZoned {
string id();
var varMethod();
Combo getCombo();
+ string timer(int interval);
signal anotherChanged(AnotherStruct another);
signal foobar(string foo);
diff --git a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.cpp b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.cpp
index 57e4782..c3edac1 100644
--- a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.cpp
+++ b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.cpp
@@ -28,6 +28,7 @@
****************************************************************************/
#include "echoservice.h"
+#include <QTimer>
EchoService::EchoService()
: m_testCombo(Contact(QStringLiteral("Antti"), 34, true, QVariant()), EchoModule::Friday)
@@ -41,30 +42,42 @@ void EchoService::setLastMessage(QString lastMessage)
EchoSimpleSource::setLastMessage(lastMessage);
}
-QString EchoService::echo(const QString &msg)
+QVariant EchoService::echo(const QString &msg)
{
emit echoSlotCalled(msg);
return msg;
}
-QString EchoService::id()
+QVariant EchoService::id()
{
emit idSlotCalled();
return m_testId;
}
-Combo EchoService::getCombo()
+QVariant EchoService::getCombo()
{
emit getComboSlotCalled();
- return m_testCombo;
+ return QVariant::fromValue(m_testCombo);
}
-void EchoService::voidSlot()
+QVariant EchoService::voidSlot()
{
emit voidSlotCalled();
+ return QVariant();
}
-void EchoService::voidSlot2(int param)
+QVariant EchoService::voidSlot2(int param)
{
emit voidSlot2Called(param);
+ return QVariant();
+}
+
+QVariant EchoService::timer(int interval)
+{
+ static quint64 counter = 0;
+ EchoPendingResult pendingResult(counter++, false);
+ QTimer::singleShot(interval, this, [this, pendingResult](){
+ emit pendingResultAvailable(pendingResult.id(), true, QVariant());
+ });
+ return QVariant::fromValue(pendingResult);
}
diff --git a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.h b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.h
index ad3eba1..2bdbf54 100644
--- a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.h
+++ b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echoservice.h
@@ -45,11 +45,12 @@ public:
virtual void setLastMessage(QString lastMessage) override;
public Q_SLOTS:
- virtual QString echo(const QString &msg) override;
- virtual QString id() override;
- virtual Combo getCombo() override;
- virtual void voidSlot() override;
- virtual void voidSlot2(int param) override;
+ virtual QVariant echo(const QString &msg) override;
+ virtual QVariant id() override;
+ virtual QVariant getCombo() override;
+ virtual QVariant voidSlot() override;
+ virtual QVariant voidSlot2(int param) override;
+ virtual QVariant timer(int interval) override;
Q_SIGNALS:
void echoSlotCalled(const QString &msg);
diff --git a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.cpp b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.cpp
index f7c5862..8476f35 100644
--- a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.cpp
+++ b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.cpp
@@ -28,6 +28,8 @@
#include "echozonedservice.h"
+#include <QTimer>
+
#define SET_VALUE(m_VALUE, VALUE, CHANGED_SIGNAL) \
if (m_zoneHash.value(zone).m_VALUE == VALUE) \
return; \
@@ -207,13 +209,13 @@ QStringList EchoZonedService::availableZones()
return keys;
}
-QString EchoZonedService::echo(const QString &msg, const QString &zone)
+QVariant EchoZonedService::echo(const QString &msg, const QString &zone)
{
emit echoSlotCalled(msg, zone);
return msg;
}
-QString EchoZonedService::id(const QString &zone)
+QVariant EchoZonedService::id(const QString &zone)
{
emit idSlotCalled(zone);
return m_testId;
@@ -225,8 +227,18 @@ QVariant EchoZonedService::varMethod(const QString &zone)
return QVariant("FOOOO");
}
-Combo EchoZonedService::getCombo(const QString &zone)
+QVariant EchoZonedService::getCombo(const QString &zone)
{
emit getComboSlotCalled(zone);
- return m_testCombo;
+ return QVariant::fromValue(m_testCombo);
+}
+
+QVariant EchoZonedService::timer(int interval, const QString &zone)
+{
+ static quint64 counter = 0;
+ EchoZonedPendingResult pendingResult(counter++, false);
+ QTimer::singleShot(interval, this, [this, pendingResult, zone](){
+ emit pendingResultAvailable(pendingResult.id(), true, zone);
+ });
+ return QVariant::fromValue(pendingResult);
}
diff --git a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.h b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.h
index acdb5f5..6bfb567 100644
--- a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.h
+++ b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/echozonedservice.h
@@ -70,10 +70,11 @@ public slots:
qreal UPPERCASEPROPERTY(const QString &zone) override;
void setUPPERCASEPROPERTY(qreal UPPERCASEPROPERTY, const QString &zone) override;
QStringList availableZones() override;
- QString echo(const QString &msg, const QString &zone) override;
- QString id(const QString &zone) override;
+ QVariant echo(const QString &msg, const QString &zone) override;
+ QVariant id(const QString &zone) override;
QVariant varMethod(const QString &zone) override;
- Combo getCombo(const QString &zone) override;
+ QVariant getCombo(const QString &zone) override;
+ QVariant timer(int interval, const QString &zone) override;
Q_SIGNALS:
void echoSlotCalled(const QString &msg, const QString& zone);
diff --git a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.cpp b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.cpp
index c1f520a..33508d7 100644
--- a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.cpp
+++ b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.cpp
@@ -801,6 +801,71 @@ void EchoQtroTest::testMultipleSlotCalls()
QCOMPARE(echoZonedSpy[0][1].toString(), frontLeftZone);
}
+void EchoQtroTest::testAsyncSlotResults()
+{
+ Server server;
+ server.start();
+
+ Echo client;
+ QSignalSpy initSpy(&client, SIGNAL(isInitializedChanged(bool)));
+ QVERIFY(initSpy.isValid());
+ QVERIFY(client.startAutoDiscovery() == QIviAbstractFeature::ProductionBackendLoaded);
+
+ //wait until the client has connected and initial values are set
+ initSpy.wait(1000);
+ QCOMPARE(initSpy.count(), 1);
+ QVERIFY(client.isInitialized());
+
+ // test the timer() function which uses a pendingReply on the server side to return the
+ // function when the timer is finished.
+ QIviPendingReply<void> reply = client.timer(1000);
+ QIviPendingReply<void> reply2 = client.timer(500);
+ QSignalSpy echoReplySpy(reply.watcher(), SIGNAL(replySuccess()));
+ QSignalSpy echoReplySpy2(reply2.watcher(), SIGNAL(replySuccess()));
+
+ //Wait for the second reply to return first. Verify the other reply is not yet ready.
+ echoReplySpy2.wait();
+ QCOMPARE(echoReplySpy2.count(), 1);
+ QCOMPARE(echoReplySpy.count(), 0);
+
+ //Wait for the first reply and verify both replies were sent.
+ echoReplySpy.wait();
+ QCOMPARE(echoReplySpy2.count(), 1);
+ QCOMPARE(echoReplySpy.count(), 1);
+
+ EchoZoned zonedClient;
+ QSignalSpy zonedInitSpy(&zonedClient, SIGNAL(isInitializedChanged(bool)));
+ QVERIFY(zonedInitSpy.isValid());
+ QVERIFY(zonedClient.startAutoDiscovery() == QIviAbstractFeature::ProductionBackendLoaded);
+
+ //wait until the client has connected and initial values are set
+ zonedInitSpy.wait(1000);
+ QCOMPARE(zonedInitSpy.count(), 1);
+ QVERIFY(zonedClient.isInitialized());
+
+ EchoZoned *zone = qobject_cast<EchoZoned*>(zonedClient.zoneAt(frontLeftZone));
+ QVERIFY(zone);
+
+ // test the timer() function which uses a pendingReply on the server side to return the
+ // function when the timer is finished.
+ QIviPendingReply<QString> zonedReply = zonedClient.timer(1000);
+ QIviPendingReply<QString> zonedReply2 = zone->timer(500);
+ QSignalSpy zonedEchoReplySpy(zonedReply.watcher(), SIGNAL(replySuccess()));
+ QSignalSpy zonedEchoReplySpy2(zonedReply2.watcher(), SIGNAL(replySuccess()));
+
+ //Wait for the second reply to return first. Verify the other reply is not yet ready.
+ zonedEchoReplySpy2.wait();
+ QCOMPARE(zonedEchoReplySpy2.count(), 1);
+ QCOMPARE(zonedEchoReplySpy.count(), 0);
+ QCOMPARE(zonedReply2.value(), frontLeftZone);
+
+ //Wait for the first reply and verify both replies were sent.
+ zonedEchoReplySpy.wait();
+ QCOMPARE(zonedEchoReplySpy2.count(), 1);
+ QCOMPARE(zonedEchoReplySpy.count(), 1);
+ QCOMPARE(zonedReply.value(), QString());
+}
+
void EchoQtroTest::testSignals()
{
Server server;
diff --git a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.h b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.h
index 84aabcd..5ff3da4 100644
--- a/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.h
+++ b/tests/auto/core/ivigenerator/projects/org-example-echo-qtro/server_qtro_test/tst_echoqtro.h
@@ -52,6 +52,7 @@ private slots:
void testSlots();
void testZonedSlots();
void testMultipleSlotCalls();
+ void testAsyncSlotResults();
void testSignals();
void testModel();
};