Commit 8140aae0 authored by DonLakeFlyer's avatar DonLakeFlyer

parent 22a75d33
...@@ -219,56 +219,48 @@ void APMAirframeComponentController::loadParameters(const QString& paramFile) ...@@ -219,56 +219,48 @@ void APMAirframeComponentController::loadParameters(const QString& paramFile)
QString paramFileUrl = QStringLiteral("https://api.github.com/repos/ArduPilot/ardupilot/contents/Tools/Frame_params/%1?ref=master"); QString paramFileUrl = QStringLiteral("https://api.github.com/repos/ArduPilot/ardupilot/contents/Tools/Frame_params/%1?ref=master");
QGCFileDownload* downloader = new QGCFileDownload(this); QGCFileDownload* downloader = new QGCFileDownload(this);
connect(downloader, &QGCFileDownload::downloadFinished, this, &APMAirframeComponentController::_githubJsonDownloadFinished); connect(downloader, &QGCFileDownload::downloadComplete, this, &APMAirframeComponentController::_githubJsonDownloadComplete);
connect(downloader, &QGCFileDownload::error, this, &APMAirframeComponentController::_githubJsonDownloadError);
downloader->download(paramFileUrl.arg(paramFile)); downloader->download(paramFileUrl.arg(paramFile));
} }
void APMAirframeComponentController::_githubJsonDownloadFinished(QString remoteFile, QString localFile) void APMAirframeComponentController::_githubJsonDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
{ {
Q_UNUSED(remoteFile); if (errorMsg.isEmpty()) {
QFile jsonFile(localFile);
QFile jsonFile(localFile); if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "Unable to open github json file" << localFile << jsonFile.errorString();
qWarning() << "Unable to open github json file" << localFile << jsonFile.errorString(); qgcApp()->restoreOverrideCursor();
qgcApp()->restoreOverrideCursor(); return;
return; }
} QByteArray bytes = jsonFile.readAll();
QByteArray bytes = jsonFile.readAll(); jsonFile.close();
jsonFile.close();
QJsonParseError jsonParseError;
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Unable to open json document" << localFile << jsonParseError.errorString();
qgcApp()->restoreOverrideCursor();
return;
}
QJsonObject json = doc.object();
QJsonParseError jsonParseError; QGCFileDownload* downloader = new QGCFileDownload(this);
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError); connect(downloader, &QGCFileDownload::downloadComplete, this, &APMAirframeComponentController::_paramFileDownloadComplete);
if (jsonParseError.error != QJsonParseError::NoError) { downloader->download(json[QLatin1Literal("download_url")].toString());
qWarning() << "Unable to open json document" << localFile << jsonParseError.errorString(); } else {
qgcApp()->showAppMessage(tr("Param file github json download failed: %1").arg(errorMsg));
qgcApp()->restoreOverrideCursor(); qgcApp()->restoreOverrideCursor();
return;
} }
QJsonObject json = doc.object();
QGCFileDownload* downloader = new QGCFileDownload(this);
connect(downloader, &QGCFileDownload::downloadFinished, this, &APMAirframeComponentController::_paramFileDownloadFinished);
connect(downloader, &QGCFileDownload::error, this, &APMAirframeComponentController::_paramFileDownloadError);
downloader->download(json[QLatin1Literal("download_url")].toString());
}
void APMAirframeComponentController::_githubJsonDownloadError(QString errorMsg)
{
qgcApp()->showAppMessage(tr("Param file github json download failed: %1").arg(errorMsg));
qgcApp()->restoreOverrideCursor();
} }
void APMAirframeComponentController::_paramFileDownloadFinished(QString remoteFile, QString localFile) void APMAirframeComponentController::_paramFileDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
{ {
Q_UNUSED(remoteFile); if (errorMsg.isEmpty()) {
_loadParametersFromDownloadFile(localFile);
_loadParametersFromDownloadFile(localFile); } else {
} qgcApp()->showAppMessage(tr("Param file download failed: %1").arg(errorMsg));
qgcApp()->restoreOverrideCursor();
void APMAirframeComponentController::_paramFileDownloadError(QString errorMsg) }
{
qgcApp()->showAppMessage(tr("Param file download failed: %1").arg(errorMsg));
qgcApp()->restoreOverrideCursor();
} }
APMFrameClass::APMFrameClass(const QString& name, bool copter, int frameClass, Fact* frameTypeFact, QObject* parent) APMFrameClass::APMFrameClass(const QString& name, bool copter, int frameClass, Fact* frameTypeFact, QObject* parent)
......
...@@ -35,10 +35,8 @@ public: ...@@ -35,10 +35,8 @@ public:
Q_INVOKABLE void loadParameters(const QString& paramFile); Q_INVOKABLE void loadParameters(const QString& paramFile);
private slots: private slots:
void _githubJsonDownloadFinished(QString remoteFile, QString localFile); void _githubJsonDownloadComplete(QString remoteFile, QString localFile, QString errorMsg);
void _githubJsonDownloadError(QString errorMsg); void _paramFileDownloadComplete(QString remoteFile, QString localFile, QString errorMsg);
void _paramFileDownloadFinished(QString remoteFile, QString localFile);
void _paramFileDownloadError(QString errorMsg);
private: private:
void _fillFrameClasses(void); void _fillFrameClasses(void);
......
...@@ -831,20 +831,16 @@ void FirmwarePlugin::checkIfIsLatestStable(Vehicle* vehicle) ...@@ -831,20 +831,16 @@ void FirmwarePlugin::checkIfIsLatestStable(Vehicle* vehicle)
QGCFileDownload* downloader = new QGCFileDownload(this); QGCFileDownload* downloader = new QGCFileDownload(this);
connect( connect(
downloader, downloader,
&QGCFileDownload::downloadFinished, &QGCFileDownload::downloadComplete,
this, this,
[vehicle, this](QString remoteFile, QString localFile) { [vehicle, this](QString remoteFile, QString localFile, QString errorMsg) {
_versionFileDownloadFinished(remoteFile, localFile, vehicle); if (errorMsg.isEmpty()) {
_versionFileDownloadFinished(remoteFile, localFile, vehicle);
} else {
qCDebug(FirmwarePluginLog) << "Failed to download the latest fw version file. Error: " << errorMsg;
}
sender()->deleteLater(); sender()->deleteLater();
}); });
connect(
downloader,
&QGCFileDownload::error,
this,
[=](QString errorMsg) {
qCDebug(FirmwarePluginLog) << "Failed to download the latest fw version file. Error: " << errorMsg;
downloader->deleteLater();
});
downloader->download(versionFile); downloader->download(versionFile);
} }
......
...@@ -866,48 +866,46 @@ void QGCApplication::_checkForNewVersion() ...@@ -866,48 +866,46 @@ void QGCApplication::_checkForNewVersion()
if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) { if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) {
QString versionCheckFile = toolbox()->corePlugin()->stableVersionCheckFileUrl(); QString versionCheckFile = toolbox()->corePlugin()->stableVersionCheckFileUrl();
if (!versionCheckFile.isEmpty()) { if (!versionCheckFile.isEmpty()) {
_currentVersionDownload = new QGCFileDownload(this); QGCFileDownload* download = new QGCFileDownload(this);
connect(_currentVersionDownload, &QGCFileDownload::downloadFinished, this, &QGCApplication::_currentVersionDownloadFinished); connect(download, &QGCFileDownload::downloadComplete, this, &QGCApplication::_qgcCurrentStableVersionDownloadComplete);
connect(_currentVersionDownload, &QGCFileDownload::error, this, &QGCApplication::_currentVersionDownloadError); download->download(versionCheckFile);
_currentVersionDownload->download(versionCheckFile);
} }
} }
} }
#endif #endif
} }
void QGCApplication::_currentVersionDownloadFinished(QString /*remoteFile*/, QString localFile) void QGCApplication::_qgcCurrentStableVersionDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
{ {
#ifdef __mobile__ #ifdef __mobile__
Q_UNUSED(localFile); Q_UNUSED(localFile)
Q_UNUSED(errorMsg)
#else #else
QFile versionFile(localFile); if (errorMsg.isEmpty()) {
if (versionFile.open(QIODevice::ReadOnly)) { QFile versionFile(localFile);
QTextStream textStream(&versionFile); if (versionFile.open(QIODevice::ReadOnly)) {
QString version = textStream.readLine(); QTextStream textStream(&versionFile);
QString version = textStream.readLine();
qDebug() << version;
qDebug() << version;
int majorVersion, minorVersion, buildVersion;
if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) { int majorVersion, minorVersion, buildVersion;
if (_majorVersion < majorVersion || if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) {
(_majorVersion == majorVersion && _minorVersion < minorVersion) || if (_majorVersion < majorVersion ||
(_majorVersion == majorVersion && _minorVersion == minorVersion && _buildVersion < buildVersion)) { (_majorVersion == majorVersion && _minorVersion < minorVersion) ||
//-- TODO (_majorVersion == majorVersion && _minorVersion == minorVersion && _buildVersion < buildVersion)) {
///QGCMessageBox::information(tr("New Version Available"), tr("There is a newer version of %1 available. You can download it from %2.").arg(applicationName()).arg(toolbox()->corePlugin()->stableDownloadLocation())); showAppMessage(tr("There is a newer version of %1 available. You can download it from %2.").arg(applicationName()).arg(toolbox()->corePlugin()->stableDownloadLocation()), tr("New Version Available"));
}
} }
} }
} else {
qDebug() << "Download QGC stable version failed" << errorMsg;
} }
_currentVersionDownload->deleteLater(); sender()->deleteLater();
#endif #endif
} }
void QGCApplication::_currentVersionDownloadError(QString /*errorMsg*/)
{
_currentVersionDownload->deleteLater();
}
bool QGCApplication::_parseVersionText(const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion) bool QGCApplication::_parseVersionText(const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion)
{ {
QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)"); QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
class QQmlApplicationEngine; class QQmlApplicationEngine;
class QGCSingleton; class QGCSingleton;
class QGCToolbox; class QGCToolbox;
class QGCFileDownload;
/** /**
* @brief The main application and management class. * @brief The main application and management class.
...@@ -166,15 +165,14 @@ public: ...@@ -166,15 +165,14 @@ public:
bool _checkTelemetrySavePath(bool useMessageBox); bool _checkTelemetrySavePath(bool useMessageBox);
private slots: private slots:
void _missingParamsDisplay (void); void _missingParamsDisplay (void);
void _currentVersionDownloadFinished(QString remoteFile, QString localFile); void _qgcCurrentStableVersionDownloadComplete (QString remoteFile, QString localFile, QString errorMsg);
void _currentVersionDownloadError (QString errorMsg); bool _parseVersionText (const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion);
bool _parseVersionText (const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion); void _onGPSConnect (void);
void _onGPSConnect (void); void _onGPSDisconnect (void);
void _onGPSDisconnect (void); void _gpsSurveyInStatus (float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active);
void _gpsSurveyInStatus (float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active); void _gpsNumSatellites (int numSatellites);
void _gpsNumSatellites (int numSatellites); void _showDelayedAppMessages (void);
void _showDelayedAppMessages (void);
private: private:
QObject* _rootQmlObject (); QObject* _rootQmlObject ();
...@@ -194,7 +192,6 @@ private: ...@@ -194,7 +192,6 @@ private:
int _majorVersion = 0; int _majorVersion = 0;
int _minorVersion = 0; int _minorVersion = 0;
int _buildVersion = 0; int _buildVersion = 0;
QGCFileDownload* _currentVersionDownload = nullptr;
GPSRTKFactGroup* _gpsRtkFactGroup = nullptr; GPSRTKFactGroup* _gpsRtkFactGroup = nullptr;
QGCToolbox* _toolbox = nullptr; QGCToolbox* _toolbox = nullptr;
QQuickItem* _mainRootWindow = nullptr; QQuickItem* _mainRootWindow = nullptr;
......
...@@ -31,29 +31,6 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect) ...@@ -31,29 +31,6 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect)
return false; return false;
} }
// Split out filename from path
QString remoteFileName = QFileInfo(remoteFile).fileName();
if (remoteFileName.isEmpty()) {
qWarning() << "Unabled to parse filename from downloadFile" << remoteFile;
return false;
}
// Strip out parameters from remote filename
int parameterIndex = remoteFileName.indexOf("?");
if (parameterIndex != -1) {
remoteFileName = remoteFileName.left(parameterIndex);
}
// Determine location to download file to
QString localFile = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
if (localFile.isEmpty()) {
localFile = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
if (localFile.isEmpty()) {
qDebug() << "Unabled to find writable download location. Tried downloads and temp directory.";
return false;
}
}
localFile += "/" + remoteFileName;
QUrl remoteUrl; QUrl remoteUrl;
if (remoteFile.startsWith("http:") || remoteFile.startsWith("https:")) { if (remoteFile.startsWith("http:") || remoteFile.startsWith("https:")) {
...@@ -72,9 +49,6 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect) ...@@ -72,9 +49,6 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect)
tProxy.setType(QNetworkProxy::DefaultProxy); tProxy.setType(QNetworkProxy::DefaultProxy);
setProxy(tProxy); setProxy(tProxy);
// Store local file location in user attribute so we can retrieve when the download finishes
networkRequest.setAttribute(QNetworkRequest::User, localFile);
QNetworkReply* networkReply = get(networkRequest); QNetworkReply* networkReply = get(networkRequest);
if (!networkReply) { if (!networkReply) {
qWarning() << "QNetworkAccessManager::get failed"; qWarning() << "QNetworkAccessManager::get failed";
...@@ -84,8 +58,7 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect) ...@@ -84,8 +58,7 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect)
connect(networkReply, &QNetworkReply::downloadProgress, this, &QGCFileDownload::downloadProgress); connect(networkReply, &QNetworkReply::downloadProgress, this, &QGCFileDownload::downloadProgress);
connect(networkReply, &QNetworkReply::finished, this, &QGCFileDownload::_downloadFinished); connect(networkReply, &QNetworkReply::finished, this, &QGCFileDownload::_downloadFinished);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(networkReply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), connect(networkReply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), this, &QGCFileDownload::_downloadError);
this, &QGCFileDownload::_downloadError);
#else #else
connect(networkReply, &QNetworkReply::errorOccurred, this, &QGCFileDownload::_downloadError); connect(networkReply, &QNetworkReply::errorOccurred, this, &QGCFileDownload::_downloadError);
#endif #endif
...@@ -95,10 +68,10 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect) ...@@ -95,10 +68,10 @@ bool QGCFileDownload::download(const QString& remoteFile, bool redirect)
void QGCFileDownload::_downloadFinished(void) void QGCFileDownload::_downloadFinished(void)
{ {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender()); QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
// When an error occurs or the user cancels the download, we still end up here. So bail out in // When an error occurs or the user cancels the download, we still end up here. So bail out in
// those cases. // those cases.
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
reply->deleteLater(); reply->deleteLater();
return; return;
} }
...@@ -112,25 +85,46 @@ void QGCFileDownload::_downloadFinished(void) ...@@ -112,25 +85,46 @@ void QGCFileDownload::_downloadFinished(void)
return; return;
} }
// Download file location is in user attribute // Split out filename from path
QString downloadFilename = reply->request().attribute(QNetworkRequest::User).toString(); QString remoteFileName = QFileInfo(reply->url().toString()).fileName();
if (remoteFileName.isEmpty()) {
qWarning() << "Unabled to parse filename from remote url" << reply->url().toString();
remoteFileName = "DownloadedFile";
}
// Strip out http parameters from remote filename
int parameterIndex = remoteFileName.indexOf("?");
if (parameterIndex != -1) {
remoteFileName = remoteFileName.left(parameterIndex);
}
// Determine location to download file to
QString downloadFilename = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
if (downloadFilename.isEmpty()) {
downloadFilename = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
if (downloadFilename.isEmpty()) {
emit downloadComplete(_originalRemoteFile, QString(), tr("Unabled to find writable download location. Tried downloads and temp directory."));
return;
}
}
downloadFilename += "/" + remoteFileName;
if (!downloadFilename.isEmpty()) { if (!downloadFilename.isEmpty()) {
// Store downloaded file in download location // Store downloaded file in download location
QFile file(downloadFilename); QFile file(downloadFilename);
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
emit error(tr("Could not save downloaded file to %1. Error: %2").arg(downloadFilename).arg(file.errorString())); emit downloadComplete(_originalRemoteFile, downloadFilename, tr("Could not save downloaded file to %1. Error: %2").arg(downloadFilename).arg(file.errorString()));
return; return;
} }
file.write(reply->readAll()); file.write(reply->readAll());
file.close(); file.close();
emit downloadFinished(_originalRemoteFile, downloadFilename); emit downloadComplete(_originalRemoteFile, downloadFilename, QString());
} else { } else {
QString errorMsg = "Internal error"; QString errorMsg = "Internal error";
qWarning() << errorMsg; qWarning() << errorMsg;
emit error(errorMsg); emit downloadComplete(_originalRemoteFile, downloadFilename, errorMsg);
} }
reply->deleteLater(); reply->deleteLater();
...@@ -151,5 +145,5 @@ void QGCFileDownload::_downloadError(QNetworkReply::NetworkError code) ...@@ -151,5 +145,5 @@ void QGCFileDownload::_downloadError(QNetworkReply::NetworkError code)
errorMsg = tr("Error during download. Error: %1").arg(code); errorMsg = tr("Error during download. Error: %1").arg(code);
} }
emit error(errorMsg); emit downloadComplete(_originalRemoteFile, QString(), errorMsg);
} }
...@@ -7,9 +7,7 @@ ...@@ -7,9 +7,7 @@
* *
****************************************************************************/ ****************************************************************************/
#pragma once
#ifndef QGCFileDownload_H
#define QGCFileDownload_H
#include <QNetworkReply> #include <QNetworkReply>
...@@ -21,15 +19,14 @@ public: ...@@ -21,15 +19,14 @@ public:
QGCFileDownload(QObject* parent = nullptr); QGCFileDownload(QObject* parent = nullptr);
/// Download the specified remote file. /// Download the specified remote file.
/// @param remoteFile File to download. Can be http address or file system path. /// @param remoteFile File to download. Can be http address or file system path.
/// @param redirect true: call is internal due to redirect /// @param redirect true: call is internal due to redirect
/// @return true: Asynchronous download has started, false: Download initialization failed /// @return true: Asynchronous download has started, false: Download initialization failed
bool download(const QString& remoteFile, bool redirect = false); bool download(const QString& remoteFile, bool redirect = false);
signals: signals:
void downloadProgress(qint64 curr, qint64 total); void downloadProgress(qint64 curr, qint64 total);
void downloadFinished(QString remoteFile, QString localFile); void downloadComplete(QString remoteFile, QString localFile, QString errorMsg);
void error(QString errorMsg);
private: private:
void _downloadFinished(void); void _downloadFinished(void);
...@@ -37,5 +34,3 @@ private: ...@@ -37,5 +34,3 @@ private:
QString _originalRemoteFile; QString _originalRemoteFile;
}; };
#endif
...@@ -169,30 +169,27 @@ signals: ...@@ -169,30 +169,27 @@ signals:
void downloadingFirmwareListChanged (bool downloadingFirmwareList); void downloadingFirmwareListChanged (bool downloadingFirmwareList);
private slots: private slots:
void _firmwareDownloadProgress(qint64 curr, qint64 total); void _firmwareDownloadProgress (qint64 curr, qint64 total);
void _firmwareDownloadFinished(QString remoteFile, QString localFile); void _firmwareDownloadComplete (QString remoteFile, QString localFile, QString errorMsg);
void _firmwareDownloadError(QString errorMsg); void _foundBoard (bool firstAttempt, const QSerialPortInfo& portInfo, int boardType, QString boardName);
void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int boardType, QString boardName); void _noBoardFound (void);
void _noBoardFound(void); void _boardGone (void);
void _boardGone(); void _foundBoardInfo (int bootloaderVersion, int boardID, int flashSize);
void _foundBoardInfo(int bootloaderVersion, int boardID, int flashSize); void _error (const QString& errorString);
void _error(const QString& errorString); void _status (const QString& statusString);
void _status(const QString& statusString); void _bootloaderSyncFailed (void);
void _bootloaderSyncFailed(void); void _flashComplete (void);
void _flashComplete(void); void _updateProgress (int curr, int total);
void _updateProgress(int curr, int total); void _eraseStarted (void);
void _eraseStarted(void); void _eraseComplete (void);
void _eraseComplete(void); void _eraseProgressTick (void);
void _eraseProgressTick(void); void _px4ReleasesGithubDownloadComplete (QString remoteFile, QString localFile, QString errorMsg);
void _px4ReleasesGithubDownloadFinished(QString remoteFile, QString localFile); void _ardupilotManifestDownloadComplete (QString remoteFile, QString localFile, QString errorMsg);
void _px4ReleasesGithubDownloadError(QString errorMsg); void _buildAPMFirmwareNames (void);
void _ardupilotManifestDownloadFinished(QString remoteFile, QString localFile);
void _ardupilotManifestDownloadError(QString errorMsg);
void _buildAPMFirmwareNames(void);
private: private:
QHash<FirmwareIdentifier, QString>* _firmwareHashForBoardId(int boardId); QHash<FirmwareIdentifier, QString>* _firmwareHashForBoardId(int boardId);
void _getFirmwareFile(FirmwareIdentifier firmwareId); void _getFirmwareFile (FirmwareIdentifier firmwareId);
void _initFirmwareHash (void); void _initFirmwareHash (void);
void _downloadFirmware (void); void _downloadFirmware (void);
void _appendStatusLog (const QString& text, bool critical = false); void _appendStatusLog (const QString& text, bool critical = false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment