Add example of communication using protobuf messages

Add the protobuf example that emulates the work of dummy sensors that
send data to the sensor client. The example uses UDP sockets to send
datagrams that contain protobuf messages. Messages consist of two
layers:
 - The Type-Length-Value wrapping message that specifies the the
   message type and allows to verify the message size.
 - Sensor message that contains a sensor data.

The example intends to show how to generate the code from the protobuf
schema and use it in simple UDP signalling protocol on both sender and
receiver sides.

Both client and emulator have simple UI implemented using QtWidgets.

Task-number: QTBUG-109598
Pick-to: 6.5
Change-Id: I13e2c5bcd995b8aa6d873c495a7bd83f6651a061
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Alexey Edelev 2023-01-06 18:18:36 +01:00
parent 1a72cce91d
commit 76d94d29ea
25 changed files with 1181 additions and 2 deletions

View File

@ -12,7 +12,7 @@ project(QtGrpc
)
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core)
find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Network)
find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Network Gui Widgets)
# Try to find Qt6::qtprotobufgen and Qt6::qtgrpcgen targets from host tools
# when cross-compiling.

8
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_examples_build_begin(EXTERNAL_BUILD)
add_subdirectory(protobuf)
qt_examples_build_end()

View File

@ -0,0 +1,6 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(QT_FEATURE_qtprotobufgen AND TARGET Qt6::Network AND TARGET Qt6::Widgets)
qt_internal_add_example(sensors)
endif()

View File

@ -0,0 +1,32 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(protobuf_sensors LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/protobuf/sensors")
find_package(Qt6 REQUIRED COMPONENTS Core Protobuf Widgets Network)
qt_standard_project_setup()
add_subdirectory(emulator)
add_subdirectory(client)
#! [0]
qt_add_protobuf(protobuf_sensors
PROTO_FILES
sensors.proto
tlv.proto
)
#! [0]
install(TARGETS protobuf_sensors
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,25 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
qt_add_executable(protobuf_sensors_client
main.cpp
clientconsole.cpp clientconsole.h
sensorclient.cpp sensorclient.h
clientconsole.ui
)
target_link_libraries(protobuf_sensors_client PRIVATE
Qt6::Core
Qt6::Protobuf
Qt6::Widgets
Qt6::Network
protobuf_sensors
)
install(TARGETS protobuf_sensors_client
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "clientconsole.h"
#include "ui_clientconsole.h"
#include "sensors.qpb.h"
#include <QMessageBox>
ClientConsole::ClientConsole(QWidget *parent) : QWidget(parent), ui(new Ui::ClientConsole)
{
ui->setupUi(this);
}
void ClientConsole::onCoordinatesUpdated(const qt::examples::sensors::Coordinates &coord)
{
//! [0]
ui->latitudeValue->setText(QString::number(coord.latitude(), 'f', 7));
ui->longitudeValue->setText(QString::number(coord.longitude(), 'f', 7));
ui->altitudeValue->setText(QString::number(coord.altitude(), 'f', 7));
//! [0]
}
void ClientConsole::onTemperatureUpdated(const qt::examples::sensors::Temperature &temp)
{
ui->temperature->setText(
QString("%1 %2")
.arg(QString::number(temp.value()))
.arg(temp.units() == qt::examples::sensors::Temperature::Celsius ? 'C' : 'F'));
}
void ClientConsole::onWarning(const qt::examples::sensors::WarningNotification &warn)
{
if (!warn.text().isEmpty())
QMessageBox::information(this, QObject::tr("Important notification"), warn.text());
}
ClientConsole::~ClientConsole()
{
delete ui;
}
#include "moc_clientconsole.cpp"

View File

@ -0,0 +1,37 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CLIENTCONSOLE_H
#define CLIENTCONSOLE_H
#include <QWidget>
namespace qt::examples::sensors {
class Coordinates;
class Temperature;
class WarningNotification;
} // namespace qt::examples::sensors
QT_BEGIN_NAMESPACE
namespace Ui {
class ClientConsole;
}
QT_END_NAMESPACE
class ClientConsole : public QWidget
{
Q_OBJECT
public:
ClientConsole(QWidget *parent = nullptr);
~ClientConsole();
void onCoordinatesUpdated(const qt::examples::sensors::Coordinates &coord);
void onTemperatureUpdated(const qt::examples::sensors::Temperature &temp);
void onWarning(const qt::examples::sensors::WarningNotification &warn);
private:
Ui::ClientConsole *ui;
};
#endif // CLIENTCONSOLE_H

View File

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ClientConsole</class>
<widget class="QWidget" name="ClientConsole">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>256</width>
<height>244</height>
</rect>
</property>
<property name="windowTitle">
<string>Client Console</string>
</property>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>241</width>
<height>141</height>
</rect>
</property>
<property name="title">
<string>Coordinates</string>
</property>
<widget class="QWidget" name="layoutWidget1">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="longitudeValue">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget2">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Altitude</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="altitudeValue">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget3">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="latitudeValue">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>241</width>
<height>81</height>
</rect>
</property>
<property name="title">
<string>Weather</string>
</property>
<widget class="QWidget" name="layoutWidget4">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Temperature</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="temperature">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,21 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "clientconsole.h"
#include "sensorclient.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SensorClient client;
ClientConsole console;
QObject::connect(&client, &SensorClient::coordinatesUpdated, &console,
&ClientConsole::onCoordinatesUpdated);
QObject::connect(&client, &SensorClient::temperatureUpdated, &console,
&ClientConsole::onTemperatureUpdated);
QObject::connect(&client, &SensorClient::warning, &console, &ClientConsole::onWarning);
console.show();
return app.exec();
}

View File

@ -0,0 +1,68 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sensorclient.h"
#include <QNetworkDatagram>
#include <QDebug>
#include "sensors.qpb.h"
#include "tlv.qpb.h"
SensorClient::SensorClient(QObject *parent) : QObject(parent)
{
Q_ASSERT_X(m_client.bind(QHostAddress::LocalHost, 65500), "SensorClient",
"Unable to bind to port 65500");
QObject::connect(&m_client, &QUdpSocket::readyRead, this, &SensorClient::receive);
}
void SensorClient::receive()
{
while (m_client.hasPendingDatagrams()) {
//! [0]
const auto datagram = m_client.receiveDatagram();
qt::examples::sensors::tlv::TlvMessage msg;
msg.deserialize(&m_serializer, datagram.data());
if (m_serializer.deserializationError()
!= QAbstractProtobufSerializer::NoError) {
qWarning().nospace() << "Unable to deserialize datagram ("
<< m_serializer.deserializationError() << ")"
<< m_serializer.deserializationErrorString();
continue;
}
//! [0]
switch (msg.type()) {
case qt::examples::sensors::tlv::MessageTypeGadget::Coordinates: {
//! [1]
qt::examples::sensors::Coordinates coord;
coord.deserialize(&m_serializer, msg.value());
emit coordinatesUpdated(coord);
break;
//! [1]
}
case qt::examples::sensors::tlv::MessageTypeGadget::Temperature: {
qt::examples::sensors::Temperature temp;
temp.deserialize(&m_serializer, msg.value());
emit temperatureUpdated(temp);
break;
}
case qt::examples::sensors::tlv::MessageTypeGadget::WarningNotification: {
qt::examples::sensors::WarningNotification warn;
warn.deserialize(&m_serializer, msg.value());
emit warning(warn);
break;
}
}
if (m_serializer.deserializationError()
!= QAbstractProtobufSerializer::NoError) {
qWarning().nospace() << "Unable to deserialize message ("
<< m_serializer.deserializationError() << ")"
<< m_serializer.deserializationErrorString();
continue;
}
}
}
#include "moc_sensorclient.cpp"

View File

@ -0,0 +1,35 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SENSORCLIENT_H
#define SENSORCLIENT_H
#include <QObject>
#include <QProtobufSerializer>
#include <QUdpSocket>
namespace qt::examples::sensors {
class Coordinates;
class Temperature;
class WarningNotification;
} // namespace qt::examples::sensors
class SensorClient : public QObject
{
Q_OBJECT
public:
explicit SensorClient(QObject *parent = nullptr);
void receive();
signals:
void coordinatesUpdated(const qt::examples::sensors::Coordinates &);
void temperatureUpdated(const qt::examples::sensors::Temperature &);
void warning(const qt::examples::sensors::WarningNotification &);
private:
QUdpSocket m_client;
QProtobufSerializer m_serializer;
};
#endif // SENSORCLIENT_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,85 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example sensors
\ingroup qtprotobuf-examples
\meta tag {network,protobuf,serialization,udp}
\meta category {Networking}
\title Sensors
\brief The Sensors example shows how two applications can communicate by
sending protobuf messages using UDP sockets.
The Sensors example consists of the following components:
\list
\li \c protobuf_sensors library that you generate from the protobuf schema
using the \l qt_add_protobuf call.
\li \c protobuf_sensor_emulator application that emulates simple sensor
behavior.
\li \c protobuf_sensors_client application that displays the sensor data
from the UDP socket.
\endlist
The client application binds on the \c localhost UDP port \c 65500 and
waits for data from the emulator application.
\image client.webp
Use the emulator application to change the values of sensor data and send
the data to the client's UDP port.
\image emulator.webp
The applications use the generated messages from the \c protobuf_sensors
library to communicate. The library is generated from the protobuf schema
described in .proto files.
\snippet sensors/CMakeLists.txt 0
The first file describes the Type-Length-Value message, that wraps the
sensor data:
\snippet sensors/tlv.proto 0
The second .proto file contains a description of the sensor messages:
\snippet sensors/sensors.proto 0
Messages are serialized using \l QProtobufSerializer that you instantiate in
the client:
\code
class SensorClient : public QObject
{
Q_OBJECT
...
private:
QUdpSocket m_client;
QProtobufSerializer m_serializer;
};
\endcode
And the emulator:
\code
class SensorEmulator : public QObject
{
Q_OBJECT
...
QUdpSocket m_socket;
QProtobufSerializer m_serializer;
};
\endcode
After you click the \uicontrol Send button in the emulator application, the
data from \l QLineEdit fields is converted from string format to the message
field-specific format, for example, double for the fields of the Coordinates
message:
\snippet sensors/emulator/emulatorconsole.cpp 0
Then the message with all the fields is serialized and wrapped with the
Type-Length-Value message:
\snippet sensors/emulator/sensoremulator.cpp 0
The client applies the reverse operations on the received datagrams. First
the Type-Length-Value message is deserialized from the datagram data:
\snippet sensors/client/sensorclient.cpp 0
Then the Type-Length-Value message is deserialized into the sensor message:
\snippet sensors/client/sensorclient.cpp 1
Finally, it is converted and displayed to the user:
\snippet sensors/client/clientconsole.cpp 0
\note Before running the example, make sure that your operating system
allows binding on UDP port \c 65500 and sending the data over UDP.
*/

View File

@ -0,0 +1,25 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
qt_add_executable(protobuf_sensor_emulator
main.cpp
sensoremulator.h sensoremulator.cpp
emulatorconsole.h emulatorconsole.cpp
emulatorconsole.ui
)
target_link_libraries(protobuf_sensor_emulator PRIVATE
Qt6::Core
Qt6::Protobuf
Qt6::Widgets
Qt6::Network
protobuf_sensors
)
install(TARGETS protobuf_sensor_emulator
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,58 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "emulatorconsole.h"
#include "ui_emulatorconsole.h"
#include <QDoubleValidator>
#include <QIntValidator>
#include "sensors.qpb.h"
EmulatorConsole::EmulatorConsole(QWidget *parent) : QWidget(parent), ui(new Ui::EmulatorConsole)
{
ui->setupUi(this);
auto validator = new QDoubleValidator(-90, 90, 7, ui->latitudeValue);
validator->setLocale(QLocale::c());
ui->latitudeValue->setValidator(validator);
validator = new QDoubleValidator(-180, 180, 7, ui->longitudeValue);
validator->setLocale(QLocale::c());
ui->longitudeValue->setValidator(validator);
validator = new QDoubleValidator(-1000, 1000, 7, ui->altitudeValue);
validator->setLocale(QLocale::c());
ui->altitudeValue->setValidator(validator);
ui->temperatureValue->setValidator(new QIntValidator(-50, 50, ui->temperatureValue));
// ![0]
QObject::connect(ui->sendCoordinates, &QPushButton::clicked, this, [this]() {
qt::examples::sensors::Coordinates coord;
coord.setLatitude(ui->latitudeValue->text().toDouble());
coord.setLongitude(ui->longitudeValue->text().toDouble());
coord.setAltitude(ui->altitudeValue->text().toDouble());
emit coordinatesUpdated(coord);
});
// ![0]
QObject::connect(ui->sendTemperature, &QPushButton::clicked, this, [this]() {
qt::examples::sensors::Temperature temp;
temp.setValue(ui->temperatureValue->text().toInt());
temp.setUnits(ui->celciusRadio->isChecked()
? qt::examples::sensors::Temperature::Celsius
: qt::examples::sensors::Temperature::Farenheit);
emit temperatureUpdated(temp);
});
QObject::connect(ui->sendMessage, &QPushButton::clicked, this, [this]() {
qt::examples::sensors::WarningNotification warn;
warn.setText(ui->warningText->toPlainText());
emit warning(warn);
});
}
EmulatorConsole::~EmulatorConsole()
{
delete ui;
}
#include "moc_emulatorconsole.cpp"

View File

@ -0,0 +1,37 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef EMULATORCONSOLE_H
#define EMULATORCONSOLE_H
#include <QWidget>
namespace qt::examples::sensors {
class Coordinates;
class Temperature;
class WarningNotification;
} // namespace qt::examples::sensors
QT_BEGIN_NAMESPACE
namespace Ui {
class EmulatorConsole;
}
QT_END_NAMESPACE
class EmulatorConsole : public QWidget
{
Q_OBJECT
public:
explicit EmulatorConsole(QWidget *parent = nullptr);
~EmulatorConsole();
signals:
void coordinatesUpdated(const qt::examples::sensors::Coordinates &);
void temperatureUpdated(const qt::examples::sensors::Temperature &);
void warning(const qt::examples::sensors::WarningNotification &);
private:
Ui::EmulatorConsole *ui;
};
#endif // EMULATORCONSOLE_H

View File

@ -0,0 +1,326 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EmulatorConsole</class>
<widget class="QWidget" name="EmulatorConsole">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>333</width>
<height>456</height>
</rect>
</property>
<property name="windowTitle">
<string>Emulator Console</string>
</property>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>241</width>
<height>141</height>
</rect>
</property>
<property name="title">
<string>Coordinates</string>
</property>
<widget class="QWidget" name="layoutWidget1">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="longitudeValue">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>13.5331485</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget2">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Altitude</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="altitudeValue">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0.0</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget3">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="latitudeValue">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>52.4317463</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>241</width>
<height>141</height>
</rect>
</property>
<property name="title">
<string>Weather</string>
</property>
<widget class="QWidget" name="layoutWidget4">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>216</width>
<height>27</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Temperature</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="temperatureValue">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QRadioButton" name="celciusRadio">
<property name="geometry">
<rect>
<x>20</x>
<y>80</y>
<width>112</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Celcius</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QRadioButton" name="fahrenheitRadio">
<property name="geometry">
<rect>
<x>20</x>
<y>110</y>
<width>112</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Fahrenheit</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_3">
<property name="geometry">
<rect>
<x>10</x>
<y>310</y>
<width>241</width>
<height>141</height>
</rect>
</property>
<property name="title">
<string>Warning message</string>
</property>
<widget class="QTextEdit" name="warningText">
<property name="geometry">
<rect>
<x>3</x>
<y>24</y>
<width>231</width>
<height>111</height>
</rect>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;!!!A simple warning message!!!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</widget>
<widget class="QPushButton" name="sendCoordinates">
<property name="geometry">
<rect>
<x>260</x>
<y>120</y>
<width>61</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
<widget class="QPushButton" name="sendTemperature">
<property name="geometry">
<rect>
<x>260</x>
<y>270</y>
<width>61</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
<widget class="QPushButton" name="sendMessage">
<property name="geometry">
<rect>
<x>260</x>
<y>420</y>
<width>61</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,21 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "emulatorconsole.h"
#include <QApplication>
#include "sensoremulator.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SensorEmulator emul;
EmulatorConsole console;
QObject::connect(&console, &EmulatorConsole::coordinatesUpdated, &emul,
&SensorEmulator::sendCoordinates);
QObject::connect(&console, &EmulatorConsole::temperatureUpdated, &emul,
&SensorEmulator::sendTemperature);
QObject::connect(&console, &EmulatorConsole::warning, &emul, &SensorEmulator::sendWarning);
console.show();
return app.exec();
}

View File

@ -0,0 +1,52 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sensoremulator.h"
#include <QDebug>
#include <QNetworkDatagram>
#include "tlv.qpb.h"
#include "sensors.qpb.h"
namespace {
QByteArray makeTlvMessage(QProtobufSerializer *serializer, const QByteArray &data,
qt::examples::sensors::tlv::MessageTypeGadget::MessageType type)
{
//! [0]
Q_ASSERT(serializer != nullptr);
qt::examples::sensors::tlv::TlvMessage msg;
msg.setType(type);
msg.setValue(data);
return msg.serialize(serializer);
//! [0]
}
} // namespace
SensorEmulator::SensorEmulator(QObject *parent) : QObject(parent) { }
void SensorEmulator::send(const QByteArray &data)
{
if (m_socket.writeDatagram(data, QHostAddress::LocalHost, 65500) == -1)
qWarning() << "Unable to send data of size: " << data.size() << "to UDP port 65500";
}
void SensorEmulator::sendCoordinates(const qt::examples::sensors::Coordinates &coords)
{
send(makeTlvMessage(&m_serializer, coords.serialize(&m_serializer),
qt::examples::sensors::tlv::MessageTypeGadget::Coordinates));
}
void SensorEmulator::sendTemperature(const qt::examples::sensors::Temperature &temp)
{
send(makeTlvMessage(&m_serializer, temp.serialize(&m_serializer),
qt::examples::sensors::tlv::MessageTypeGadget::Temperature));
}
void SensorEmulator::sendWarning(const qt::examples::sensors::WarningNotification &warn)
{
send(makeTlvMessage(&m_serializer, warn.serialize(&m_serializer),
qt::examples::sensors::tlv::MessageTypeGadget::WarningNotification));
}
#include "moc_sensoremulator.cpp"

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SENSOREMULATOR_H
#define SENSOREMULATOR_H
#include <QObject>
#include <QUdpSocket>
#include <QProtobufSerializer>
namespace qt::examples::sensors {
class Coordinates;
class Temperature;
class WarningNotification;
} // namespace qt::examples::sensors
class SensorEmulator : public QObject
{
Q_OBJECT
public:
explicit SensorEmulator(QObject *parent = nullptr);
void sendCoordinates(const qt::examples::sensors::Coordinates &coords);
void sendTemperature(const qt::examples::sensors::Temperature &temp);
void sendWarning(const qt::examples::sensors::WarningNotification &warn);
private:
void send(const QByteArray &data);
QUdpSocket m_socket;
QProtobufSerializer m_serializer;
};
#endif // SENSOREMULATOR_H

View File

@ -0,0 +1,27 @@
syntax = "proto3";
//! [0]
package qt.examples.sensors;
message Coordinates
{
double longitude = 1;
double latitude = 2;
double altitude = 3;
}
message Temperature
{
enum Unit {
Farenheit = 0;
Celsius = 1;
}
sint32 value = 1;
Unit units = 2;
}
message WarningNotification
{
string text = 1;
}
//! [0]

View File

@ -0,0 +1,18 @@
syntax = "proto3";
//! [0]
package qt.examples.sensors.tlv;
enum MessageType {
Coordinates = 0;
Temperature = 1;
WarningNotification = 2;
}
// Protobuf messages imply inline data size.
message TlvMessage
{
MessageType type = 1;
bytes value = 2;
}
//! [0]

View File

@ -32,9 +32,12 @@ depends += qtdoc qtcore qtcmake
{headerdirs,sourcedirs} += ..
sourcedirs += ../../tools/doc/src
url.examples = "https://code.qt.io/cgit/qt/qtgrpc.git/tree/examples/\1?h=$QT_VER"
exampledirs += ../../../examples/protobuf
imagedirs += images
imagedirs += images \
../../../examples/protobuf/sensors/doc/images
navigation.landingpage = "Qt Protobuf"
navigation.cppclassespage = "Qt Protobuf C++ Classes"

View File

@ -21,6 +21,12 @@
\list
\l {Qt Protobuf C++ Classes}{C++ Classes}
\endlist
\section1 Examples
\list
\li \l {Qt Protobuf Examples}
\endlist
*/
/*!
@ -32,3 +38,14 @@
\since 6.5
\brief Provides protocol buffers support in Qt.
*/
/*!
\group qtprotobuf-examples
\ingroup all-examples
\keyword Qt Protobuf Examples
\title Qt Protobuf Examples
\brief A collection of examples for \l {Qt Protobuf}
These examples demonstrate how to generate code using the protobuf schema
and use it in your projects.
*/