aboutsummaryrefslogtreecommitdiffstats
path: root/src/ivicore/doc/src/examples-qface-ivi-remote.qdoc
blob: 7ea93795c36e1d611e515646347882ddbf094659 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2019 Luxoft Sweden AB
** Copyright (C) 2018 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
\example ivicore/qface-ivi-remote
\brief Learn how to use the Qt IVI Generator to create QtRemoteObjects based backends.
\ingroup qtivicore-examples
\title Generate QtRemoteObjects based backends with the Qt IVI Generator
\image examples_qface_ivi_remote.png

\section1 Introduction

This example shows how to generate a Middleware API, a Middleware Backend, and the
corresponding Middleware Service using the Qt IVI Generator. The communication between
the backend and the service is done with QtRemoteObjects as the IPC.

We use a single QFace IDL file to generate:

\list
\li a shared library with the front-end code
\li a backend plugin that implements a client to connect to the server
\li a server that runs the actual backend logic in a separate server process
\li a demo application that connects to the server and provides a UI to use the service
\endlist

In addition to the generated C++ code, the backend plugin and the server also contain an
intermediate \c{.rep} file that is further processed by the
\l [QtRemoteObjects] {Qt Remote Objects Compiler} {replica compiler} to produce the source and
replica classes.

\section2 Walkthrough

The IDL file used in the example represents an imaginary remote service for processing data. It
contains a single interface with one property and one method.

First, we need to define which \e module we want to describe. The \e module acts as a namespace,
because the IDL file can contain multiple interfaces.

\snippet ../../../../examples/ivicore/qface-ivi-remote/example-ivi-remote.qface 0

The most important part is the definition of the \e interface.

\snippet ../../../../examples/ivicore/qface-ivi-remote/example-ivi-remote.qface 1

In this case, we define an \e interface named \b ProcessingService with one property and one
method. Every property and method definition needs to contain at least a type and a name. Most of
the basic types are built-in and can be found in the \l{QFace IDL Syntax}.


\section1 Frontend library

Next, we use the IVI Generator to generate a shared library containing a C++ implementation of our
module and its interface; particularly the \e frontend template. This template generates a class
derived from \l{QIviAbstractFeature}, that includes all of the specified properties. The
generated library uses the \l{Dynamic Backend System} from QtIviCore, consequently providing an
easy way to change how the behavior is implemented.

To call the autogenerator for our shared library, the qmake project file needs to use the
\c ivigenerator qmake feature:

\snippet ../../../../examples/ivicore/qface-ivi-remote/frontend/frontend.pro 1

By adding \c ivigenerator to the \c CONFIG variable, the \c ivigenerator feature file is loaded and
interprets the \c QFACE_SOURCES variable, similar to the \c SOURCES variable in regular qmake
projects. However, activating the qmake feature via the \c CONFIG variable has one disadvantage: if
the feature is not available, no errors are reported. We recommend using the following additional
code to report errors:

\snippet ../../../../examples/ivicore/qface-ivi-remote/frontend/frontend.pro 0

The remaining part of the project file is a normal library setup that works on Linux, macOS, and
Windows.


\section1 QtRemoteObjects Backend Plugin

As mentioned above, the \e frontend library uses the \l{Dynamic Backend System}. This means
that for the library to provide some functionality, we also need a \e backend plugin. The generated
plugin here works as a client that connects to the server using Qt Remote Objects. The qmake
integration works in the same way, but it uses the \e QFACE_FORMAT variable to tell the
\c ivigenerator to use a different generation template, \e backend_qtro:

\snippet ../../../../examples/ivicore/qface-ivi-remote/backend_qtro/backend_qtro.pro 2

The generated backend plugin code is usable as is, and doesn't require any further change. As we
want to generate a plugin instead of a plain library, we need to instruct qmake to do so by adding
\c plugin to the \c CONFIG variable. For the plugin to compile correctly it needs to get the
backend interface header from the previously created library. But this header is also generated, so
it's not part of our source tree, but part of the build tree. To provide the backend interface
header, we add it to the include path using the following construct:

\snippet ../../../../examples/ivicore/qface-ivi-remote/backend_qtro/backend_qtro.pro 1

Most of the code in the backend plugin is generated by the IVI Generator, but some of it is
generated by the Qt's Remote Object Compiler, \c repc. To achieve this, the IVI Generator produces
an intermediate \c{.repc} file that's further processed by the \c repc compiler. This compiler is
called via the generated \c{.pri} file, found in the build directory. Notice that you have to call
qmake on the project, at least once to have the generated files available.


Our application doesn't know about the existence of our backend plugin, so we need to put this
plugin in a folder where the application typically looks for plugins. By default, Qt either
searches in the \b plugins folder within its installation directory or in the application's current
working directory. For QtIvi plugins to be found, they need to be provided within a \b qtivi
sub-folder. Add the following line to the backend project file, as follows:

\snippet ../../../../examples/ivicore/qface-ivi-remote/backend_qtro/backend_qtro.pro 0


\section1 RemoteObjects Server

The server is an independent, GUI-less application that contains the backend's business logic, and
we need to write most of its implementation. Nevertheless, the generator produces some code to
simplify the development. We can generate server side code by using the IVI Generator with the
\e server_qtro template:

\snippet ../../../../examples/ivicore/qface-ivi-remote/server_qtro/server_qtro.pro 0
\dots 0
\snippet ../../../../examples/ivicore/qface-ivi-remote/server_qtro/server_qtro.pro 1

To use the generated remote source, we need to inherit from one of the classes defined in the
generated \c rep_processingservice_source.h file. In this example, we implement our server's
logic in the \c ProcessingService class and use the \c ProcessingServiceSimpleSource as the base
class:

\code
// server_qtro/processingservice.h
\endcode
\snippet ../../../../examples/ivicore/qface-ivi-remote/server_qtro/processingservice.h 0

Note that the base class already has the definitions for property accessors, but any custom
method or slot needs to be overridden and defined. Our implementation of the process function
merely counts and returns the length of the data passed and updates the \c lastMessage property:

\code
// server_qtro/processingservice.cpp
\endcode
\snippet ../../../../examples/ivicore/qface-ivi-remote/server_qtro/processingservice.cpp 0

To make the \c ProcessingService class accessible remotely, we need to share it via the
QRemoteObjectNode::enableRemoting() function. The \c Core class generated provides a preconfigured
instance of a \c remotenode that is used for the remoting. For the plugin to connect to the right
object, use an identifier in the format "ModuleName.InterfaceName", which in our case is
"Example.IVI.Remote.ProcessingService". All this is done in the \c main() function, along with
the start of the main event loop:

\code
// server_qtro/main.cpp
\endcode
\snippet ../../../../examples/ivicore/qface-ivi-remote/server_qtro/main.cpp 0

This is all you need to do to implement a service that is accessible remotely; use the properties
as usual and provide the method implementations. The QtRemoteObjects library takes care of the
communication.


\section1 Demo Client Application

The demo application presents a simple QML GUI to use the remote service over the generated
interface.

As we do not provide a QML plugin, the application needs to link to the generated frontend library
and call the \c{RemoteModule::registerTypes} and \c{RemoteModule::registerQmlTypes} methods that
are generated in the module singleton to register all autogenerated interfaces and types with the
QML engine.

In our QML application, we still need to import the module using the same module URI which is used
in the QFace file. Afterwards the interface can be instantiated like any other QML item.

\code
// demo/main.qml
\endcode
\snippet ../../../../examples/ivicore/qface-ivi-remote/demo/main.qml 0
\dots 0

Every method call that is made through a generated API, is asynchronous. This means that instead
of directly returning a return value, a QIviPendingReply object is returned. Using the
\l QIviPendingReply::then() method on the returned object, we may assign callbacks to it that are
called when the method call has been successfully finished; or if it has failed.

\code
// demo/main.qml
\endcode
\snippet ../../../../examples/ivicore/qface-ivi-remote/demo/main.qml 1

In case of properties, we use bindings as usual:

\code
// demo/main.qml
\endcode
\snippet ../../../../examples/ivicore/qface-ivi-remote/demo/main.qml 2

\section1 Running the Example

To see the demo's entire functionality, run both the server and the demo application
simultaneously. You may leave the server running and restart the application, or vice versa,
to see that the reconnection works. Run the demo application alone without the server running,
to test how the remote method call fails when there is no connection.

*/