aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKavindra Palaraja <kpalaraja@luxoft.com>2019-08-01 21:26:33 +0200
committerKavindra Palaraja <kpalaraja@luxoft.com>2019-08-06 17:38:00 +0200
commita657189eaf144d79575718ca5021cf298cd17f63 (patch)
treee0ce0af080bd5602fd25a5ba1316e713cd26a39e
parent013230e3101cec6894cd8c187d07a4a9c2fc0053 (diff)
[docs] Review IVI Tutorial
Change-Id: Ieaf85dac8946879392a0849aa4807fa82d28b705 Reviewed-by: Robert Griebl <robert.griebl@pelagicore.com>
-rw-r--r--src/ivicore/doc/src/examples-qface-tutorial.qdoc1187
1 files changed, 617 insertions, 570 deletions
diff --git a/src/ivicore/doc/src/examples-qface-tutorial.qdoc b/src/ivicore/doc/src/examples-qface-tutorial.qdoc
index a574d94..214d856 100644
--- a/src/ivicore/doc/src/examples-qface-tutorial.qdoc
+++ b/src/ivicore/doc/src/examples-qface-tutorial.qdoc
@@ -26,692 +26,739 @@
****************************************************************************/
/*!
-\example ivicore/qface-tutorial
-\brief <<< ADD A GOOD BRIEF HERE >>>
-\ingroup qtivicore-examples
-\title Qt IVI Generator Tutorial
-\image examples_qface_tutorial.png
-
-This tutorial will show you how to extend your QML Application with your own autogenerated
-middleware API. This will be done using a existing QML Instrument Cluster application and consists
-of the following steps:
-
-\list 1
- \li Basic Interface integration without backend, but with QML plugin ? (extra step ?)
- \li Extending the Interface, add annotations
- \li Adding a simulation backend (and simulation annotations) (explain linking)
- \li Adding a custom simulation behavior
- \li Adding a simulation server and using it from a qtro_backend
- \li Developing a production backend which connects to a dbus interface
-\endlist
+ \example ivicore/qface-tutorial
+ \brief Demonstrates step-by-step how to generate a Middleware API based on a QML application.
+ \ingroup qtivicore-examples
+ \title Qt IVI Generator Tutorial
+ \image examples_qface_tutorial.png
+
+ This tutorial demonstrates how you can extend a QML application with your own autogenerated
+ Middleware API. We use an existing QML Instrument Cluster application and proceed through the
+ following steps:
+
+ \list 1
+ \li Integrate a basic interface integration without a backend
+ \li Extend the interface and add annotations
+ \li Add a simulation backend and corresponding simulation annotations; with a QML plugin
+ \li Add a custom simulation behavior
+ \li Add a simulation server and use it from a Qt Remote Objects Backend
+ \li Develop a production backend that connects to a DBus interface
+ \endlist
+
+ Before we start the actual Middleware integration, let's take a look at the existing Instrument
+ Cluster QML code and all the features it supports:
+ \list
+ \li \c images -- This folder contains all images used in the QML code.
+ \li \c Cluster.qml -- The main QML file that assembles all other QML components together.
+ \li \c Dial.qml -- The base component to show values like speed or Revolutions per Minute
+ (RPM), using a needle.
+ \li \c Fuel.qml -- The component to show the actual fuel level.
+ \li \c Label.qml -- A small helper component which sets all common settings used to display
+ text.
+ \li \c LeftDial.qml -- Shows the current speed using the Dial component and as text, as
+ well as the current metric in miles per hour (mph) or kilometers per hour (km/h).
+ \li \c RightDial.qml -- Shows the current RPM and offers a way to show warning indicators.
+ \li \c Top.qml -- The top bar that shows the current date and the current temperature.
+ \endlist
+
+ Next, we use our Middleware API to add support for the following features:
+ \list
+ \li Show the current speed in the left dial.
+ \li Show the current RPM in the right dial.
+ \li Change between different metrics.
+ \li Show the current temperature in the top bar.
+ \li Show different warnings on the right dial.
+ \li Indicate whether the instrument cluster is connected and show real data.
+ \endlist
+
+ The ultimate goal is to connect all of these features together to simulate a real-time driving
+ experience like this:
+
+ \image examples_qface_tutorial_final.gif
+
+ \section1 Basic Middlware API with the help of the IVI Generator
+
+ In this chapter we integrate a Middleware API into the existing Instrument Cluster QML code.
+ Instead of manually writing all of these parts ourselves, which is done in most basic
+ \l{https://doc.qt.io/qt-5/qtquick-codesamples.html}{QML examples}, we'll use the IVI Generator
+ to autogenerate the required parts.
+
+ \target define-speed-property
+ \section2 Interface Definition Language
+
+ To be able to autogenerate the Middleware API, the IVI Generator needs some input on what to
+ generate. This input is given in form of an Interface Definition Language (IDL), QFace, which
+ describes the API in a very simple way.
+
+ Let's start to define a very simple interface which provides us with a speed property:
+
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
+ \printuntil }
+
+ First, we need to define which module we want to describe. The module acts as a namespace,
+ because the IDL file can contain multiple interfaces.
-Before we start the actual Middleware integration, let's take a look at the existing Instrument
-Cluster QML code and all the features it supports and our Middleware API should support as well.
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
+ \printuntil module
-\list
- \li \c images This folder contains all images used in the QML code
- \li \c Cluster.qml The main QML file assembling all other QML components together
- \li \c Dial.qml The base component for showing values like speed or rpm using a needle
- \li \c Fuel.qml A component to show the actual fuel level
- \li \c Label.qml A small helper component which sets all common settings used to display Text
- \li \c LeftDial.qml Shows the current speed using the Dial component and as Text and the current metric (mph or km/h)
- \li \c RightDial.qml Shows the current rpm and offers a way to show warning indicators.
- \li \c Top.qml The top bar showing the current Date and the current temperature.
-\endlist
+ The most important part of the module is its interface definition.
-In the following chapters we will integrate the following features:
-\list
- \li Show the current speed in the left dial
- \li Show the current rpm in the right dial
- \li Support to change between different metrics
- \li Show the current temperature in the top bar
- \li Show different warnings on the right dial
- \li Indicate whether the instrument cluster is connected and shows real data
-\endlist
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
+ \skipto interface
+ \printuntil }
-All this will be connected together to simulate a real driving experience and should look like
-this:
+ In this case, we define an interface named \c InstrumentCluster that consists of one property.
+ Each property definition must 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}.
-\image examples_qface_tutorial_final.gif
-
-\section1 Basic Middlware API with the help of IVI Generator
+ \section2 Autogeneration
-In this chapter we integrate a Middleware API into the existing Instrument Cluster QML code.
-Instead of manually writing all parts ourself which is done in most other basic QML examples
-(link), we will use the IVI Generator to autogenerate the needed parts.
+ Now that our first version of the IDL file is ready, it's time to autogenerate API from it,
+ using the \l{Qt IVI Autogenerator}{IVI Generator tool}. Similar to
+ \l{https://doc.qt.io/qt-5/moc.html}{moc}, this autogeneration process is integrated into the
+ qmake Build System and is done on compile time.
-\section2 Interface Definition Language
+ In the following \c{.pro} file we build a C++ library based on our IDL file:
-To be able to autogenerate the Middleware API the IVI Generator needs some input on what to
-generate. This input is given in form of a Interface Definition Language (IDL) which describes the
-API in a very simple form.
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/frontend/frontend.pro
+ \printto CONFIG += install_ok
-Let's start to define a very simple IDL which provides us with a speed property:
+ Most of the \c{.pro} file is a standard setup to define a C++ library, using "lib" \c TEMPLATE
+ and defining the required file name in the \c TARGET variable. The \c qtLibraryTarget function
+ that we use helps to append the "d" postfix on the filename correctly, for a library that
+ provides debugging information. In the future, we need to link this file, so we set the
+ \c DESTDIR to the upper directory to simplify this.
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
-\printuntil }
+ \note Windows searches for libraries in the same directory automatically.
-First, we need to define which module we want to describe. The module acts as a namespace, because
-the IDL file can contain multiple interfaces.
+ Activating the IVI Generator integration requires the \c CONFIG variable to specify the
+ \c ivigenerator option. This makes sure the IVI Generator is called during the build process,
+ using the QFace file that we specify in \c QFACE_SOURCES. For more information, see
+ \l{qmake integration}.
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
-\printuntil module
+ To make sure the library we build works on Windows, it's important to add the
+ \c QT_BUILD_EXAMPLE_IVI_INSTRUMENTCLUSTER_LIB to the \c DEFINES variable. This way, all symbols
+ are exported when building the library, but imported when linking against it. For more
+ information, see \l{https://doc.qt.io/qt-5/sharedlibrary.html}{Creating Shared Libraries}.
-The most important part of the module is its interface definition.
+ \section2 Which Files are Autogenerated
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
-\skipto interface
-\printuntil }
+ The IVI Generator works based on generation templates. These templates define what content
+ should be generated from a QFace file. If no \c QFACE_FORMAT is defined, this automatically
+ defaults to "frontend" template. For more details on these templates, see \l{Use the Generator}.
-In this case, we define an interface named InstrumentCluster consisting of one property. Each
-property definition must contain at least a type and a name. Most of the basic types are built-in
-and can be found in the QFace IDL syntax.
+ In short, the "frontend" template generates:
+ \list
+ \li a C++ class derived from QIviAbstractFeature for every interface in the QFace file
+ \li one module class that helps to register all interfaces to QML and stores global types
+ and functions.
+ \endlist
-\section2 Autogeneration
+ To inspect the C++ code yourself, you can view these files in the your library's build folder.
-Now as our first version of the IDL file is ready, it's time to autogenerate API out of it. This is
-done by the IVI Generator tool. Similar to moc this autogeneration is integrated into the qmake
-Build System and is done on compile time. In the following pro file we build a C++ library based on
-our IDL file:
+ Right now, the most important autogenerated file for us, is the resulting C++ class for our
+ defined interface. It looks like this:
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/frontend/frontend.pro
-\printto CONFIG += install_ok
+ \quotefile ivicore/qface-tutorial/chapter1-basics/frontend/instrumentcluster.h
-Most of the qmake file is a standard setup to define a C++ library, using 'lib' TEMPLATE and
-defining the wanted file name in the TARGET variable. The used qtLibraryTarget function helps to
-correctly append the 'd' postfix on the filename for a library providing debugging information.
-Because we need to link this file in the future we set the DESTDIR to the upper directory to make
-this a bit easier (Windows is searching for libraries in the same directory automatically).
+ As you can see, the autogenerated C++ class implements a \c speed property, that we previously
+ defined in the QFace file. By using the \c Q_OBJECT and \c Q_PROPERTY macros, the class is now
+ ready for use directly in your QML code.
-To activate the actual IVI Generator integration, the CONFIG needs to have the ivigenerator option.
-By enabling this config the generator will be called during the build process using the qface file
-provided in QFACE_SOURCES. See (link) for more information about the qmake integration.
+ \section2 Integrate the Frontend Library with the QML Code
-Because we are building a library which is also supposed to work on Windows it's important to add
-the QT_BUILD_EXAMPLE_IVI_INSTRUMENTCLUSTER_LIB to the DEFINES variable. This makes sure that all
-symbols are exported when building the library, but imported when linking against it. See Creating
-Shared Libraries for more information on this topic.
+ For this integration, we use the autogenerated frontend library from the QML code. For the sake
+ of simplicity, we follow the standard Qt example pattern and use a small C++ main function
+ which registers our autogenerated types to QML and loads the Instrument Cluster QML code into
+ the QQmlApplicationEngine:
-\section2 What will be autogenerated ?
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster/main.cpp
+ \skipto #include "instrumentclustermodule.h"
+ \printuntil }
-The ivigenerator is based on generation templates. These templates define what content is generated
-from the IDL file. When no QFACE_FORMAT is defined this will automatically default to use the
-"frontend" template. What a template generates is explained here (link to the frontend template).
+ All we need now is the actual integration of the InstrumentCluster QML element and connecting
+ the \c speed property to the \c leftDial. This is done by instantiating the element first with
+ the id instrumentCluster.
-In short the 'frontend' template will generate a C++ class derived from QIviAbstractFeature for
-every interface in the IDL file and one module class which helps to register all interfaces to QML
-and which stores global types and functions for. More on that in a later chapter.
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster/Cluster.qml
+ \skipto import
+ \printuntil InstrumentCluster
+ \printuntil }
+ \codeline
-To inspect the C++ code yourself, you can either checkout the files in the build folder of your
-library.
+ Lastly, we can create a Binding for the \c LeftDial Item's \c value property to our
+ InstrumentCluster API's \c speed property.
-The most important file for us right now which is autogenerated is the resulting C++ class for our
-defined interface. It looks like this:
+ \printuntil }
-\quotefile ivicore/qface-tutorial/chapter1-basics/frontend/instrumentcluster.h
+ \section1 Chapter 2
-As you can see does the autogenerated C++ class implement a "speed" property as defined in the IDL
-file. By using the Q_OBJECT macro and Q_PROPERTY the class is already now ready to be used directly
-in your QML code.
+ In this chapter we extend our Middleware API with more properties via enums and by defining our
+ own structure.
-\section2 Integrating the frontend library into the QML code
+ \section2 Define Speed as a Read-only Property
-The last part of this chapter is to use the autogenerated frontend library from the QML code. To
-keep this integration simple we follow the standard Qt example way and use a small C++ main
-function which registers our autogenerated types to QML and loads the Instrument Cluster QML code
-into the QQmlApplicationEngine:
+ \l{define-speed-property}{Previously}, we defined the speed property in our QFace file in the
+ following way:
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster/main.cpp
-\skipto #include "instrumentclustermodule.h"
-\printuntil }
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
+ \printuntil }
-What's still missing is the actual integration of the InstrumentCluster QML element and connecting
-the speed property to the leftDial. This can be done by instantiating the element first with the id
-instrumentCluster.
+ This property is defined as readable and writable, as we didn't use any extra specifiers.
+ However, it's not necessary for our Instrument Cluster example to have a writable \c speed
+ property because it's not used to accelerate the car, but just to visualize the current state.
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster/Cluster.qml
-\skipto import
-\printuntil InstrumentCluster
-\printuntil }
-\codeline
+ To define the property as read-only, use the \c readonly keyword.
-Now we can create a Binding for the value property of the LeftDial Item to the speed property of
-our InstrumentCluster API.
+ \quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
+ \printuntil readonly
+ \skipto }
+ \printuntil }
-\printuntil }
+ When we build our app again, the build system recognizes this change and runs the IVI
+ Generator to generate an updated version of the C++ code. After the IVI Generator is done,
+ open the \c instrumentcluster.h from the build folder and notice that the generated
+ \c speed property changed -- it no longer has a setter anymore and is now read-only.
-\section1 Chapter 2
+ \quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/frontend/instrumentcluster.h
+ \skipto class Q_EXAMPLE
+ \printuntil Q_PROPERTY
+ \dots
+ \skipto };
+ \printuntil };
-In the second chapter we will extend our Middleware API with more properties, using enums and
-defining our own struct.
+ \section2 Extend the Interface
-\section2 Defining speed as readonly property
+ To reach our goal to provide a full simulation for the Instrument Cluster, we need to add more
+ properties to our QFace file: \c rpm, \c fuel and \c temperature:
-In the previous chapter we defined the speed property in our QFace IDL file in the following way:
+ \quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
+ \printuntil readonly real temperature
+ \skipto }
+ \printuntil }
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster.qface
-\printuntil }
+ You might have noticed that we use a different type for the \c fuel and \c temperature
+ properties. We use \c real here, as we would like to show the temperature as a floating point
+ number, and the current fuel level as a value between 0 and 1.
-Because we didn't use any extra specifier this property is defined as being readable and writable.
-For our Instrument Cluster example having a writable speed property doesn't make much sense, as it
-is not supposed to be used to accelerate the car, but just to visualize the current state.
+ \section2 Define a New Enum Type
-We can define the property to be read only by using the "readonly" keyword.
+ One useful feature is to be able to switch between the metric and the imperial system, so we
+ need to define a property for the system we currently use. Using a boolean property would work,
+ but doesn't offer a nice API, so we define a new enum type in the QFace file and use it as the
+ type for our new \c system property:
-\quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
-\printuntil readonly
-\skipto }
-\printuntil }
+ \quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
+ \printuntil readonly SystemType
+ \skipto }
+ \printuntil enum
+ \printuntil }
-Starting to build our app again, the build system will recognize the changed qface file and start
-the IVI Generator to generate an updated version of the C++ code. After the IVI Generator is done
-you can open the instrumentcluster.h from within the build folder and you will see that the
-generated C++ property changed to NOT have any setter anymore and by that is now read only.
+ In the autogenerated code, this results in an enum which is part of the module class, making it
+ possible for the same enum to be used by multiple classes which are part of the same module:
-\quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/frontend/instrumentcluster.h
-\skipto class Q_EXAMPLE
-\printuntil Q_PROPERTY
-\dots
-\skipto };
-\printuntil };
+ \quotefile ivicore/qface-tutorial/chapter2-enums-structs/frontend/instrumentclustermodule.h
-\section2 Extending the interface
+ \section2 Add a New Structure
-To reach our goal to provide a full simulation for the Instrument Cluster, we need to add more
-properties to our IDL file. Adding additional properties for 'rpm', 'fuel' and 'temperature' should
-be a easy job and result in this:
+ To display warnings on the Instrument Cluster's right dial, we'd like to use a structure that
+ stores color, icon, and text for the warning; instead of using 3 independent properties.
+ Similar to defining an interface, we can use the \c struct keyword in our QFace file:
-\quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
-\printuntil readonly real temperature
-\skipto }
-\printuntil }
+ \quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
+ \skipto struct
+ \printuntil }
-You might have noticed that we used a different type for the 'fuel' and 'temperature' properties.
-We use the type real here as we would like to show the temperature as a floating point number and
-the current fuel level between 0 and 1.
+ Using this new structure as a type for a property, works in the same way as when using an enum.
+ The QFace file should now look like this:
-\section2 Defining a new enum type
+ \quotefile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
-As we like to be able to switch between the metric and the imperial system, we also need to define
-a property for the system we currently use. Using a boolean property would work, but doesn't offer
-a nice API, so we define a new type in the IDL and use it as the type for the new 'system'
-property:
+ \section2 Integrate the New Properties
-\quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
-\printuntil readonly SystemType
-\skipto }
-\printuntil enum
-\printuntil }
+ Like in the previous chapter, actually integrating the newly introduced properties involves
+ creating Bindings. The \c rpm property can be directly connected to the \c rightDial Item's
+ \c value property; the same is done for the top Item's \c temperature property. To control
+ which unit is displayed in the left Dial, the \c leftDial Item provides \c metricSystem, a bool
+ property. As we used an enum in our QFace file, we need to convert the value first by testing
+ the \c sytemType property for the "Metric" value.
-In the autogenerated code, this will create a enum which is part of the module class. This makes it
-possible that the same enum can be used by multiple classes which are part of the same module:
+ \quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster/Cluster.qml
+ \skipto LeftDial
+ \printuntil }
+ \codeline
-\quotefile ivicore/qface-tutorial/chapter2-enums-structs/frontend/instrumentclustermodule.h
+ These enums are part of the module class, which is also exported to QML as
+ InstrumentClusterModule. To trigger a warning in the \c rightDial Item, we use 3 bindings to
+ connect to the 3 member variables in the structure:
-\section2 Add a new struct
+ \printuntil }
-To display warnings on the right dial of the Instrument Cluster we would like to use a struct which
-stores color, icon and text of the warning instead of using 3 independent properties for this.
-Similar to define a interface, we can use the struct keyword in the IDL file:
+ \section1 Chapter 3
-\quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
-\skipto struct
-\printuntil }
+ In the previous two chapters, we wrote a Middleware API using a QFace file and used the IVI
+ Generator to autogenerate a C++ API in the form of a library. Now, in this chapter, we extend
+ this further by introducing a simulation backend and using annotations to define default values
+ for our simulation.
-Using this new struct for as a type for a property works in the same way as when using a enum, the
-final IDL file should now look like this:
+ \section2 Separation between the Frontend and Backend
-\quotefile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster.qface
+ Both QtIvi and the IVI Generator enable you to write code that separates the frontend from the
+ backend -- to split an API from its actual implementation. Already, Qt uses this concept in a
+ lot of areas, most prominently in the underlying window system technology on various Qt
+ platforms like XCB on Linux and Cocoa on macOS.
-\section2 Integrating the new properties
+ The same separation is done for our Middleware API, where the frontend provides the API as
+ a library; the backend provides an implementation of this API. This implementation is based on
+ QtIvi's \l{Dynamic Backend System} which enables us to switch between such backends at runtime.
+
+ \image feature-backend.png
+
+ \section2 Add a Simulation Backend
+
+ For our Instrument Cluster, we'd like to add such a backend to provide actual values. For now,
+ we'd like to just have some simulation behavior as we can't connect it easily to a real car.
+ This is why such backends are called "simulation backend". To add this type of backend, once
+ again, we use the IVI Generator to do the heavy lifting for us and generate one. This work
+ is done in a similar way to when we generated a library with the "frontend" template. But now,
+ we are using the "backend_simulator" template:
+
+ \quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro
+ \printto DESTDIR
+ \skipto QT
+ \printuntil CONFIG
+ \skipto QFACE_FORMAT
+ \printto CONFIG += install_ok
+
+ Just like for the frontend library, the project file builds a \c lib and defines the library
+ name using \c qtLibraryTarget to also support the Windows debug postfix. One important aspect
+ here is that the library name ends with "_simulation", which is a way to tell QtIvi that this
+ is a simulation backend. When a "production" backend is available, it is preferred over the
+ "simulation" one. For more information, see \l{Dynamic Backend System}.
+
+ Enabling the IVI Generator is also done in the same way as we did earlier: by using the same
+ \c QFACE_SOURCE variable, but defining \c QFACE_FORMAT to "backend_simulator", to use the
+ correct generation template. In addition, we need to add 'plugin' to the \c CONFIG variable,
+ to make this library a Qt plugin which can be easily loaded at runtime.
+
+ \section2 Link Settings and Locating Plugins
+
+ Trying to build the project file just as it is, right now, would result in compilation and
+ linking errors. This is because: to do the frontend and backend separation, we need to have the
+ backend implement a defined interface class, that is known to the frontend. This interface is
+ aptly called "backend interface" and is automatically generated as part of the frontend
+ library. Because this class provides signals and slots and uses QObject for its base class, you
+ need to link to the frontend library when you inherit from it. As this is needed for the
+ backend plugin, we need to add the following lines in addition:
+
+ \quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro
+ \skipuntil CONFIG
+ \printuntil INCLUDEPATH
+
+ Now the project should build fine and create the plugin in your build folder; or the plugin
+ folder if you don't use a shadow build. When you start the Instrument Cluster again, you should
+ see the following message:
+
+ \badcode
+ There is no production backend implementing "Example.IVI.InstrumentCluster.InstrumentCluster" .
+ There is no simulation backend implementing "Example.IVI.InstrumentCluster.InstrumentCluster" .
+ No suitable ServiceObject found.
+ \endcode
+
+ This message indicates that QtIvi is still unable to find the simulation plugin we just created.
+ Here, you need to know a little bit more about Qt's Plugin System, especially how it it finds
+ plugins.
+
+ Qt searches for it's plugins in multiple directories, the first one is the plugin folder,
+ \c plugins, which comes with your Qt installation. Within the plugins folder, every plugin type
+ has it's own sub-folder, such as \c platforms, for the platform plugins used to talk to the
+ underlying platform API and the windowing system.
+
+ Similarly, QtIvi searches for its backend plugins in the \c qtivi folder. To make sure our
+ simulation backend ends up in such a folder, we add the following \c DESTDIR definition.
+
+ \quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro
+ \skipto DESTDIR
+ \printuntil DESTDIR
+
+ You might wonder how creating a \c qtivi folder in the upper directory solves this problem of
+ finding the plugin as it's not part of the system plugins folder. But Qt supports searching in
+ multiple folders for such plugins and one of those folders is the path to where the executable
+ itself is located.
+
+ Alternatively, we could add an additional plugin path using the QCoreApplication::addLibraryPath()
+ function or using the \c QT_PLUGIN_PATH environment variable. For more information, see
+ \l{https://doc.qt.io/qt-5/plugins-howto.html}{How to create Qt Plugins}.
+
+ \section2 Export the QML Types in a QML Plugin
+
+ In the first chapter, we extended our \c main.cpp to register all types of our autogenerated
+ Middleware APIs. Although this works fine, in bigger projects it's common to use a QML Plugin
+ instead and be able to use qmlscene for development. Although the code for doing this is
+ not complex, the IVI Generator supports this as well and makes it even easier.
+
+ From the first chapter, we know that the module name is used for the QML import URI. This is
+ important for a QML plugin as the QmlEngine expects the plugin in a specific folder to
+ follow the module name, where every section of the module name is a sub-folder. Our project
+ file to generate a QML plugin looks like this:
+
+ \quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/imports/imports.pro
+ \printto target.path
+
+ All lines until \c QFACE_SOURCES should be familiar. We use \c CONFIG to build a plugin, then
+ define the settings for the linker to link against our frontend library. Then, we use
+ \c QFACE_FORMAT to define "qmlplugin" as the generation template. Instead of adding
+ \c ivigenerator to \c CONFIG, this time we use
+ \l{https://doc.qt.io/qt-5/qmake-test-function-reference.html#load-feature}
+ {qmake's load() function} to explicitly load the feature. This enables us to use the \c URI
+ variable which is part of the "qmlplugin" generation template. This URI can be used to define
+ a \c DESTDIR by replacing all dots with slashes.
+
+ In addition to the folder structure, the QmlEngine also needs a \c qmldir file which indicates
+ what files are part of the plugin, and under which URI. For more information, see
+ \l{https://doc.qt.io/qt-5/qtqml-modules-qmldir.html}{Module Definition qmldir Files}. Both this
+ \c qmldir file and a \c plugins.qmltypes file which provides information about code-completion,
+ are autogenerated by the IVI Generator; but they need to be placed next to the library. To do
+ so, we add the files to a scope similar to an \c INSTALL target, but add it to the \c COPIES
+ variable instead. This makes sure that the files are copied when the plugin is built.
+
+ Now the plugin is ready for use, but our Instrument Cluster application doesn't know where to
+ search for it and is still using the old hardcoded registration. So, we can now remove the
+ linking step in the \c instrument-cluster.pro file and change our main file accordingly:
+
+ \quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster/main.cpp
+ \skipto #include
+ \printuntil }
+
+ What has changed is that we've now added an additional import path with the \c addImportPath
+ function, which points to the "imports" folder next to the binary's location.
+
+ \section1 Chapter 4
+
+ So far, we've created a Middleware API and integrated it into our Instrument Cluster QML code,
+ extended it with a QML plugin, and generated a simulation backend. In the background, quite a
+ lot has happened to support us; but on the UI side not much has changed till now. This chapter
+ is about bringing our simulation backend to life by defining sane default values and starting
+ to simulate a real car ride.
+
+ \section2 Define Default Values
+
+ We start by defining default values for our properties, using annotations in our QFace file.
+ An annotation is a special kind of comment which adds extra data to an interface, method,
+ property, and so on. For this use case we use the \c config_simulator annotation. For more
+ information, see \{annotations-yaml}{Annotations}.
+
+ Currently, in our Instrument Cluster, the temperatur defaults to 0. Let's change this to a
+ temperature in spring, 15 degrees Celsius, with the following YAML fragment:
+
+ \quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/instrument-cluster.qface
+ \printuntil }
+
+ Compile the plugin again for this temperature change to be reflected in our Instrument Cluster.
+ Let's see how this actually works: when starting the IVI Generator, the config_simulator
+ annotation was transformed into a JSON file that's now part of the "simulation backend" build
+ folder. This JSON file looks like this:
+
+ \quotefile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/instrumentcluster.json
+
+ But how is this JSON file related to the actual simulation backend code? The autogenerated
+ simulation backend code uses QIviSimulationEngine, that reads the JSON file and provides its
+ data to a QML simulation file. A default QML file is also autogenerated and loaded from the
+ QIviSimulationEngine. This default QML file provides the behavior of what should happen in the
+ the simulation backend.
+
+ Later, in the next section, we take a look at the QML file and how we can change it. But first,
+ let's see how we can change the default values in a more dynamic way.
+
+ The QIviSimulationEngine allows us to override which JSON file should be loaded into the
+ engine, when we set the \c QTIVI_SIMULATION_DATA_OVERRIDE environment variable. Since there can
+ be multiple engines run by different backends, we need to define which engine we're referring
+ to. In the autogenerated code, the module name is always used as the engine specifier. For this
+ chapter, we already prepared a second JSON file which is part of our source directory. Setting
+ the environment variable as follows, changes the \c systemType to mph instead of km/h:
+
+ \badcode
+ QTIVI_SIMULATION_DATA_OVERRIDE=instrumentcluster=<path-to-file>/miles.json
+ \endcode
+
+ \section2 Define a QML Behavior
+
+ Before we define our custom behavior, let's see what's been autogenerated for us. There are two
+ QML files: The first is \c instrumentcluster_simulation.qml and rather simple. It defines an
+ entry point that istantiates the second file, an \c InstrumentClusterSimulation.qml file. This
+ split is done as there can be multiple interfaces defined as part of the same module.
+
+ \note A QML Engine can only have one entry point. While QIviSimulationEngine has this same
+ limitation, if you have a module with multiple interfaces, you want to have multiple simulation
+ files -- one per interface. This is why the first QML file merely instantiates the QML files for
+ all interfaces that it supports. In the case of our example, it's only one interface.
+
+ The InstrumentClusterSimulation.qml file is very interesting:
+
+ \quotefile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/InstrumentClusterSimulation.qml
+
+ First, there's a \c settings property, that's initialized with the return value from the
+ \l{IviSimulator::findData}{IviSimulator.findData} method, which takes the
+ \l{IviSimulator::simulationData}{IviSimulator.simulationData} and a string as input. The
+ \c simulationData is the JSON file represented as a JavaScript object.
+
+ The \c findData method helps us to extract only the data that is of interest for this
+ interface, \c InstrumentCluster. The properties that follow help the interface to know whether
+ the default values are set. The \c LoggingCategory is used to identify the log output from this
+ simulation file.
+
+ Afterwards, the actual behavior is defined by instantiating an \c InstrumentClusterBackend Item
+ and extending it with more functions. The \c InstrumentClusterBackend is the interface towards
+ our \c InstrumentCluster QML frontend class. But, apart from the frontend, these properties are
+ also writable to make it possible to change them to provide a useful simulation.
+
+ Each time a frontend instance connects to a backend, the \c initialize() function is called.
+ The same applies to the QML simulation: as the \c initialize() C++ function forwards this to
+ the QML instance. This also applies to all other functions, like setter and getters, for
+ properties or methods. For more details, see \l{QIviSimulationEngine.
-Like in the previous chapter, doing the actual integration of the new introduced properties should
-be pretty straightforward by creating Bindings. The rpm property can be directly connected to the
-value property of the rightDial Item and in the same way the temperature property of the top Item
-can be connected. To control what unit is displayed in the left Dial, the leftDial Item provides a
-bool property named metricSystem. As we used a enum in our IDL file we need to convert the value
-first by testing the 'sytemType' property for the 'Metric' value.
+ Inside the QML \c initialize() function, we call \c{IviSimulator.initializeDefault()}, to read
+ the default values from the \c simulationData object and initialize all properties. This is
+ done only \b once, as we don't want the properties be reset to default when the next frontend
+ instance connects to the backend. Lastly, the base implementation is called to make sure that
+ the \c initializationDone signal is sent to the frontend.
-\quotefromfile ivicore/qface-tutorial/chapter2-enums-structs/instrument-cluster/Cluster.qml
-\skipto LeftDial
-\printuntil }
-\codeline
+ Similarly, a setter function is defined for each property; they use the
+ \c{IviSimulator.checkSettings()} to read specific constraint settings for the property from
+ the \c simulationData and check whether these constraints are valid for the new value. If
+ these constraints aren't valid, then \{IviSimulator.constraint()} is used to provide a
+ meaningful error message to the user.
-Like seen above the enums are part of the module class, which is also exported to QML as
-InstrumentClusterModule. To trigger a warning in the rightDial Item, we can also use 3 bindings to
-connect to the 3 member variables of the struct:
-
-\printuntil }
-
-\section1 Chapter 3
-
-In the previous two chapters we wrote a Middleware API in form of a IDL file and used the IVI
-Generator to autogenerate a C++ API in form of a library. In this chapter we will extend this by
-introducing a simulation backend and use annotations to define default values for our simulation.
-
-\section2 Frontend / Backend Separation
-
-QtIvi and the IVI Generator enables you to write code which use a Frontend / Backend Separation.
-Now what does that actually mean ? The separation is done to split an API from the actual
-implementation of this API. Qt uses this already in a lot of areas, most prominently the underling
-window system technology on the various supported Qt platforms e.g. xcb on linux and cocoa on mac.
-The same separation is done for our Middleware API, where the frontend provides the API in form of
-a library and the backend provides a implementation of this API. This is based on the Dynamic
-backend System of QtIvi which enables to switch between such backends at runtime.
-
-\image feature-backend.png
-
-\section2 Adding a simulation Backend
-
-For our Instrument Cluster we would like to add such a backend to provide actual values. For now we
-would like to just have some simulation behavior as we can't connect it easily to a real car,
-that's why such backends are called "simulation backend". To add such a backend we can use the help
-of the IVI Generator again and let it do the heavy lifting for us and generate one. This is done in
-a similar way as we already generated the library using the 'frontend' template, but we are now
-using the 'backend_simulator' template:
-
-\quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro
-\printto DESTDIR
-\skipto QT
-\printuntil CONFIG
-\skipto QFACE_FORMAT
-\printto CONFIG += install_ok
-
-Like for the frontend library, the project file builds a lib and defines the library name using
-qtLibraryTarget to also support windows debug postfix. One important aspect here is that the
-library name ends with '_simulation' which is a way to tell QtIvi that this is a simulation backend
-and once a 'production' backend is available prefer to use this over the 'simulation'. See <link>
-for more information on this.
-
-Enable the IVI Generator is also done in the same way by using the same QFACE_SOURCE, but defining
-QFACE_FORMAT to 'backend_simulator' to use the correct generation template. In addition we need to
-add 'plugin' into the CONFIG variable to make this library a Qt plugin which can easily be loaded
-at runtime.
-
-\section2 Link settings and how plugins are found.
-
-Trying to build the project file just as it is now, would result in compilation and linking errors.
-The reason for that is, in order to do the Frontend / Backend Separation there needs to be a
-defined interface class which needs to be implemented by the backend and is known to the frontend.
-This interface is called 'backend interface' and is automatically generated as part of the frontend
-library. Because this class provides signals and slots and uses QObject as the base class you need
-to link to the frontend library when you inherit from it. As this is needed for the backend plugin,
-we need to add the following lines in addition:
-
-\quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro
-\skipuntil CONFIG
-\printuntil INCLUDEPATH
-
-Now the project should build fine and create the plugin in your build folder (or the plugin folder
-in case you don't use a shadow build). If you would start the Instrument Cluster again you should
-still the following message:
-
-\badcode
-There is no production backend implementing "Example.IVI.InstrumentCluster.InstrumentCluster" .
-There is no simulation backend implementing "Example.IVI.InstrumentCluster.InstrumentCluster" .
-No suitable ServiceObject found.
-\endcode
-
-This indicates that QtIvi is still not able to find our just created simulation plugin. For this to
-understand you need to know a little bit more about Qts Plugin System and how it it finds plugins.
-Qt searches for it's plugins in multiple directories, the first one is the plugin folder which
-comes with your Qt installation and is called 'plugins'. Within the plugins folder every plugin
-type has it's own sub-folder e.g. 'platforms' for the platform plugins used to talk to the
-underlying platform API and the windowing system. QtIvi searches for its backend plugins in the
-'qtivi' folder. To make sure our simulation backend ends up in such a folder we add the following
-DESTDIR definition.
-
-\quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/backend_simulator/backend_simulator.pro
-\skipto DESTDIR
-\printuntil DESTDIR
-
-You might wondering how creating a qtivi folder in the upper directory should solve this problem of
-finding the plugin as it's not part of the system plugins folder. As said Qt supports searching in
-multiple folder for such plugins and one of those folders is the path of the executable itself.
-Another way would be to add an additional plugin path using the QCoreApplication::addLibraryPath()
-function or using the QT_PLUGIN_PATH environment variable. See "How to create Qt Plugins" for more
-information about this.
-
-\section2 Exporting the QML types in a QML Plugin
-
-In the first chapter we extended our main.cpp to register all types of our autogenerated Middleware
-API. Although this works fine, in bigger projects it's common to use a QML Plugin instead and be
-able to use qmlscene for the development. Although the code for doing this is relative simple, the
-IVI Generator supports to do this as well and makes it even easier. We already learned in the first
-chapter that for the QML import uri the module name is used. For a QML plugin this is important
-as the QmlEngine expects the plugin on a specific folder following the module name, where every section
-of the module name is a sub-folder. Our project file to generate a QML plugin looks like this:
-
-\quotefromfile ivicore/qface-tutorial/chapter3-simulation-backend/imports/imports.pro
-\printto target.path
-
-All lines until QFACE_SOURCES should be know. We use CONFIG to build a plugin. Define settings for
-the linker to link against our frontend library and use QFACE_FORMAT to define 'qmlplugin' as the
-used generation template. Instead of adding 'ivigenerator' to CONFIG we use the load() function to
-explicitly load the feature, this enables us to use the URI variable which is part of the 'qmlplugin'
-generation template. This URI can be used to define a DESTDIR by replacing all dots with slashes.
-
-In addition to the folder structure the QmlEngine also needs a qmldir file which indicates what
-files are part of the plugin under which uri. See <link> for more information about this. This
-qmldir file as well as a plugins.qmltypes files which provides information about code-completion is
-autogenerated by the IVI Generator, but needs to be put next to the library. To do this we add
-the files to a scope similar to a INSTALL target, but instead add it to the COPIES variable, which
-makes sure the files are copied when building the plugin.
-
-Now the plugin is ready to be used, but our Instrument Cluster application doesn't know where to
-search for it and is still using the old hardcoded registration. We can now remove the linking step
-in the instrument-cluster.pro file and change our main file to the following:
-
-\quotefromfile ivicore/qface-tutorial/chapter1-basics/instrument-cluster/main.cpp
-\skipto #include
-\printuntil }
-
-The important part which was changed is that we added an additional import path by using the
-addImportPath function, which points to the 'imports' folder next to the binary location.
-
-\section1 Chapter 4
-
-So far we created a Middleware API and integrated it into our Instrument Cluster QML code, extended
-it by using a QML plugin and generated a simulation backend. Quite a lot happened in the background
-to support us, but on the UI side not much has changed till now. This chapter is about bringing our
-simulation backend to life by defining sane default values and starting to simulate a real car
-ride.
-
-\section2 Defining default values
-
-We will start by defining default values for our properties. This is done using annotations in our
-qface IDL file. An annotation is a special kind of comment which adds extra data to a interface,
-method, property etc. For this use case we will use the 'config_simulator' annotation. More
-information what other kind of annotations are available can be found here. At the moment the
-temperature of our Instrument Cluster defaults to 0. Lets change this to a spring temperature and
-change it to 15 degree celsius. This is done by adding the following YAML fragment:
-
-\quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/instrument-cluster.qface
-\printuntil }
-
-After compiling the plugin again the temperature change should now be reflected in our Instrument
-Cluster. Let's take a look how this actually works. What happened when starting the IVIGenerator is
-that the config_simulator annotation was transformed into a JSON file which is now part of the
-simulation backend build folder. This JSON file looks like this:
-
-\quotefile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/instrumentcluster.json
-
-So how is this JSON file related to the actual simulation backend code ? The autogenerated
-simulation backend code is using the QIviSimulationEngine. The QIviSimulationEngine reads the JSON
-file and provides it's data to a QML simulation file. A default QML file is also autogenerated and
-loaded from the QIviSimulationEngine. This QML file provides the behavior of what should happen in
-the simulation backend. We will take a look at the QML file and how we can change in the next
-section, let's first see how we can change the default values in a more dynamic fashion.
-
-The QIviSimulationEngine allows us to override which JSON file should be loaded into the engine by
-setting the QTIVI_SIMULATION_DATA_OVERRIDE environment variable. As there can be multiple engines
-running by different backends we need to define which engine we mean. In the autogenerated code
-always the module name is used as the engine specifier. For this chapter we already prepared a
-second JSON file which is part of our source directory. Setting the environment variable like
-follows, will change the systemType to Mph instead of km/h:
-
-\badcode
-QTIVI_SIMULATION_DATA_OVERRIDE=instrumentcluster=<path-to-file>/miles.json
-\endcode
-
-\section2 How to define a QML behavior ?
-
-Before we define our own behavior, let's first take a look on what has been autogenerated already
-for us: There are two QML files. The first file instrumentcluster_simulation.qml is the entry point
-and just instantiates a InstrumentClusterSimulation.qml file. This is done as there can be multiple
-interfaces defined as part of the same module. The InstrumentClusterSimulation.qml file is way more
-interesting:
-
-\quotefile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/InstrumentClusterSimulation.qml
-
-First there is a 'settings' property, this is initialised with the return value of the
-IviSimulator.findData method, which takes the IviSimulator.simulationData and a string as input.
-The simulationData is the JSON file represented as a JavaScript object. The findData method helps
-us to extract just the data which is of interest for this interface. In this case for the
-"InstrumentCluster". The following properties helps the behavior to know whether the default
-values are set and a LoggingCategory to identify the log output of this simulation file.
-
-Afterwards the actual behavior is defined by instantiating a InstrumentClusterBackend Item and
-extending it with more functions. The InstrumentClusterBackend is our interface towards our
-InstrumentCluster QML frontend class, but other than the frontend the properties are also writable
-to make it possible to change them to provide a useful simulation.
-
-Every time a frontend instance connects to a backend the initialize() function is called. The same
-happens for the QML simulation, as the initialize() C++ function forwards this to the QML instance.
-The same happens for all other functions like setter and getters for properties or the methods. See
-QIviSimulationEngine for more information.
-
-Inside the QML initialize() function we call the IviSimulator.initializeDefault() function, which
-will read the default values from the simulationData object and initialises all properties. This is
-only done once, as we don't want the properties be reset to default when the next frontend instance
-connects to the backend. Last the base implementation is called to make sure the initializationDone
-signal is sent to the frontend.
-
-Similar a setter function is defined for every property. These setter functions use the
-IviSimulator.checkSettings() function which reads specific constraint settings for the property
-from the simulationData and checks whether these constraints are valid for the new value. In case
-the constraints are not valid, the IviSimulator.constraint() function is used to provide a
-meaningful error message to the user.
-
-\section2 Defining our own QML simulation
-
-As explained above does the InstrumentClusterBackend Item provide all the properties of our IDL
-file. This can be used to simulate a behavior by changing the properties to the values we want.
-This simplest form for this would be a value assignment, but this would be rather static and is not
-what we would like to achieve. Instead we use QML Animation objects to change the values over time:
-
-\quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml
-\skipto NumberAnimation
-\printuntil }
-
-This will change the speed property to 80 over 4000 seconds and simulates a accelerating car.
-Extending this to the other properties and by using sequential and parallel animations a full
-simulation could look like this:
-
-\quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml
-\skipto property var animation
-\printuntil property: "fuel"
-\printuntil property: "fuel"
-\printuntil }
-\printuntil }
+ \section2 Define Our Own QML Simulation
-To also provide a nice simulation for the 'rpm' property, we use a binding which does some calculations
-based on the current speed. The complete simulation file looks like this:
+ As mentioned above, does the InstrumentClusterBackend Item provide all the properties of our
+ QFace file. This can be used to simulate a behavior by changing the properties to the values
+ we want. The simplest form for this would be value assignment, but this would be rather static
+ not exactly what we'd like to achieve. Instead, we use QML Animation objects to change the
+ values over time:
-\quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml
-\skipto import
+ \quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml
+ \skipto NumberAnimation
+ \printuntil }
-The next step is to tell the IVI Generator and the QIviSimulationEngine about the new simulation file.
-Similar to QML files, the best aproach here is to put the simulation file into a resource file. In
-our example we added a new file called simulation.qrc which contains our simulation.qml using the
-/ prefix.
+ The code snippet above changes the speed property to 80 over 4000 seconds and simulates an
+ accelerating car. Extending this to the other properties, and combining both sequential and
+ parallel animations, we can create a full simulation:
-This location now needs to be added in form of an annotation to our QFace file:
+ \quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml
+ \skipto property var animation
+ \printuntil property: "fuel"
+ \printuntil property: "fuel"
+ \printuntil }
+ \printuntil }
-\quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/instrument-cluster.qface
-\printuntil module
-\dots
+ Then, to provide a nice simulation for the \c rpm property, we use a binding which does some
+ calculations based on the current speed. The complete simulation file looks like this:
-Rebuilding the simulation backend now will embed the simulation file into the plugin and give the
-file to the QIviSimulationEngine, which will start with the simulation once loaded.
+ \quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/backend_simulator/simulation.qml
+ \skipto import
-\section1 Chapter 5
+ The next step is to tell the IVI Generator and the QIviSimulationEngine about our new
+ simulation file. Similar to QML files, the best aproach here is to put the simulation file into
+ a resource file. In our example, we add a new file called \c simulation.qrc which contains our
+ \c simulation.qml using the \c{/} prefix.
-In this chapter we will extend our Instrument Cluster to use a IPC mechanism and use two processes.
-At the moment the simulation is loaded as a plugin and because of that part of the same service.
-Although this is good enough for a small example application, it is not how it works in modern
-multi-process architectures, where multiple processes need to be able to access the same value and
-react to changes. We could now write a second Application which is using the same Middleware API,
-but instead we can achieve the same thing by just starting the Instrument Cluster twice and check
-whether the animations are in sync, which they are not.
+ In our QFace file, this location now needs to be added in the form of an annotation:
-<< gif >> ????
+ \quotefromfile ivicore/qface-tutorial/chapter4-simulation-behavior/instrument-cluster.qface
+ \printuntil module
+ \dots
-\section2 Adding a QtRemoteObjects integration
+ Now, rebuilding the simulation backend embeds the simulation file into the plugin and hands the
+ file over to the QIviSimulationEngine, which starts the simulation when loaded.
-The IPC for this example is QtRemoteObjects as the IVI Generator already provides support for this
-out of the box. To use QtRemoteObjects we generate a second plugin, this time it is a "production"
-backend and because of that automatically preferred over the simulation backend we introduced
-before. This is done by the following project file:
+ \section1 Chapter 5
-\quotefromfile ivicore/qface-tutorial/chapter5-ipc/backend_qtro/backend_qtro.pro
-\printto CONFIG += install_ok
+ In this chapter we extend our Instrument Cluster to use an Inter-Process Communication (IPC)
+ mechanism and use two processes. At the moment, the simulation is loaded as a plugin that
+ causes it to be part of the same service. Although this is good enough for a small example
+ application, it's not how it's done in modern multi-process architectures, where multiple
+ processes need to be able to access the same value and react to changes. We could write a
+ second Application that uses the same Middleware API. However, we can achieve the same thing
+ just by starting the Instrument Cluster twice and checking whether the animations are in sync.
+ Currently, they're not.
-This pro file looks almost identical to the pro file we used for our simulation backend. For now we
-will just highlight what has been changed. The name for the plugin doesn't end with "_simulation"
-to indicate that this is a "production" backend and the QFACE_FORMAT has been changed to
-"backend_qtro" to generate a backend which uses QtRemoteObjects Replicas to connect to a
-QtRemoteObjects Source providing the values.
+ << gif >> ????
-In addition to a QtRemoteObject based backend, we also need a QtRemoteObject based server. This
-part can also be autogenerated using the IVI Generator in a similar fashion:
+ \section2 Add a QtRemoteObjects Integration
-\quotefromfile ivicore/qface-tutorial/chapter5-ipc/simulation_server/simulation_server.pro
-\printto target.path
+ The IPC for this example is QtRemoteObjects, because the IVI Generator already supports it
+ out of the box. To use QtRemoteObjects we generate a second plugin, a "production" backend,
+ this time. Production backends are automatically preferred over the simulation backend we
+ introduced before.
-Because we would like to generate a server binary, the TEMPLATE needs to be set to app instead of
-lib and similar to the plugin the server also needs to link against our library as it needs to have
-access to the defined enums, structs and other types. The template we use to generate a simulation
-server is called "server_qtro_simulator".
+ This is done with the following project, \c{.pro}, file:
-\section2 Reusing the existing simulation behavior
+ \quotefromfile ivicore/qface-tutorial/chapter5-ipc/backend_qtro/backend_qtro.pro
+ \printto CONFIG += install_ok
-If you start the server now and then the Instrument Cluster you don't see the simulation anymore,
-which we added in the previous chapter. The reason for this, is that the simulation code is part of
-our simulation backend, but this backend is not used anymore as we added the QtRemoteObjects based
-"production" backend.
+ This \c{.pro} file is almost identical to the one we used earlier for our simulation backend.
+ For now we highlight what's changed.
-Because we used the "server_qtro_simulator" generation template, this can easily be fixed, as the
-generated server code is also using the QIviSimulationEngine and supports to use the same
-simulation file than our simulation backend. We just need to extend the project file in the same
-way as we did before and are also able to use the same resource file for this.
+ The name of the plugin doesn't end with "_simulation" to indicate that this is a "production"
+ backend. The \c QFACE_FORMAT is now changed to "backend_qtro" to generate a backend that uses
+ QtRemoteObjects Replicas to connect to a QtRemoteObjects Source that provides the values. In
+ addition to a QtRemoteObject-based backend, we also need a QtRemoteObject-based server. This
+ part can also be autogenerated using the IVI Generator in a similar fashion:
-\quotefromfile ivicore/qface-tutorial/chapter5-ipc/simulation_server/simulation_server.pro
-\skipto RESOURCES
-\printuntil RESOURCES
+ \quotefromfile ivicore/qface-tutorial/chapter5-ipc/simulation_server/simulation_server.pro
+ \printto target.path
-In the same way we can also use the other simulation data JSON file we defined in the previous
-chapter by using the same environment variable. We just need to pass it to the server instead of
-our Instrument Cluster application.
+ Because we'd like to generate a server binary, the \c TEMPLATE needs to be set to "app" instead
+ of "lib". Similar to the plugin, the server also needs to link against our library to give it
+ access to the defined enums, structures, and other types. The template we use to generate a
+ simulation server is called "server_qtro_simulator".
-Doing the final test, starting two Instrument Cluster instances should now show the animations in
-sync:
+ \section2 Reuse the Existing Simulation Behavior
-<< gif >> ???
+ Now, if you start the server and then the Instrument Cluster, you don't see the simulation
+ from our previous chapter anymore. The reason for this, is that the simulation code is part of
+ our simulation backend, but this backend is no longer used as we added the
+ QtRemoteObjects-based "production" backend.
-\section1 Chapter 6
+ Because we used the "server_qtro_simulator" generation template, this can easily be fixed, as
+ the generated server code is also using the QIviSimulationEngine and supports to use the same
+ simulation file than our simulation backend. We just need to extend the project file in the
+ same way as we did before and are also able to use the same resource file for this.
-In the previous chapter we extended our Instrument Cluster code by using QtRemoteObjects as IPC and
-autogenerated a backend for it, as well as a server providing the simulation. In this chapter we
-would like to manually write our own backend which is using DBus as IPC. For this we already
-prepared a working D-Bus server which provides a very limited simulation.
+ \quotefromfile ivicore/qface-tutorial/chapter5-ipc/simulation_server/simulation_server.pro
+ \skipto RESOURCES
+ \printuntil RESOURCES
-Let's first take a short look at the server code and see what is done there and then write the
-backend connecting to it.
+ In the same way, we can also use the other simulation data JSON file that we defined in the
+ previous chapter, by using the same environment variable. We just need to pass it to the
+ server instead of our Instrument Cluster application.
-\section2 D-Bus Server
+ Let's do the final test: starting two Instrument Cluster instances should now show the
+ animations in sync:
-As said above we will use D-Bus for this chapter and we already have a XML file describing the
-D-Bus interface, similar to our qface IDL:
+ << gif >> ???
-\quotefile ivicore/qface-tutorial/chapter6-own-backend/demo_server/instrumentcluster.xml
+ \section1 Chapter 6
-This xml file is used to let qmake generate a base class which is extended by the server with
-actual functionality. See the QtDBus documentation for more information about this works in detail.
+ Previously, we extended our Instrument Cluster code by using QtRemoteObjects as IPC and
+ autogenerated a backend for it, as well as a server that provides the simulation. In this
+ chapter, we'd like to write our own backend \b manually using D-Bus as IPC.
-Our D-Bus server starts on the session bus on the / path and provides a interface name
-"Example.IVI.InstrumentCluster". To simulate some values, we keep it simple and just use a timer
-event to change the speed value each 100 milliseconds and start at 0 again once the maximum of 250
-is reached. In a similar way the rpm value is increased until 5000. For all other properties we
-simply provide hardcoded values.
+ We've already prepared a working D-Bus server which provides limited simulation.
-\quotefromfile ivicore/qface-tutorial/chapter6-own-backend/demo_server/instrumentcluster.cpp
-\skipto timerEvent
-\printuntil }
+ First, let's look at the server code and see what's done there; then write the backend that
+ connects to it.
-\section2 Our own D-Bus Backend
+ \section2 D-Bus Server
-Let's start with a project file for our backend. This is very similar to previous project files,
-but doesn't use the IVI Generator, but instead uses DBUS_INTERFACES to autogenerate some client
-code which sends and receives messages over D-Bus. First we need to define the entry point for our
-plugin. This plugin class needs to derive from QIviServiceInterface and implement two functions:
+ As mentioned above, we use D-Bus for this chapter and we already have an XML file that
+ describes the D-Bus interface, similar to our QFace file:
-\list
- \li \c {QStringList interfaces()} Returns a list of all interfaces supported by this plugin
- \li \c {QIviFeatureInterface *interfaceInstance(const QString &interface)} Returns a instance of the requested interface
-\endlist
+ \quotefile ivicore/qface-tutorial/chapter6-own-backend/demo_server/instrumentcluster.xml
-In addition we also need to provide the list of interfaces we support as plugin metadata in form of
-a JSON file which looks like this:
+ This XML file is used to let qmake generate a base class which is extended by the server with
+ actual functionality. For more information, see \l{QtDBus}.
-\quotefile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentcluster_dbus.json
+ Our D-Bus server starts on the session bus, on the \c{/} path, and provides an interface named
+ "Example.IVI.InstrumentCluster". To simulate some values, we keep it simple and use a timer
+ event to change the speed value every 100 milliseconds. Then, we start from 0, once the
+ maximum of 250 is reached. Similarly, the \c rpm value is increased to 5000. For all other
+ properties, we provide hardcoded values.
-This is needed as it gives QtIvi the chance to know which interfaces a backend supports before
-instantiating it and only load the plugins which are needed for by the app code.
+ \quotefromfile ivicore/qface-tutorial/chapter6-own-backend/demo_server/instrumentcluster.cpp
+ \skipto timerEvent
+ \printuntil }
-Our plugin code looks like this:
+ \section2 Write Our own D-Bus Backend
-\quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterplugin.cpp
-\skipto #include
-\printuntil eof
+ Let's start with a \c{.pro} file for our backend. This is very similar to previous \c{.pro}
+ files, but it doesn't use the IVI Generator. Instead, it uses \c DBUS_INTERFACES to
+ autogenerate some client code which sends and receives messages over D-Bus.
-In interfaces() we use the IID which is defined in instrumentclusterbackendinterface.h from our
-autogenerated library. In insterfaceInstance() we check for the correct string and return a
-instance of our implemented instrument cluster backend.
+ Now, we need to define an entry point for our plugin. This plugin class needs to derive from
+ QIviServiceInterface and implement two functions:
-This backend will be defined in instrumentclusterbackend.h and derives from
-InstrumentClusterBackendInterface. In our InstrumentClusterBackend class we need to implement all
-pure virtual functions from InstrumentClusterBackendInterface and derived classes. For our example
-this is pretty simple, as we just need to implement the initialize() function. If our IDL would use
-writable properties or methods, we would need to implement these as well. We don't need to
-implement getters for our properties, because QtIvi uses the changed signals during the
-initialization phase to get informed about the current state. Although the generated DBus interface
-class would provides getters to retrieve the properties from our server, it is not recommended to
-use these when developing a backend. These getters are implemented by using synchronous calls, which
-means they will block the event loop until an answer is received by the client. This can lead to
-performance issues and is the reason why we recommend to use async calls instead.
+ \list
+ \li \c {QStringList interfaces()} -- that returns a list of all interfaces this plugin
+ supports.
+ \li \c {QIviFeatureInterface *interfaceInstance(const QString &interface)} -- that returns
+ an instance of the requested interface.
+ \endlist
-In our backend we define a fetch function for every property which is implemented like this:
+ Additionally, we also need to provide a list of interfaces we support as plugin metadata, in
+ the form of a JSON file which looks like this:
-\quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp
-\skipto ::fetchSpeed
-\printto ::fetchRpm
+ \quotefile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentcluster_dbus.json
-First we add the property to a list to know which properties have been fetched successfully. In the
-next step we use the asyncCall() function to call the getter for the 'speed' property and use a
-QDBusPendingCallWatcher to wait for the result. Once the result is ready the lambda will remove the
-property again from our fetchList use the onSpeedChanged() function to store the value and notify
-the frontend about it. As we don't need the watcher anymore we delete it in the next event loop
-run using deleteLater() and call the checkInitDone() function.
+ We need this list, as it gives QtIvi the chance to know which interfaces a backend supports,
+ before instantiating it and loading only the plugins which the application code needs.
-The checkInitDone() function is defined as follows:
+ Our plugin code looks like this:
-\quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp
-\skipto ::checkInitDone
-\printto onSpeedChanged
+ \quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterplugin.cpp
+ \skipto #include
+ \printuntil eof
-It makes sure the initializationDone() signal is sent to the frontend once all our properties were
-fetched from the server and the initialization is complete.
+ In \c interfaces() we use the IID which is defined in \c{instrumentclusterbackendinterface.h}
+ from our autogenerated library. In \c insterfaceInstance() we check for the correct string and
+ return an instance of the instrument cluster backend we implemented.
-In addition to retrieving the current state from the server, we also need to inform our frontend
-about every property change. This is done by emitting the corresponding change signal once the
-server changed one of it's properties. For this we define a slot for every property. This slot
-will save the property in our class an emit the change signal:
+ This backend is defined in \c instrumentclusterbackend.h and derives from
+ \c InstrumentClusterBackendInterface. In our \c InstrumentClusterBackend class, we need to
+ implement all pure virtual functions from InstrumentClusterBackendInterface and derived classes.
-\quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp
-\skipto void InstrumentClusterBackend::onSpeedChanged(int speed)
-\printto onRpmChanged
+ For our example, this isn't complex, as we just need to implement the initialize() function.
+ If our XML file would use writable properties or methods, then we'd need to implement those as
+ well. We don't need to implement getters for our properties, because QtIvi uses the changed
+ signals during the initialization phase to get information about the current state. Although
+ the generated D-Bus interface class would provide getters to retrieve the properties from our
+ server, it's not recommended to use these when you develop a backend. These getters are
+ implemented by using synchronous calls, which means they will block the event loop until an
+ answer is received by the client. Since this can lead to performance issues, we recommend to
+ use \b asynchronous calls instead.
-The same slot is also used during the initialization phase to save and emit the value.
+ In our backend, we define a fetch function for each property that's implemented like this:
-You might think now why saving the value is needed at all and why not just emit the signal. The
-reason for this is that the backend plugin is used directly by every instance of the InstrumentCluster
-class and every instance calls the initialize() function to retrieve the current state. Instead
-of fetching all properties again the second initialize() call will just emit the already saved
-values and the slots will keep them uptodate.
+ \quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp
+ \skipto ::fetchSpeed
+ \printto ::fetchRpm
-When starting the Instrument Cluster now, our backend should connect to our D-Bus server and now
-look like this:
+ First, we add the property to a list, to know which properties have been fetched successfully.
+ Next, we use the \c asyncCall() function to call the getter for the \c speed property and use a
+ \c QDBusPendingCallWatcher to wait for the result. Once the result is ready, the lambda removes
+ the property again from our \c fetchList, uses the \c onSpeedChanged() function to store the
+ value and notifies the frontend about it. Since we don't need the watcher anymore, we delete it
+ in the next event loop run using \c deleteLater(), and call the \c checkInitDone() function.
+
+ The \c checkInitDone() function is defined as follows:
+
+ \quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp
+ \skipto ::checkInitDone
+ \printto onSpeedChanged
+
+ It ensures that the \c initializationDone() signal is sent to the frontend once all our
+ properties are fetched from the server, and the initialization is complete.
+
+ In addition to retrieving the current state from the server, we also need to inform our frontend
+ every time a property changes. This is done by emitting the corresponding change signal when the
+ server changes one of its properties. To handle this, we define a slot for each property. This
+ slot saves the property in our class an emits the change signal:
+
+ \quotefromfile ivicore/qface-tutorial/chapter6-own-backend/backend_dbus/instrumentclusterbackend.cpp
+ \skipto void InstrumentClusterBackend::onSpeedChanged(int speed)
+ \printto onRpmChanged
+
+ The same slot is also used during the initialization phase to save and emit the value.
+
+ You might wonder why saving the value is needed at all, if we can just emit the signal. This is
+ because the backend plugin is used directly by every instance of the \c InstrumentCluster class
+ and every instance calls the \c initialize() function to retrieve the current state. Instead of
+ fetching all properties again, the second \c initialize() call just emits values that were
+ already saved; and the slots keep them up to date.
+
+ Now, when we start the Instrument Cluster, our backend should connect to our D-Bus server and
+ look like this:
< gif >