Add support for system upgrades

This commit is contained in:
Michael Zanetti 2019-04-08 02:04:37 +02:00
parent e28103312f
commit e0d14b8c92
14 changed files with 371 additions and 6 deletions

View File

@ -284,7 +284,6 @@ void NymeaConfiguration::getConfigurationsResponse(const QVariantMap &params)
webServerConfigurations()->clear();
foreach (const QVariant &webServerVariant, params.value("params").toMap().value("webServerConfigurations").toList()) {
QVariantMap webServerConfigMap = webServerVariant.toMap();
qDebug() << "**********+ web config" << webServerConfigMap;
WebServerConfiguration* config = new WebServerConfiguration(webServerConfigMap.value("id").toString(), QHostAddress(webServerConfigMap.value("address").toString()), webServerConfigMap.value("port").toInt(), webServerConfigMap.value("authenticationEnabled").toBool(), webServerConfigMap.value("sslEnabled").toBool());
config->setPublicFolder(webServerConfigMap.value("publicFolder").toString());
m_webServerConfigurations->addConfiguration(config);
@ -293,7 +292,7 @@ void NymeaConfiguration::getConfigurationsResponse(const QVariantMap &params)
void NymeaConfiguration::getAvailableLanguagesResponse(const QVariantMap &params)
{
qDebug() << "available languages" << params;
// qDebug() << "available languages" << params;
m_availableLanguages = params.value("params").toMap().value("languages").toStringList();
emit availableLanguagesChanged();
}

View File

@ -25,6 +25,7 @@
#include "tagsmanager.h"
#include "configuration/nymeaconfiguration.h"
#include "connection/awsclient.h"
#include "system/systemcontroller.h"
#include "connection/tcpsockettransport.h"
#include "connection/websockettransport.h"
@ -39,7 +40,8 @@ Engine::Engine(QObject *parent) :
m_ruleManager(new RuleManager(m_jsonRpcClient, this)),
m_logManager(new LogManager(m_jsonRpcClient, this)),
m_tagsManager(new TagsManager(m_jsonRpcClient, this)),
m_nymeaConfiguration(new NymeaConfiguration(m_jsonRpcClient, this))
m_nymeaConfiguration(new NymeaConfiguration(m_jsonRpcClient, this)),
m_systemController(new SystemController(m_jsonRpcClient, this))
{
m_connection->registerTransport(new TcpSocketTransportFactory());
m_connection->registerTransport(new WebsocketTransportFactory());
@ -97,6 +99,11 @@ NymeaConfiguration *Engine::nymeaConfiguration() const
return m_nymeaConfiguration;
}
SystemController *Engine::systemController() const
{
return m_systemController;
}
void Engine::deployCertificate()
{
if (!m_jsonRpcClient->connected()) {
@ -129,6 +136,7 @@ void Engine::onConnectedChanged()
m_deviceManager->init();
m_ruleManager->init();
m_nymeaConfiguration->init();
m_systemController->init();
}
}
}

View File

@ -32,6 +32,7 @@ class RuleManager;
class LogManager;
class TagsManager;
class NymeaConfiguration;
class SystemController;
class Engine : public QObject
{
@ -42,6 +43,7 @@ class Engine : public QObject
Q_PROPERTY(TagsManager* tagsManager READ tagsManager CONSTANT)
Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT)
Q_PROPERTY(NymeaConfiguration* nymeaConfiguration READ nymeaConfiguration CONSTANT)
Q_PROPERTY(SystemController* systemController READ systemController CONSTANT)
public:
explicit Engine(QObject *parent = nullptr);
@ -56,6 +58,7 @@ public:
JsonRpcClient *jsonRpcClient() const;
LogManager *logManager() const;
NymeaConfiguration *nymeaConfiguration() const;
SystemController *systemController() const;
Q_INVOKABLE void deployCertificate();
@ -67,6 +70,7 @@ private:
LogManager *m_logManager;
TagsManager *m_tagsManager;
NymeaConfiguration *m_nymeaConfiguration;
SystemController *m_systemController;
private slots:
void onConnectedChanged();

View File

@ -133,7 +133,7 @@ void JsonRpcClient::notificationReceived(const QVariantMap &data)
void JsonRpcClient::isCloudConnectedReply(const QVariantMap &data)
{
qDebug() << "Cloud is connected" << data;
// qDebug() << "Cloud is connected" << data;
QMetaEnum connectionStateEnum = QMetaEnum::fromType<CloudConnectionState>();
m_cloudConnectionState = static_cast<CloudConnectionState>(connectionStateEnum.keyToValue(data.value("params").toMap().value("connectionState").toByteArray().data()));
emit cloudConnectionStateChanged();

View File

@ -56,6 +56,7 @@
#include "ruletemplates/ruleactionparamtemplate.h"
#include "connection/awsclient.h"
#include "models/devicemodel.h"
#include "system/systemcontroller.h"
#include <QtQml/qqml.h>
@ -199,6 +200,8 @@ void registerQmlTypes() {
qmlRegisterUncreatableType<RuleActionTemplate>(uri, 1, 0, "RuleActionTemplate", "Get it from RuleActionTemplates");
qmlRegisterUncreatableType<RuleActionParamTemplates>(uri, 1, 0, "RuleActionParamTemplates", "Get it from RuleActionTemplate");
qmlRegisterUncreatableType<RuleActionParamTemplate>(uri, 1, 0, "RuleActionParamTemplate", "Get it from RuleActionParamTemplates");
qmlRegisterUncreatableType<SystemController>(uri, 1, 0, "SystemController", "Get it from Engine");
}
#endif // LIBNYMEAAPPCORE_H

View File

@ -81,7 +81,8 @@ SOURCES += \
configuration/nymeaconfiguration.cpp \
configuration/mqttpolicy.cpp \
configuration/mqttpolicies.cpp \
models/devicemodel.cpp
models/devicemodel.cpp \
system/systemcontroller.cpp
HEADERS += \
engine.h \
@ -142,4 +143,10 @@ HEADERS += \
configuration/nymeaconfiguration.h \
configuration/mqttpolicy.h \
configuration/mqttpolicies.h \
models/devicemodel.h
models/devicemodel.h \
system/systemcontroller.h
unix {
target.path = /usr/lib
INSTALLS += target
}

View File

@ -0,0 +1,129 @@
#include "systemcontroller.h"
SystemController::SystemController(JsonRpcClient *jsonRpcClient, QObject *parent):
JsonHandler(parent),
m_jsonRpcClient(jsonRpcClient)
{
m_jsonRpcClient->registerNotificationHandler(this, "notificationReceived");
}
void SystemController::init()
{
if (m_jsonRpcClient->ensureServerVersion("2.0")) {
m_jsonRpcClient->sendCommand("System.GetCapabilities", this, "getCapabilitiesResponse");
} else {
m_powerManagementAvailable = false;
}
}
QString SystemController::nameSpace() const
{
return "System";
}
bool SystemController::powerManagementAvailable() const
{
return m_powerManagementAvailable;
}
bool SystemController::updateManagementAvailable() const
{
return m_updateManagementAvailable;
}
bool SystemController::updateAvailable() const
{
return m_updateAvailable;
}
QString SystemController::currentVersion() const
{
return m_currentVersion;
}
QString SystemController::candidateVersion() const
{
return m_candidateVersion;
}
QStringList SystemController::availableChannels() const
{
return m_availableChannels;
}
QString SystemController::currentChannel() const
{
return m_currentChannel;
}
bool SystemController::updateInProgress() const
{
return m_updareInProgress;
}
void SystemController::startUpdate()
{
m_jsonRpcClient->sendCommand("System.StartUpdate");
}
void SystemController::selectChannel(const QString &channel)
{
QVariantMap params;
params.insert("channel", channel);
m_jsonRpcClient->sendCommand("System.SelectChannel", params, this, "selectChannelResponse");
}
void SystemController::reboot()
{
m_jsonRpcClient->sendCommand("System.Reboot");
}
void SystemController::shutdown()
{
m_jsonRpcClient->sendCommand("System.Shutdown");
}
void SystemController::getCapabilitiesResponse(const QVariantMap &data)
{
qDebug() << "capabilities received" << data;
m_powerManagementAvailable = data.value("params").toMap().value("powerManagement").toBool();
emit powerManagementAvailableChanged();
m_updateManagementAvailable = data.value("params").toMap().value("updateManagement").toBool();
emit updateManagementAvailableChanged();
if (m_updateManagementAvailable) {
m_jsonRpcClient->sendCommand("System.GetUpdateStatus", this, "getUpdateStatusResponse");
}
}
void SystemController::getUpdateStatusResponse(const QVariantMap &data)
{
qDebug() << "Update status:" << data;
m_currentVersion = data.value("params").toMap().value("currentVersion").toString();
m_candidateVersion = data.value("params").toMap().value("candidateVersion").toString();
m_availableChannels = data.value("params").toMap().value("availableChannels").toStringList();
m_currentChannel = data.value("params").toMap().value("currentChannel").toString();
m_updareInProgress = data.value("params").toMap().value("updateInProgress").toBool();
m_updateAvailable = data.value("params").toMap().value("updateAvailable").toBool();
emit updateStatusChanged();
}
void SystemController::selectChannelResponse(const QVariantMap &data)
{
qDebug() << "Select channel response" << data;
}
void SystemController::notificationReceived(const QVariantMap &data)
{
if (data.value("notification").toString() == "System.UpdateStatusChanged") {
qDebug() << "Update status changed:" << data;
m_currentVersion = data.value("params").toMap().value("currentVersion").toString();
m_candidateVersion = data.value("params").toMap().value("candidateVersion").toString();
m_availableChannels = data.value("params").toMap().value("availableChannels").toStringList();
m_currentChannel = data.value("params").toMap().value("currentChannel").toString();
m_updareInProgress = data.value("params").toMap().value("updateInProgress").toBool();
m_updateAvailable = data.value("params").toMap().value("updateAvailable").toBool();
emit updateStatusChanged();
}
}

View File

@ -0,0 +1,72 @@
#ifndef SYSTEMCONTROLLER_H
#define SYSTEMCONTROLLER_H
#include <QObject>
#include "jsonrpc/jsonrpcclient.h"
class SystemController : public JsonHandler
{
Q_OBJECT
Q_PROPERTY(bool powerManagementAvailable READ powerManagementAvailable NOTIFY powerManagementAvailableChanged)
// Whether the update mechanism is available in the connected core
Q_PROPERTY(bool updateManagementAvailable READ updateManagementAvailable NOTIFY updateManagementAvailableChanged)
// Whether there is an update available
Q_PROPERTY(bool updateAvailable READ updateAvailable NOTIFY updateStatusChanged)
Q_PROPERTY(QString currentVersion READ currentVersion NOTIFY updateStatusChanged)
Q_PROPERTY(QString candidateVersion READ candidateVersion NOTIFY updateStatusChanged)
Q_PROPERTY(QStringList availableChannels READ availableChannels NOTIFY updateStatusChanged)
Q_PROPERTY(QString currentChannel READ currentChannel NOTIFY updateStatusChanged)
Q_PROPERTY(bool updateInProgress READ updateInProgress NOTIFY updateStatusChanged)
public:
explicit SystemController(JsonRpcClient *jsonRpcClient, QObject *parent = nullptr);
void init();
QString nameSpace() const override;
bool powerManagementAvailable() const;
Q_INVOKABLE void reboot();
Q_INVOKABLE void shutdown();
bool updateManagementAvailable() const;
bool updateAvailable() const;
QString currentVersion() const;
QString candidateVersion() const;
QStringList availableChannels() const;
QString currentChannel() const;
bool updateInProgress() const;
Q_INVOKABLE void startUpdate();
Q_INVOKABLE void selectChannel(const QString &channel);
signals:
void powerManagementAvailableChanged();
void updateManagementAvailableChanged();
void updateStatusChanged();
private slots:
void getCapabilitiesResponse(const QVariantMap &data);
void getUpdateStatusResponse(const QVariantMap &data);
void selectChannelResponse(const QVariantMap &data);
void notificationReceived(const QVariantMap &data);
private:
JsonRpcClient *m_jsonRpcClient = nullptr;
bool m_powerManagementAvailable = false;
bool m_updateManagementAvailable = false;
bool m_updateAvailable = false;
QString m_currentVersion;
QString m_candidateVersion;
QStringList m_availableChannels;
QString m_currentChannel;
bool m_updareInProgress = false;
};
#endif // SYSTEMCONTROLLER_H

View File

@ -180,5 +180,6 @@
<file>ui/images/sensors/closable.svg</file>
<file>ui/images/lock-closed.svg</file>
<file>ui/images/lock-open.svg</file>
<file>ui/images/system-update.svg</file>
</qresource>
</RCC>

View File

@ -180,5 +180,6 @@
<file>ui/appsettings/LookAndFeelSettingsPage.qml</file>
<file>ui/appsettings/AppLogPage.qml</file>
<file>ui/magic/SelectStatePage.qml</file>
<file>ui/system/SystemUpdatePage.qml</file>
</qresource>
</RCC>

View File

@ -155,6 +155,22 @@ Page {
}
}
Pane {
Layout.fillWidth: true
Material.elevation: layout.isGrid ? 1 : 0
padding: 0
MeaListItemDelegate {
width: parent.width
iconName: "../images/system-update.svg"
text: qsTr("System update")
subText: qsTr("Update your %1:core system").arg(app.systemName)
prominentSubText: false
wrapTexts: false
onClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
}
}
Pane {
Layout.fillWidth: true
Material.elevation: layout.isGrid ? 1 : 0

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg4874" width="96" height="96" version="1.1" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata id="metadata4879">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" transform="translate(67.857 -78.505)">
<rect id="rect4782-35" transform="rotate(90)" x="78.505" y="-28.143" width="96" height="96" style="color:#000000;fill:none"/>
<path id="path4116-8" d="m-19.856 84.505c-23.173 0-42 18.827-42 41.999 0 23.172 18.827 41.999 42 41.999 23.173 0 42-18.827 42-41.999 0-23.172-18.827-41.999-42-41.999zm0 3.9984c21.01 0 38 16.99 38 38.001 0 21.011-16.99 38.001-38 38.001-21.01 0-38-16.99-38-38.001 0-21.011 16.99-38.001 38-38.001z" style="color-rendering:auto;color:#000000;fill:#808080;font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:none;font-variant-numeric:normal;font-variant-position:normal;image-rendering:auto;isolation:auto;mix-blend-mode:normal;shape-padding:0;shape-rendering:auto;solid-color:#000000;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-transform:none;white-space:normal"/>
<path id="ellipse4183" d="m-21.663 100.57c2.815-0.19766 5.6957 0.0631 8.5352 0.8239 11.358 3.0433 19.27 13.356 19.27 25.113h-4c0-9.9674-6.6798-18.67-16.307-21.249-9.6269-2.5795-19.761 1.618-24.744 10.25l-3.4648-1.9992c4.4092-7.6369 12.266-12.346 20.711-12.938z" style="color-rendering:auto;color:#000000;fill:#808080;font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:none;font-variant-numeric:normal;font-variant-position:normal;image-rendering:auto;isolation:auto;mix-blend-mode:normal;shape-padding:0;shape-rendering:auto;solid-color:#000000;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-transform:none;white-space:normal"/>
<path id="path4187" d="m-18.052 152.44c-2.815 0.19767-5.6957-0.0612-8.5352-0.82193-11.358-3.0433-19.27-13.356-19.27-25.113h4c0 9.9674 6.6798 18.67 16.307 21.249 9.6269 2.5795 19.761-1.6181 24.744-10.25l3.4668 1.9992c-4.4092 7.6369-12.268 12.344-20.713 12.936z" style="color-rendering:auto;color:#000000;fill:#808080;font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:none;font-variant-numeric:normal;font-variant-position:normal;image-rendering:auto;isolation:auto;mix-blend-mode:normal;shape-padding:0;shape-rendering:auto;solid-color:#000000;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-transform:none;white-space:normal"/>
<path id="path5918" d="m-6.5222 122.42 18.665-7.1843c-0.22375 3.2869-0.5836 6.6835-1.0752 10.19-0.50612 3.481-1.1013 6.8801-1.7886 10.196-2.6998-2.0116-5.4056-4.1394-8.1157-6.3818-2.7182-2.2723-5.2797-4.5458-7.686-6.8213z" style="color:#000000;fill:#808080"/>
<path id="path4471" d="m-33.192 130.59-18.665 7.1843c0.22375-3.2869 0.5836-6.6834 1.0752-10.19 0.50612-3.481 1.1013-6.8801 1.7886-10.196 2.6998 2.0116 5.4056 4.1394 8.1157 6.3818 2.7182 2.2723 5.2797 4.5458 7.686 6.8213z" style="color:#000000;fill:#808080"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -93,5 +93,22 @@ Page {
}
}
}
Button {
Layout.fillWidth: true
text: qsTr("Reboot %1:core").arg(app.systemName)
visible: engine.systemController.powerManagementAvailable
onClicked: {
engine.systemController.reboot()
}
}
Button {
Layout.fillWidth: true
text: qsTr("Shutdown %1:core").arg(app.systemName)
visible: engine.systemController.powerManagementAvailable
onClicked: {
engine.systemController.shutdown()
}
}
}
}

View File

@ -0,0 +1,87 @@
import QtQuick 2.8
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.3
import "../components"
import Nymea 1.0
Page {
id: root
header: GuhHeader {
text: qsTr("System update")
backButtonVisible: true
onBackPressed: pageStack.pop()
// HeaderButton {
// imageSource: "../images/configure.svg"
// color: pluginsProxy.showOnlyConfigurable ? app.accentColor : keyColor
// onClicked: {
// pluginsProxy.showOnlyConfigurable = !pluginsProxy.showOnlyConfigurable
// }
// }
}
ColumnLayout {
anchors { left: parent.left; top: parent.top; right: parent.right }
Label {
Layout.fillWidth: true
Layout.margins: app.margins
text: qsTr("Your %1 system is up to date.").arg(app.systemName)
visible: !engine.systemController.updateAvailable
}
MeaListItemDelegate {
Layout.fillWidth: true
progressive: false
text: qsTr("Installed version")
subText: engine.systemController.currentVersion
}
MeaListItemDelegate {
Layout.fillWidth: true
progressive: false
text: qsTr("Candidate version")
subText: engine.systemController.candidateVersion
visible: engine.systemController.updateAvailable
}
Button {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
text: qsTr("Update system")
visible: engine.systemController.updateAvailable
onClicked: {
engine.systemController.startUpdate()
}
}
ThinDivider {
visible: settings.showHiddenOptions
}
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
spacing: app.margins
visible: settings.showHiddenOptions
Label {
Layout.fillWidth: true
text: qsTr("Update channel")
}
ComboBox {
Layout.minimumWidth: 200
model: engine.systemController.availableChannels
currentIndex: model.indexOf(engine.systemController.currentChannel)
onActivated: {
engine.systemController.selectChannel(model[index])
}
}
}
}
BusyOverlay {
visible: engine.systemController.updateInProgress
}
}