Implement initially flashed property and reqork update provider

pull/9/head
Simon Stürz 2020-10-24 11:08:41 +02:00
parent 09fdd20dbd
commit 7918d4de2d
9 changed files with 179 additions and 29 deletions

View File

@ -34,10 +34,16 @@
#include <QJsonParseError> #include <QJsonParseError>
/* /*
* [UpdateProvider]
* updateBinary=/usr/bin/maveo-zigbee-flasher Example update provider configuration, if initiallyFlashed is false, a factory reset will be performed in any case
* updateReleaseFile=/usr/share/maveo-zigbee-flasher/firmware/release.json
*/ [UpdateProvider]
updateCommand=/usr/bin/maveo-zigbee-flasher -s /dev/ttymxc2 -p 131 -r 132
updateReleaseFile=/usr/share/maveo-zigbee-flasher/firmware/release.json
factoryResetCommand=/usr/bin/maveo-zigbee-flasher -s /dev/ttymxc2 -p 131 -r 132 -e
initiallyFlashed=false
*/
FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(QObject *parent) : FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(QObject *parent) :
QObject(parent) QObject(parent)
@ -49,26 +55,68 @@ FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(const QFileInfo &updateProvid
QObject(parent), QObject(parent),
m_updateProviderConfgigurationFileInfo(updateProviderConfgigurationFileInfo) m_updateProviderConfgigurationFileInfo(updateProviderConfgigurationFileInfo)
{ {
qCDebug(dcZigbeeController()) << "Initialize NXP firmware update provider"; qCDebug(dcZigbeeController()) << "Initialize NXP firmware update provider from" << updateProviderConfgigurationFileInfo.absoluteFilePath();
QSettings configuration(updateProviderConfgigurationFileInfo.absoluteFilePath(), QSettings::IniFormat, this); QSettings configuration(updateProviderConfgigurationFileInfo.absoluteFilePath(), QSettings::IniFormat, this);
configuration.beginGroup("UpdateProvider"); configuration.beginGroup("UpdateProvider");
// Verify the update provider binary // Verify the update command
m_updateBinary = configuration.value("updateBinary").toString(); QString updateCommand = configuration.value("updateCommand").toString();
if (m_updateBinary.isEmpty()) { if (updateCommand.isEmpty()) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary is not specified in" << m_updateProviderConfgigurationFileInfo.absoluteFilePath(); qCWarning(dcZigbeeController()) << "Update provider configuration available but the update command is not specified in" << m_updateProviderConfgigurationFileInfo.absoluteFilePath();
return; return;
} }
QFileInfo updateBinaryFileInfo(m_updateBinary); QStringList updateCommandTokens = updateCommand.split(" ");
if (!updateBinaryFileInfo.exists()) { qCDebug(dcZigbeeController()) << "Update command tokens" << updateCommandTokens;
qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary does not exist" << updateBinaryFileInfo.absoluteFilePath(); if (updateCommandTokens.count() == 0) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the update command could not be parsed correctly" << m_updateProviderConfgigurationFileInfo.absoluteFilePath();
return; return;
} }
if (!updateBinaryFileInfo.isExecutable()) { m_updateProgram = updateCommandTokens.takeFirst();
qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary is not executable" << updateBinaryFileInfo.absoluteFilePath(); m_updateProgramParameters << updateCommandTokens;
qCDebug(dcZigbeeController()) << "Update program:" << m_updateProgram << "Parameters:" << m_updateProgramParameters;
QFileInfo updateProgramFileInfo(m_updateProgram);
if (!updateProgramFileInfo.exists()) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary does not exist" << updateProgramFileInfo.absoluteFilePath();
return;
}
if (!updateProgramFileInfo.isExecutable()) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary is not executable" << updateProgramFileInfo.absoluteFilePath();
return;
}
// Verify the factory reset command
QString factoryResetCommand = configuration.value("factoryResetCommand").toString();
if (factoryResetCommand.isEmpty()) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the factory reset command is not specified in" << m_updateProviderConfgigurationFileInfo.absoluteFilePath();
return;
}
QStringList factoryResetCommandTokens = factoryResetCommand.split(" ");
qCDebug(dcZigbeeController()) << "Factory reset command tokens" << factoryResetCommandTokens;
if (factoryResetCommandTokens.count() == 0) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the factory reset command could not be parsed correctly" << m_updateProviderConfgigurationFileInfo.absoluteFilePath();
return;
}
m_factoryResetProgram = factoryResetCommandTokens.takeFirst();
m_factoryResetParameters << factoryResetCommandTokens;
qCDebug(dcZigbeeController()) << "Factory reset program:" << m_factoryResetProgram << "Parameters:" << m_factoryResetParameters;
QFileInfo factoryResetProgramFileInfo(m_factoryResetProgram);
if (!factoryResetProgramFileInfo.exists()) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the factory reset binary does not exist" << factoryResetProgramFileInfo.absoluteFilePath();
return;
}
if (!factoryResetProgramFileInfo.isExecutable()) {
qCWarning(dcZigbeeController()) << "Update provider configuration available but the factory reset binary is not executable" << updateProgramFileInfo.absoluteFilePath();
return; return;
} }
@ -126,12 +174,18 @@ FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(const QFileInfo &updateProvid
return; return;
} }
qCDebug(dcZigbeeController()) << "Firmware update prvider available:" << firmwareFileInfo.absoluteFilePath() << "Version:" << m_availableFirmwareVersion; // Check if the controller has been initially flashed
m_initiallyFlashed = configuration.value("initiallyFlashed", false).toBool();
configuration.endGroup(); configuration.endGroup();
qCDebug(dcZigbeeController()) << "Firmware update provider available:" << firmwareFileInfo.fileName() << "Version:" << m_availableFirmwareVersion << "Initially flashed:" << m_initiallyFlashed;
// Set up update process
m_updateProcess = new QProcess(this); m_updateProcess = new QProcess(this);
m_updateProcess->setProcessChannelMode(QProcess::MergedChannels); m_updateProcess->setProcessChannelMode(QProcess::MergedChannels);
m_updateProcess->setProgram(m_updateBinary); m_updateProcess->setProgram(m_updateProgram);
m_updateProcess->setArguments(m_updateProgramParameters);
connect(m_updateProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus exitStatus) { connect(m_updateProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus exitStatus) {
qCDebug(dcZigbeeController()) << "Update process finihed" << exitCode << exitStatus; qCDebug(dcZigbeeController()) << "Update process finihed" << exitCode << exitStatus;
@ -143,12 +197,45 @@ FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(const QFileInfo &updateProvid
}); });
connect(m_updateProcess, &QProcess::readyRead, [=]() { connect(m_updateProcess, &QProcess::readyRead, [=]() {
qCDebug(dcZigbeeController()) << "Update process:" << qUtf8Printable(m_updateProcess->readAll().replace(0x1B, '\r')); qCDebug(dcZigbeeController()) << "Update process:" << qUtf8Printable(m_updateProcess->readAll());
});
// Set up factory reset process
m_factoryResetProcess = new QProcess(this);
m_factoryResetProcess->setProcessChannelMode(QProcess::MergedChannels);
m_factoryResetProcess->setProgram(m_factoryResetProgram);
m_factoryResetProcess->setArguments(m_factoryResetParameters);
connect(m_factoryResetProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus exitStatus) {
qCDebug(dcZigbeeController()) << "Factory reset process finihed" << exitCode << exitStatus;
if (exitCode != 0) {
emit updateFinished(false);
} else {
// The factory reset has been finished successfully
QSettings configuration(m_updateProviderConfgigurationFileInfo.absoluteFilePath(), QSettings::IniFormat, this);
configuration.beginGroup("UpdateProvider");
configuration.setValue("initiallyFlashed", true);
configuration.endGroup();
m_initiallyFlashed = true;
emit initiallyFlashedChanged(m_initiallyFlashed);
emit updateFinished(true);
}
});
connect(m_factoryResetProcess, &QProcess::readyRead, [=]() {
qCDebug(dcZigbeeController()) << "Factory reset process:" << qUtf8Printable(m_factoryResetProcess->readAll());
}); });
m_valid = true; m_valid = true;
} }
bool FirmwareUpdateHandlerNxp::initiallyFlashed() const
{
return m_initiallyFlashed;
}
bool FirmwareUpdateHandlerNxp::updateRunning() const bool FirmwareUpdateHandlerNxp::updateRunning() const
{ {
return m_updateProcess->state() != QProcess::NotRunning; return m_updateProcess->state() != QProcess::NotRunning;
@ -159,11 +246,6 @@ bool FirmwareUpdateHandlerNxp::isValid() const
return m_valid; return m_valid;
} }
QString FirmwareUpdateHandlerNxp::updateBinary() const
{
return m_updateBinary;
}
QString FirmwareUpdateHandlerNxp::availableFirmwareFileName() const QString FirmwareUpdateHandlerNxp::availableFirmwareFileName() const
{ {
return m_availableFirmwareFileName; return m_availableFirmwareFileName;
@ -186,3 +268,16 @@ void FirmwareUpdateHandlerNxp::startUpdate()
qCDebug(dcZigbeeController()) << "Firmware version:" << m_availableFirmwareVersion; qCDebug(dcZigbeeController()) << "Firmware version:" << m_availableFirmwareVersion;
m_updateProcess->start(); m_updateProcess->start();
} }
void FirmwareUpdateHandlerNxp::startFactoryReset()
{
if (!m_factoryResetProcess) {
qCWarning(dcZigbeeController()) << "Cannot start factory reset process. The update provider is not vaild.";
return;
}
qCDebug(dcZigbeeController()) << "Starting factory reset for NXP controller";
qCDebug(dcZigbeeController()) << "Firmware file:" << m_availableFirmwareFileName;
qCDebug(dcZigbeeController()) << "Firmware version:" << m_availableFirmwareVersion;
m_factoryResetProcess->start();
}

View File

@ -39,25 +39,36 @@ public:
explicit FirmwareUpdateHandlerNxp(QObject *parent = nullptr); explicit FirmwareUpdateHandlerNxp(QObject *parent = nullptr);
FirmwareUpdateHandlerNxp(const QFileInfo &updateProviderConfgigurationFileInfo, QObject *parent = nullptr); FirmwareUpdateHandlerNxp(const QFileInfo &updateProviderConfgigurationFileInfo, QObject *parent = nullptr);
bool initiallyFlashed() const;
bool updateRunning() const; bool updateRunning() const;
bool isValid() const; bool isValid() const;
QString updateBinary() const;
QString availableFirmwareFileName() const; QString availableFirmwareFileName() const;
QString availableFirmwareVersion() const; QString availableFirmwareVersion() const;
void startUpdate(); void startUpdate();
void startFactoryReset();
signals: signals:
void updateRunningChanged(bool updateRunning); void updateRunningChanged(bool updateRunning);
void updateFinished(bool success); void updateFinished(bool success);
void initiallyFlashedChanged(bool initiallyFlashed);
private: private:
QFileInfo m_updateProviderConfgigurationFileInfo; QFileInfo m_updateProviderConfgigurationFileInfo;
bool m_valid = false;
QProcess *m_updateProcess = nullptr;
QString m_updateBinary; bool m_initiallyFlashed = false;
bool m_valid = false;
QProcess *m_updateProcess = nullptr;
QProcess *m_factoryResetProcess = nullptr;
QString m_updateProgram;
QStringList m_updateProgramParameters;
QString m_factoryResetProgram;
QStringList m_factoryResetParameters;
QString m_updateReleaseFilePath; QString m_updateReleaseFilePath;
QString m_availableFirmwareFileName; QString m_availableFirmwareFileName;
QString m_availableFirmwareVersion; QString m_availableFirmwareVersion;

View File

@ -266,6 +266,20 @@ void ZigbeeBridgeControllerNxp::startFirmwareUpdate()
m_firmwareUpdateHandler->startUpdate(); m_firmwareUpdateHandler->startUpdate();
} }
void ZigbeeBridgeControllerNxp::startFactoryResetUpdate()
{
if (!m_firmwareUpdateHandler)
return;
m_updateRunning = true;
emit updateRunningChanged(m_updateRunning);
qCDebug(dcZigbeeController()) << "Disable UART interface for factory reset update...";
m_interface->disable();
m_firmwareUpdateHandler->startFactoryReset();
}
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent) ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent)
{ {
// Create the reply // Create the reply
@ -440,9 +454,16 @@ void ZigbeeBridgeControllerNxp::initializeUpdateProvider()
} }
}); });
connect(m_firmwareUpdateHandler, &FirmwareUpdateHandlerNxp::initiallyFlashedChanged, this, [this](bool initiallyFlashed){
qCDebug(dcZigbeeController()) << "Firmware initially flashed changed to" << initiallyFlashed;
m_initiallyFlashed = initiallyFlashed;
});
qCDebug(dcZigbeeController()) << "The firmware update provider is valid. The firmware of this NXP controller can be updated."; qCDebug(dcZigbeeController()) << "The firmware update provider is valid. The firmware of this NXP controller can be updated.";
m_canUpdate = true; m_canUpdate = true;
emit canUpdateChanged(m_canUpdate); emit canUpdateChanged(m_canUpdate);
m_initiallyFlashed = m_firmwareUpdateHandler->initiallyFlashed();
} }
void ZigbeeBridgeControllerNxp::onInterfaceAvailableChanged(bool available) void ZigbeeBridgeControllerNxp::onInterfaceAvailableChanged(bool available)

View File

@ -86,6 +86,7 @@ public:
bool updateAvailable(const QString &currentVersion) override; bool updateAvailable(const QString &currentVersion) override;
QString updateFirmwareVersion() const override; QString updateFirmwareVersion() const override;
void startFirmwareUpdate() override; void startFirmwareUpdate() override;
void startFactoryResetUpdate() override;
signals: signals:
void controllerStateChanged(ControllerState controllerState); void controllerStateChanged(ControllerState controllerState);

View File

@ -148,6 +148,7 @@ bool ZigbeeNetworkNxp::processVersionReply(ZigbeeInterfaceNxpReply *reply)
if (m_controller->canUpdate()) { if (m_controller->canUpdate()) {
qCDebug(dcZigbeeNetwork()) << "Unable to get controller version."; qCDebug(dcZigbeeNetwork()) << "Unable to get controller version.";
qCDebug(dcZigbeeNetwork()) << "Firmware update provider available. Try to flash the firmware, maybe that fixes the problem."; qCDebug(dcZigbeeNetwork()) << "Firmware update provider available. Try to flash the firmware, maybe that fixes the problem.";
// FIXME: try 3 times, then give up or perform a factory flash
if (!m_controller->updateRunning()) { if (!m_controller->updateRunning()) {
clearSettings(); clearSettings();
qCDebug(dcZigbeeNetwork()) << "Starting firmware update..."; qCDebug(dcZigbeeNetwork()) << "Starting firmware update...";
@ -249,6 +250,12 @@ void ZigbeeNetworkNxp::onControllerAvailableChanged(bool available)
{ {
qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more"); qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more");
if (available) { if (available) {
if (m_controller->canUpdate() && !m_controller->initiallyFlashed()) {
qCDebug(dcZigbeeNetwork()) << "The firmware of the controller can be updated and has not been initially flashed. Perform a factory reset flash procedure...";
m_controller->startFactoryResetUpdate();
return;
}
m_reconnectCounter = 0; m_reconnectCounter = 0;
ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion();
connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){

View File

@ -48,6 +48,11 @@ bool ZigbeeBridgeController::canUpdate() const
return m_canUpdate; return m_canUpdate;
} }
bool ZigbeeBridgeController::initiallyFlashed() const
{
return m_initiallyFlashed;
}
bool ZigbeeBridgeController::updateRunning() const bool ZigbeeBridgeController::updateRunning() const
{ {
return m_updateRunning; return m_updateRunning;
@ -69,9 +74,14 @@ void ZigbeeBridgeController::startFirmwareUpdate()
qCWarning(dcZigbeeController()) << "Cannot start firmware update. The feature is not implemented for this controller."; qCWarning(dcZigbeeController()) << "Cannot start firmware update. The feature is not implemented for this controller.";
} }
void ZigbeeBridgeController::startFactoryResetUpdate()
{
qCWarning(dcZigbeeController()) << "Cannot start firmware factory reset update. The feature is not implemented for this controller.";
}
void ZigbeeBridgeController::setSettingsDirectory(const QDir &settingsDirectory) void ZigbeeBridgeController::setSettingsDirectory(const QDir &settingsDirectory)
{ {
qCDebug(dcZigbeeController()) << "Using settings directory" << settingsDirectory.canonicalPath(); qCDebug(dcZigbeeController()) << "Using settings directory" << settingsDirectory.absolutePath();
m_settingsDirectory = settingsDirectory; m_settingsDirectory = settingsDirectory;
// Check if we have an update provider for the controller, if so, // Check if we have an update provider for the controller, if so,

View File

@ -49,16 +49,20 @@ public:
bool available() const; bool available() const;
bool canUpdate() const; bool canUpdate() const;
bool initiallyFlashed() const;
bool updateRunning() const; bool updateRunning() const;
// Optional update/initialize procedure for the zigbee controller
virtual bool updateAvailable(const QString &currentVersion); virtual bool updateAvailable(const QString &currentVersion);
virtual QString updateFirmwareVersion() const; virtual QString updateFirmwareVersion() const;
virtual void startFirmwareUpdate(); virtual void startFirmwareUpdate();
virtual void startFactoryResetUpdate();
protected: protected:
QString m_firmwareVersion; QString m_firmwareVersion;
bool m_available = false; bool m_available = false;
bool m_canUpdate = false; bool m_canUpdate = false;
bool m_initiallyFlashed = false;
bool m_updateRunning = false; bool m_updateRunning = false;
QDir m_settingsDirectory = QDir("/etc/nymea/"); QDir m_settingsDirectory = QDir("/etc/nymea/");

View File

@ -56,10 +56,11 @@ QString ZigbeeNetwork::settingsFilenName() const
void ZigbeeNetwork::setSettingsFileName(const QString &settingsFileName) void ZigbeeNetwork::setSettingsFileName(const QString &settingsFileName)
{ {
qCDebug(dcZigbeeNetwork()) << "Using settings file" << settingsFileName;
m_settingsFileName = settingsFileName; m_settingsFileName = settingsFileName;
emit settingsFileNameChanged(m_settingsFileName); emit settingsFileNameChanged(m_settingsFileName);
m_settingsDirectory = QFileInfo(m_settingsFileName).canonicalPath(); m_settingsDirectory = QDir(QFileInfo(m_settingsFileName).absolutePath());
bridgeController()->setSettingsDirectory(m_settingsDirectory); bridgeController()->setSettingsDirectory(m_settingsDirectory);
} }

View File

@ -136,7 +136,7 @@ private:
// Network storage // Network storage
QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf"; QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf";
QDir m_settingsDirectory; QDir m_settingsDirectory = QDir("/etc/nymea/");
QList<ZigbeeNode *> m_nodes; QList<ZigbeeNode *> m_nodes;
QList<ZigbeeNode *> m_uninitializedNodes; QList<ZigbeeNode *> m_uninitializedNodes;