mirror of
https://github.com/Serial-Studio/Serial-Studio.git
synced 2025-01-31 17:42:55 +08:00
Let Serial Studio act as a MQTT publisher (WIP)
This commit is contained in:
parent
44b734641f
commit
a8fb35cc49
@ -121,12 +121,8 @@ RESOURCES += \
|
|||||||
assets/assets.qrc
|
assets/assets.qrc
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
assets/qml/PlatformDependent/DecentMenuItem.qml \
|
assets/qml/PlatformDependent/*.qml \
|
||||||
assets/qml/PlatformDependent/Menubar.qml \
|
assets/qml/SetupPanes/*.qml \
|
||||||
assets/qml/PlatformDependent/MenubarMacOS.qml \
|
|
||||||
assets/qml/SetupPanes/Network.qml \
|
|
||||||
assets/qml/SetupPanes/Serial.qml \
|
|
||||||
assets/qml/SetupPanes/Settings.qml \
|
|
||||||
assets/qml/Widgets/*.qml \
|
assets/qml/Widgets/*.qml \
|
||||||
assets/qml/Windows/*.qml \
|
assets/qml/Windows/*.qml \
|
||||||
assets/qml/SetupPanes/*.qml \
|
assets/qml/SetupPanes/*.qml \
|
||||||
@ -151,6 +147,7 @@ HEADERS += \
|
|||||||
src/JSON/FrameInfo.h \
|
src/JSON/FrameInfo.h \
|
||||||
src/JSON/Generator.h \
|
src/JSON/Generator.h \
|
||||||
src/JSON/Group.h \
|
src/JSON/Group.h \
|
||||||
|
src/MQTT/Publisher.h \
|
||||||
src/Misc/ModuleManager.h \
|
src/Misc/ModuleManager.h \
|
||||||
src/Misc/TimerEvents.h \
|
src/Misc/TimerEvents.h \
|
||||||
src/Misc/Translator.h \
|
src/Misc/Translator.h \
|
||||||
@ -172,6 +169,7 @@ SOURCES += \
|
|||||||
src/JSON/FrameInfo.cpp \
|
src/JSON/FrameInfo.cpp \
|
||||||
src/JSON/Generator.cpp \
|
src/JSON/Generator.cpp \
|
||||||
src/JSON/Group.cpp \
|
src/JSON/Group.cpp \
|
||||||
|
src/MQTT/Publisher.cpp \
|
||||||
src/Misc/ModuleManager.cpp \
|
src/Misc/ModuleManager.cpp \
|
||||||
src/Misc/TimerEvents.cpp \
|
src/Misc/TimerEvents.cpp \
|
||||||
src/Misc/Translator.cpp \
|
src/Misc/Translator.cpp \
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
<file>qml/Widgets/MapDelegate.qml</file>
|
<file>qml/Widgets/MapDelegate.qml</file>
|
||||||
<file>qml/Widgets/Window.qml</file>
|
<file>qml/Widgets/Window.qml</file>
|
||||||
<file>qml/Windows/About.qml</file>
|
<file>qml/Windows/About.qml</file>
|
||||||
<file>qml/Windows/Acknowledgements.qml</file>
|
<file>qml/Windows/Acknowledgements.qml</file>
|
||||||
<file>qml/Windows/Console.qml</file>
|
<file>qml/Windows/Console.qml</file>
|
||||||
<file>qml/Windows/CsvPlayer.qml</file>
|
<file>qml/Windows/CsvPlayer.qml</file>
|
||||||
<file>qml/Windows/DataGrid.qml</file>
|
<file>qml/Windows/DataGrid.qml</file>
|
||||||
@ -94,5 +94,6 @@
|
|||||||
<file>qml/PlatformDependent/MenubarMacOS.qml</file>
|
<file>qml/PlatformDependent/MenubarMacOS.qml</file>
|
||||||
<file>qml/PlatformDependent/Menubar.qml</file>
|
<file>qml/PlatformDependent/Menubar.qml</file>
|
||||||
<file>qml/PlatformDependent/DecentMenuItem.qml</file>
|
<file>qml/PlatformDependent/DecentMenuItem.qml</file>
|
||||||
|
<file>qml/SetupPanes/MQTTPublisher.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
269
assets/qml/SetupPanes/MQTTPublisher.qml
Normal file
269
assets/qml/SetupPanes/MQTTPublisher.qml
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/alex-spataru>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
|
Control {
|
||||||
|
id: root
|
||||||
|
implicitHeight: layout.implicitHeight + app.spacing * 2
|
||||||
|
|
||||||
|
//
|
||||||
|
// Aliases
|
||||||
|
//
|
||||||
|
property alias host: _host.text
|
||||||
|
property alias port: _port.text
|
||||||
|
property alias topic: _topic.text
|
||||||
|
property alias user: _user.text
|
||||||
|
property alias password: _password.text
|
||||||
|
property alias dnsAddress: _addrLookup.text
|
||||||
|
|
||||||
|
//
|
||||||
|
// Layout
|
||||||
|
//
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: app.spacing
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
columns: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
rowSpacing: app.spacing
|
||||||
|
columnSpacing: app.spacing
|
||||||
|
|
||||||
|
//
|
||||||
|
// MQTT version
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
text: qsTr("Version") + ":"
|
||||||
|
} ComboBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: Cpp_MQTT_Publisher.mqttVersions
|
||||||
|
currentIndex: Cpp_MQTT_Publisher.mqttVersion
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
if (Cpp_MQTT_Publisher.mqttVersion !== currentIndex)
|
||||||
|
Cpp_MQTT_Publisher.mqttVersion = currentIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Host
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
text: qsTr("Host") + ":"
|
||||||
|
} TextField {
|
||||||
|
id: _host
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: Cpp_MQTT_Publisher.host
|
||||||
|
placeholderText: Cpp_MQTT_Publisher.defaultHost
|
||||||
|
onTextChanged: {
|
||||||
|
if (Cpp_MQTT_Publisher.host !== text)
|
||||||
|
Cpp_MQTT_Publisher.host = text
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Port
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
text: qsTr("Port") + ":"
|
||||||
|
} TextField {
|
||||||
|
id: _port
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: Cpp_MQTT_Publisher.port
|
||||||
|
placeholderText: Cpp_MQTT_Publisher.defaultPort
|
||||||
|
onTextChanged: {
|
||||||
|
if (Cpp_MQTT_Publisher.port !== text)
|
||||||
|
Cpp_MQTT_Publisher.port = text
|
||||||
|
}
|
||||||
|
|
||||||
|
validator: IntValidator {
|
||||||
|
bottom: 0
|
||||||
|
top: 65535
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Topic
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
text: qsTr("Topic") + ":"
|
||||||
|
} TextField {
|
||||||
|
id: _topic
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: Cpp_MQTT_Publisher.topic
|
||||||
|
placeholderText: qsTr("MQTT topic")
|
||||||
|
onTextChanged: {
|
||||||
|
if (Cpp_MQTT_Publisher.topic !== text)
|
||||||
|
Cpp_MQTT_Publisher.topic = text
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Username
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
text: qsTr("User") + ":"
|
||||||
|
} TextField {
|
||||||
|
id: _user
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: Cpp_MQTT_Publisher.username
|
||||||
|
placeholderText: qsTr("MQTT username")
|
||||||
|
onTextChanged: {
|
||||||
|
if (Cpp_MQTT_Publisher.username !== text)
|
||||||
|
Cpp_MQTT_Publisher.username = text
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Password
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
text: qsTr("Password") + ":"
|
||||||
|
} TextField {
|
||||||
|
id: _password
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: Cpp_MQTT_Publisher.password
|
||||||
|
placeholderText: qsTr("MQTT password")
|
||||||
|
onTextChanged: {
|
||||||
|
if (Cpp_MQTT_Publisher.password !== text)
|
||||||
|
Cpp_MQTT_Publisher.password = text
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Spacer
|
||||||
|
//
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumHeight: app.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Address lookup
|
||||||
|
//
|
||||||
|
Label {
|
||||||
|
text: qsTr("DNS lookup") + ": "
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
} RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: app.spacing / 2
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: _addrLookup
|
||||||
|
Layout.fillWidth: true
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
onAccepted: Cpp_MQTT_Publisher.lookup(text)
|
||||||
|
placeholderText: qsTr("Enter address (e.g. google.com)")
|
||||||
|
enabled: !Cpp_MQTT_Publisher.isConnectedToHost &&
|
||||||
|
!Cpp_MQTT_Publisher.lookupActive
|
||||||
|
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
icon.color: palette.text
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
Layout.maximumWidth: height
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
icon.source: "qrc:/icons/search.svg"
|
||||||
|
onClicked: Cpp_MQTT_Publisher.lookup(_addrLookup.text)
|
||||||
|
enabled: _addrLookup.text.length > 0 &&
|
||||||
|
!Cpp_MQTT_Publisher.isConnectedToHost &&
|
||||||
|
!Cpp_MQTT_Publisher.lookupActive
|
||||||
|
|
||||||
|
Behavior on opacity {NumberAnimation{}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Spacer
|
||||||
|
//
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Connect/disconnect button
|
||||||
|
//
|
||||||
|
Button {
|
||||||
|
icon.width: 24
|
||||||
|
icon.height: 24
|
||||||
|
font.bold: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
icon.color: palette.buttonText
|
||||||
|
checked: Cpp_MQTT_Publisher.isConnectedToHost
|
||||||
|
onClicked: Cpp_MQTT_Publisher.toggleConnection()
|
||||||
|
palette.buttonText: checked ? "#d72d60" : "#2eed5c"
|
||||||
|
text: (checked ? qsTr("Disconnect") : qsTr("Connect")) + " "
|
||||||
|
icon.source: checked ? "qrc:/icons/disconnect.svg" : "qrc:/icons/connect.svg"
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Spacer
|
||||||
|
//
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -65,6 +65,16 @@ Control {
|
|||||||
property alias socketType: network.socketType
|
property alias socketType: network.socketType
|
||||||
property alias addressLookup: network.addressLookup
|
property alias addressLookup: network.addressLookup
|
||||||
|
|
||||||
|
//
|
||||||
|
// MQTT settings
|
||||||
|
//
|
||||||
|
property alias mqttHost: mqttPublisher.host
|
||||||
|
property alias mqttPort: mqttPublisher.port
|
||||||
|
property alias mqttUser: mqttPublisher.user
|
||||||
|
property alias mqttTopic: mqttPublisher.topic
|
||||||
|
property alias mqttPassword: mqttPublisher.password
|
||||||
|
property alias mqttDnsAddress: mqttPublisher.dnsAddress
|
||||||
|
|
||||||
//
|
//
|
||||||
// App settings
|
// App settings
|
||||||
//
|
//
|
||||||
@ -204,6 +214,12 @@ Control {
|
|||||||
width: implicitWidth + 2 * app.spacing
|
width: implicitWidth + 2 * app.spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("MQTT")
|
||||||
|
height: tab.height + 3
|
||||||
|
width: implicitWidth + 2 * app.spacing
|
||||||
|
}
|
||||||
|
|
||||||
TabButton {
|
TabButton {
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
height: tab.height + 3
|
height: tab.height + 3
|
||||||
@ -238,7 +254,14 @@ Control {
|
|||||||
stack.implicitHeight = network.implicitHeight
|
stack.implicitHeight = network.implicitHeight
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
|
stack.implicitHeight = mqttPublisher.implicitHeight
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
stack.implicitHeight = settings.implicitHeight
|
stack.implicitHeight = settings.implicitHeight
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
stack.implicitHeight = 0
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +281,14 @@ Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetupPanes.MQTTPublisher {
|
||||||
|
id: mqttPublisher
|
||||||
|
background: TextField {
|
||||||
|
enabled: false
|
||||||
|
palette.base: "#16232a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetupPanes.Settings {
|
SetupPanes.Settings {
|
||||||
id: settings
|
id: settings
|
||||||
background: TextField {
|
background: TextField {
|
||||||
|
@ -38,6 +38,48 @@
|
|||||||
using namespace IO;
|
using namespace IO;
|
||||||
static Console *INSTANCE = nullptr;
|
static Console *INSTANCE = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a hexdump of the given data
|
||||||
|
*/
|
||||||
|
static QString HexDump(const char *data, size_t size)
|
||||||
|
{
|
||||||
|
char str[4096] = "";
|
||||||
|
char ascii[17];
|
||||||
|
|
||||||
|
size_t i, j;
|
||||||
|
ascii[16] = '\0';
|
||||||
|
for (i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
sprintf(str + strlen(str), "%02X ", static_cast<quint8>(data[i]));
|
||||||
|
|
||||||
|
if (data[i] >= ' ' && data[i] <= '~')
|
||||||
|
ascii[i % 16] = data[i];
|
||||||
|
else
|
||||||
|
ascii[i % 16] = '.';
|
||||||
|
|
||||||
|
if ((i + 1) % 8 == 0 || i + 1 == size)
|
||||||
|
{
|
||||||
|
sprintf(str + strlen(str), " ");
|
||||||
|
if ((i + 1) % 16 == 0)
|
||||||
|
sprintf(str + strlen(str), "| %s \n", ascii);
|
||||||
|
|
||||||
|
else if (i + 1 == size)
|
||||||
|
{
|
||||||
|
ascii[(i + 1) % 16] = '\0';
|
||||||
|
|
||||||
|
if ((i + 1) % 16 <= 8)
|
||||||
|
sprintf(str + strlen(str), " ");
|
||||||
|
for (j = (i + 1) % 16; j < 16; ++j)
|
||||||
|
sprintf(str + strlen(str), " ");
|
||||||
|
|
||||||
|
sprintf(str + strlen(str), "| %s \n", ascii);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString(str);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor function
|
* Constructor function
|
||||||
*/
|
*/
|
||||||
@ -610,19 +652,18 @@ QString Console::plainTextStr(const QByteArray &data)
|
|||||||
*/
|
*/
|
||||||
QString Console::hexadecimalStr(const QByteArray &data)
|
QString Console::hexadecimalStr(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// Convert to hex string with spaces between bytes
|
// Convert data to string with dump every ~80 chars
|
||||||
QString str;
|
QString str;
|
||||||
QString hex = QString::fromUtf8(data.toHex());
|
const int characters = 80;
|
||||||
for (int i = 0; i < hex.length(); ++i)
|
for (int i = 0; i < data.length(); ++i)
|
||||||
{
|
{
|
||||||
str.append(hex.at(i));
|
QByteArray line;
|
||||||
if ((i + 1) % 2 == 0 && i > 0)
|
for (int j = 0; j < qMin(characters, data.length() - i); ++j)
|
||||||
str.append(" ");
|
line.append(data.at(i + j));
|
||||||
}
|
|
||||||
|
|
||||||
// Add new line & carriage returns
|
str.append(HexDump(line.data(), line.size()));
|
||||||
str.replace("0a", "0a\r");
|
str.append("\n");
|
||||||
str.replace("0d", "0d\n");
|
}
|
||||||
|
|
||||||
// Return string
|
// Return string
|
||||||
return str;
|
return str;
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) MATUSICA S.A. de C.V. - All Rights Reserved
|
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/alex-spataru>
|
||||||
*
|
*
|
||||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* Proprietary and confidential.
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* Written by Alex Spataru <https://alex-spataru.com/>, February 2021
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef IO_DATA_SOURCES_NETWORK_H
|
#ifndef IO_DATA_SOURCES_NETWORK_H
|
||||||
|
230
src/MQTT/Publisher.cpp
Normal file
230
src/MQTT/Publisher.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/alex-spataru>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Publisher.h"
|
||||||
|
|
||||||
|
#include <Misc/Utilities.h>
|
||||||
|
#include <Misc/TimerEvents.h>
|
||||||
|
|
||||||
|
using namespace MQTT;
|
||||||
|
|
||||||
|
static Publisher *INSTANCE = nullptr;
|
||||||
|
|
||||||
|
Publisher::Publisher()
|
||||||
|
{
|
||||||
|
m_lookupActive = false;
|
||||||
|
|
||||||
|
connect(&m_client, &QMQTT::Client::connected, this, &Publisher::connectedChanged);
|
||||||
|
connect(&m_client, &QMQTT::Client::disconnected, this, &Publisher::connectedChanged);
|
||||||
|
connect(&m_client, &QMQTT::Client::error, this, &Publisher::onError);
|
||||||
|
|
||||||
|
setPort(defaultPort());
|
||||||
|
setHost(defaultHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
Publisher::~Publisher()
|
||||||
|
{
|
||||||
|
disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
Publisher *Publisher::getInstance()
|
||||||
|
{
|
||||||
|
if (!INSTANCE)
|
||||||
|
INSTANCE = new Publisher;
|
||||||
|
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 Publisher::port() const
|
||||||
|
{
|
||||||
|
return m_client.port();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Publisher::topic() const
|
||||||
|
{
|
||||||
|
return m_topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Publisher::mqttVersion() const
|
||||||
|
{
|
||||||
|
switch (m_client.version())
|
||||||
|
{
|
||||||
|
case QMQTT::V3_1_0:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case QMQTT::V3_1_1:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Publisher::username() const
|
||||||
|
{
|
||||||
|
return m_client.username();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Publisher::password() const
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(m_client.password());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Publisher::host() const
|
||||||
|
{
|
||||||
|
return m_client.host().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Publisher::lookupActive() const
|
||||||
|
{
|
||||||
|
return m_lookupActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Publisher::isConnectedToHost() const
|
||||||
|
{
|
||||||
|
return m_client.isConnectedToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Publisher::mqttVersions() const
|
||||||
|
{
|
||||||
|
return QStringList { "MQTT 3.1.0", "MQTT 3.1.1" };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::connectToHost()
|
||||||
|
{
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects/disconnects the application from the current MQTT broker. This function is
|
||||||
|
* used as a convenience for the connect/disconnect button.
|
||||||
|
*/
|
||||||
|
void Publisher::toggleConnection()
|
||||||
|
{
|
||||||
|
if (isConnectedToHost())
|
||||||
|
disconnectFromHost();
|
||||||
|
else
|
||||||
|
connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::disconnectFromHost()
|
||||||
|
{
|
||||||
|
m_client.disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a DNS lookup for the given @a host name
|
||||||
|
*/
|
||||||
|
void Publisher::lookup(const QString &host)
|
||||||
|
{
|
||||||
|
m_lookupActive = true;
|
||||||
|
emit lookupActiveChanged();
|
||||||
|
QHostInfo::lookupHost(host.simplified(), this, &Publisher::lookupFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::setPort(const quint16 port)
|
||||||
|
{
|
||||||
|
m_client.setPort(port);
|
||||||
|
emit portChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::setHost(const QString &host)
|
||||||
|
{
|
||||||
|
m_client.setHost(QHostAddress(host));
|
||||||
|
emit hostChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::setTopic(const QString &topic)
|
||||||
|
{
|
||||||
|
m_topic = topic;
|
||||||
|
emit topicChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::setUsername(const QString &username)
|
||||||
|
{
|
||||||
|
m_client.setUsername(username);
|
||||||
|
emit usernameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
m_client.setPassword(password.toUtf8());
|
||||||
|
emit passwordChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::setMqttVersion(const int versionIndex)
|
||||||
|
{
|
||||||
|
switch (versionIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
m_client.setVersion(QMQTT::V3_1_0);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
m_client.setVersion(QMQTT::V3_1_1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit mqttVersionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::sendData()
|
||||||
|
{
|
||||||
|
// Sort JFI list from oldest to most recent
|
||||||
|
JFI_SortList(&m_jfiList);
|
||||||
|
|
||||||
|
// Clear JFI list
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the host IP address when the lookup finishes.
|
||||||
|
* If the lookup fails, the error code/string shall be shown to the user in a messagebox.
|
||||||
|
*/
|
||||||
|
void Publisher::lookupFinished(const QHostInfo &info)
|
||||||
|
{
|
||||||
|
m_lookupActive = false;
|
||||||
|
emit lookupActiveChanged();
|
||||||
|
|
||||||
|
if (info.error() == QHostInfo::NoError)
|
||||||
|
{
|
||||||
|
auto addresses = info.addresses();
|
||||||
|
if (addresses.count() >= 1)
|
||||||
|
{
|
||||||
|
setHost(addresses.first().toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Misc::Utilities::showMessageBox(tr("IP address lookup error"), info.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::onError(const QMQTT::ClientError error)
|
||||||
|
{
|
||||||
|
qDebug() << error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Publisher::registerJsonFrame(const JFI_Object &frameInfo)
|
||||||
|
{
|
||||||
|
m_jfiList.append(frameInfo);
|
||||||
|
}
|
140
src/MQTT/Publisher.h
Normal file
140
src/MQTT/Publisher.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/alex-spataru>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MQTT_PUBLISHER_H
|
||||||
|
#define MQTT_PUBLISHER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHostInfo>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
#include <JSON/Frame.h>
|
||||||
|
#include <JSON/FrameInfo.h>
|
||||||
|
|
||||||
|
#include <qmqtt.h>
|
||||||
|
|
||||||
|
namespace MQTT
|
||||||
|
{
|
||||||
|
class Publisher : public QObject
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(quint16 port
|
||||||
|
READ port
|
||||||
|
WRITE setPort
|
||||||
|
NOTIFY portChanged)
|
||||||
|
Q_PROPERTY(QString host
|
||||||
|
READ host
|
||||||
|
WRITE setHost
|
||||||
|
NOTIFY hostChanged)
|
||||||
|
Q_PROPERTY(QString topic
|
||||||
|
READ topic
|
||||||
|
WRITE setTopic
|
||||||
|
NOTIFY topicChanged)
|
||||||
|
Q_PROPERTY(int mqttVersion
|
||||||
|
READ mqttVersion
|
||||||
|
WRITE setMqttVersion
|
||||||
|
NOTIFY mqttVersionChanged)
|
||||||
|
Q_PROPERTY(QString username
|
||||||
|
READ username
|
||||||
|
WRITE setUsername
|
||||||
|
NOTIFY usernameChanged)
|
||||||
|
Q_PROPERTY(QString password
|
||||||
|
READ password
|
||||||
|
WRITE setPassword
|
||||||
|
NOTIFY passwordChanged)
|
||||||
|
Q_PROPERTY(bool isConnectedToHost
|
||||||
|
READ isConnectedToHost
|
||||||
|
NOTIFY connectedChanged)
|
||||||
|
Q_PROPERTY(bool lookupActive
|
||||||
|
READ lookupActive
|
||||||
|
NOTIFY lookupActiveChanged)
|
||||||
|
Q_PROPERTY(QStringList mqttVersions
|
||||||
|
READ mqttVersions
|
||||||
|
CONSTANT)
|
||||||
|
Q_PROPERTY(quint16 defaultPort
|
||||||
|
READ defaultPort
|
||||||
|
CONSTANT)
|
||||||
|
Q_PROPERTY(QString defaultHost
|
||||||
|
READ defaultHost
|
||||||
|
CONSTANT)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void portChanged();
|
||||||
|
void hostChanged();
|
||||||
|
void topicChanged();
|
||||||
|
void usernameChanged();
|
||||||
|
void passwordChanged();
|
||||||
|
void connectedChanged();
|
||||||
|
void mqttVersionChanged();
|
||||||
|
void lookupActiveChanged();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Publisher *getInstance();
|
||||||
|
|
||||||
|
quint16 port() const;
|
||||||
|
QString host() const;
|
||||||
|
QString topic() const;
|
||||||
|
int mqttVersion() const;
|
||||||
|
QString username() const;
|
||||||
|
QString password() const;
|
||||||
|
bool lookupActive() const;
|
||||||
|
bool isConnectedToHost() const;
|
||||||
|
QStringList mqttVersions() const;
|
||||||
|
|
||||||
|
quint16 defaultPort() const { return 1883; }
|
||||||
|
|
||||||
|
QString defaultHost() const { return "127.0.0.1"; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void connectToHost();
|
||||||
|
void toggleConnection();
|
||||||
|
void disconnectFromHost();
|
||||||
|
void lookup(const QString &host);
|
||||||
|
void setPort(const quint16 port);
|
||||||
|
void setHost(const QString &host);
|
||||||
|
void setTopic(const QString &topic);
|
||||||
|
void setUsername(const QString &username);
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
void setMqttVersion(const int versionIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Publisher();
|
||||||
|
~Publisher();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void sendData();
|
||||||
|
void lookupFinished(const QHostInfo &info);
|
||||||
|
void onError(const QMQTT::ClientError error);
|
||||||
|
void registerJsonFrame(const JFI_Object &frameInfo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_topic;
|
||||||
|
bool m_lookupActive;
|
||||||
|
QMQTT::Client m_client;
|
||||||
|
QList<JFI_Object> m_jfiList;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -48,6 +48,8 @@
|
|||||||
#include <Misc/TimerEvents.h>
|
#include <Misc/TimerEvents.h>
|
||||||
#include <Misc/ModuleManager.h>
|
#include <Misc/ModuleManager.h>
|
||||||
|
|
||||||
|
#include <MQTT/Publisher.h>
|
||||||
|
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
#include <FileAppender.h>
|
#include <FileAppender.h>
|
||||||
#include <QSimpleUpdater.h>
|
#include <QSimpleUpdater.h>
|
||||||
@ -148,6 +150,7 @@ void ModuleManager::initializeQmlInterface()
|
|||||||
auto ioNetwork = IO::DataSources::Network::getInstance();
|
auto ioNetwork = IO::DataSources::Network::getInstance();
|
||||||
auto jsonGenerator = JSON::Generator::getInstance();
|
auto jsonGenerator = JSON::Generator::getInstance();
|
||||||
auto utilities = Misc::Utilities::getInstance();
|
auto utilities = Misc::Utilities::getInstance();
|
||||||
|
auto mqttPublisher = MQTT::Publisher::getInstance();
|
||||||
LOG_INFO() << "Finished initializing C++ modules";
|
LOG_INFO() << "Finished initializing C++ modules";
|
||||||
|
|
||||||
// Retranslate the QML interface automagically
|
// Retranslate the QML interface automagically
|
||||||
@ -170,6 +173,7 @@ void ModuleManager::initializeQmlInterface()
|
|||||||
c->setContextProperty("Cpp_IO_Serial", ioSerial);
|
c->setContextProperty("Cpp_IO_Serial", ioSerial);
|
||||||
c->setContextProperty("Cpp_IO_Network", ioNetwork);
|
c->setContextProperty("Cpp_IO_Network", ioNetwork);
|
||||||
c->setContextProperty("Cpp_JSON_Generator", jsonGenerator);
|
c->setContextProperty("Cpp_JSON_Generator", jsonGenerator);
|
||||||
|
c->setContextProperty("Cpp_MQTT_Publisher", mqttPublisher);
|
||||||
|
|
||||||
// Register app info with QML
|
// Register app info with QML
|
||||||
c->setContextProperty("Cpp_AppName", qApp->applicationName());
|
c->setContextProperty("Cpp_AppName", qApp->applicationName());
|
||||||
@ -207,6 +211,7 @@ void ModuleManager::stopOperations()
|
|||||||
CSV::Player::getInstance()->closeFile();
|
CSV::Player::getInstance()->closeFile();
|
||||||
IO::Manager::getInstance()->disconnectDevice();
|
IO::Manager::getInstance()->disconnectDevice();
|
||||||
Misc::TimerEvents::getInstance()->stopTimers();
|
Misc::TimerEvents::getInstance()->stopTimers();
|
||||||
|
MQTT::Publisher::getInstance()->disconnectFromHost();
|
||||||
|
|
||||||
LOG_INFO() << "Application modules stopped";
|
LOG_INFO() << "Application modules stopped";
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,7 @@ void WidgetProvider::updateModels()
|
|||||||
|
|
||||||
// Check if frame is valid
|
// Check if frame is valid
|
||||||
if (!DataProvider::getInstance()->latestFrame()->isValid())
|
if (!DataProvider::getInstance()->latestFrame()->isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update groups
|
// Update groups
|
||||||
m_mapGroups = getWidgetGroup("map");
|
m_mapGroups = getWidgetGroup("map");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user