From bfdaadfc8b19884f1f3368fd8a89208bab1d8d8e Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Mon, 6 Feb 2017 12:16:16 +0300 Subject: [PATCH] add socket activation for extensions (#118) Option `X-AW-Socket` is used. Any message received by this socket will trigger extension update. If the option is set, `X-AW-Interval` will be ignored --- sources/awesomewidgets/abstractextitem.cpp | 60 ++++++++++++++++++++ sources/awesomewidgets/abstractextitem.h | 13 +++++ sources/awesomewidgets/extitemaggregator.h | 9 +++ sources/awesomewidgets/extnetworkrequest.cpp | 27 ++++++--- sources/awesomewidgets/extnetworkrequest.h | 2 + sources/awesomewidgets/extquotes.cpp | 27 ++++++--- sources/awesomewidgets/extquotes.h | 2 + sources/awesomewidgets/extscript.cpp | 39 +++++++++---- sources/awesomewidgets/extscript.h | 2 + sources/awesomewidgets/extupgrade.cpp | 25 ++++++-- sources/awesomewidgets/extupgrade.h | 2 + sources/awesomewidgets/extweather.cpp | 27 ++++++--- sources/awesomewidgets/extweather.h | 2 + sources/extsysmonsources/customsource.cpp | 1 + sources/extsysmonsources/quotessource.cpp | 1 + sources/extsysmonsources/requestsource.cpp | 1 + sources/extsysmonsources/upgradesource.cpp | 1 + sources/extsysmonsources/weathersource.cpp | 1 + sources/test/testextquotes.cpp | 2 - 19 files changed, 204 insertions(+), 40 deletions(-) diff --git a/sources/awesomewidgets/abstractextitem.cpp b/sources/awesomewidgets/abstractextitem.cpp index 69a3445..9758f32 100644 --- a/sources/awesomewidgets/abstractextitem.cpp +++ b/sources/awesomewidgets/abstractextitem.cpp @@ -18,6 +18,7 @@ #include "abstractextitem.h" #include +#include #include #include #include @@ -41,6 +42,12 @@ AbstractExtItem::AbstractExtItem(QWidget *parent, const QString filePath) AbstractExtItem::~AbstractExtItem() { qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; + + if (m_socket) { + m_socket->close(); + m_socket->removeServer(socket()); + delete m_socket; + } } @@ -65,6 +72,7 @@ void AbstractExtItem::copyDefaults(AbstractExtItem *_other) const _other->setComment(comment()); _other->setInterval(interval()); _other->setName(name()); + _other->setSocket(socket()); } @@ -125,6 +133,12 @@ int AbstractExtItem::number() const } +QString AbstractExtItem::socket() const +{ + return m_socketFile; +} + + QString AbstractExtItem::tag(const QString _type) const { qCDebug(LOG_LIB) << "Tag type" << _type; @@ -196,6 +210,44 @@ void AbstractExtItem::setNumber(int _number) } +void AbstractExtItem::setSocket(const QString _socket) +{ + qCDebug(LOG_LIB) << "Socket" << _socket; + // remove old socket first + deinitSocket(); + + m_socketFile = _socket; + if (socket().isEmpty()) + return; +} + + +void AbstractExtItem::deinitSocket() +{ + if (!m_socket) + return; + + m_socket->close(); + m_socket->removeServer(socket()); + delete m_socket; + disconnect(m_socket, SIGNAL(newConnection()), this, + SLOT(newConnectionReceived())); +} + + +void AbstractExtItem::initSocket() +{ + // remove old socket first + deinitSocket(); + + m_socket = new QLocalServer(this); + bool listening = m_socket->listen(socket()); + qCInfo(LOG_LIB) << "Server listening on" << socket() << listening; + connect(m_socket, SIGNAL(newConnection()), this, + SLOT(newConnectionReceived())); +} + + void AbstractExtItem::readConfiguration() { QSettings settings(m_fileName, QSettings::IniFormat); @@ -210,6 +262,7 @@ void AbstractExtItem::readConfiguration() == QString("true")); setInterval(settings.value(QString("X-AW-Interval"), interval()).toInt()); setNumber(settings.value(QString("X-AW-Number"), number()).toInt()); + setSocket(settings.value(QString("X-AW-Socket"), socket()).toString()); settings.endGroup(); } @@ -236,7 +289,14 @@ void AbstractExtItem::writeConfiguration() const settings.setValue(QString("X-AW-Active"), QVariant(isActive()).toString()); settings.setValue(QString("X-AW-Interval"), interval()); settings.setValue(QString("X-AW-Number"), number()); + settings.setValue(QString("X-AW-Socket"), socket()); settings.endGroup(); settings.sync(); } + + +void AbstractExtItem::newConnectionReceived() +{ + emit(socketActivated()); +} diff --git a/sources/awesomewidgets/abstractextitem.h b/sources/awesomewidgets/abstractextitem.h index 7bdb3a1..5debcec 100644 --- a/sources/awesomewidgets/abstractextitem.h +++ b/sources/awesomewidgets/abstractextitem.h @@ -22,6 +22,8 @@ #include +class QLocalServer; + class AbstractExtItem : public QDialog { Q_OBJECT @@ -32,6 +34,7 @@ class AbstractExtItem : public QDialog Q_PROPERTY(int interval READ interval WRITE setInterval) Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(int number READ number WRITE setNumber) + Q_PROPERTY(QString socket READ socket WRITE setSocket) Q_PROPERTY(QString uniq READ uniq) public: @@ -51,6 +54,7 @@ public: bool isActive() const; QString name() const; int number() const; + QString socket() const; QString tag(const QString _type) const; virtual QString uniq() const = 0; // set methods @@ -60,17 +64,24 @@ public: void setInterval(const int _interval = 1); void setName(const QString _name = QString("none")); void setNumber(int _number = -1); + void setSocket(const QString _socket = QString("")); signals: void dataReceived(const QVariantHash &data); + void socketActivated(); public slots: + virtual void deinitSocket(); + virtual void initSocket(); virtual void readConfiguration(); virtual QVariantHash run() = 0; virtual int showConfiguration(const QVariant args = QVariant()) = 0; bool tryDelete() const; virtual void writeConfiguration() const; +private slots: + void newConnectionReceived(); + private: QString m_fileName = QString("/dev/null"); virtual void translate() = 0; @@ -81,6 +92,8 @@ private: int m_interval = 1; QString m_name = QString("none"); int m_number = -1; + QLocalServer *m_socket = nullptr; + QString m_socketFile = QString(""); }; diff --git a/sources/awesomewidgets/extitemaggregator.h b/sources/awesomewidgets/extitemaggregator.h index 78fe313..83e367f 100644 --- a/sources/awesomewidgets/extitemaggregator.h +++ b/sources/awesomewidgets/extitemaggregator.h @@ -61,6 +61,15 @@ public: qCInfo(LOG_LIB) << "Dialog returns" << ret; }; + void initSockets() + { + // HACK as soon as per one widget instance we have two objects each of + // them will try to control socket, whereas actually only one of them + // should be owner of the socket + for (auto item : m_items) + item->initSocket(); + } + T *itemByTag(const QString _tag, const QString _type) const { qCDebug(LOG_LIB) << "Tag" << _tag << "with used type" << _type; diff --git a/sources/awesomewidgets/extnetworkrequest.cpp b/sources/awesomewidgets/extnetworkrequest.cpp index ecc6afc..17a6b8e 100644 --- a/sources/awesomewidgets/extnetworkrequest.cpp +++ b/sources/awesomewidgets/extnetworkrequest.cpp @@ -49,6 +49,8 @@ ExtNetworkRequest::ExtNetworkRequest(QWidget *parent, const QString filePath) m_manager = new QNetworkAccessManager(nullptr); connect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(networkReplyReceived(QNetworkReply *))); + + connect(this, SIGNAL(socketActivated()), this, SLOT(sendRequest())); } @@ -58,6 +60,7 @@ ExtNetworkRequest::~ExtNetworkRequest() disconnect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(networkReplyReceived(QNetworkReply *))); + disconnect(this, SIGNAL(socketActivated()), this, SLOT(sendRequest())); m_manager->deleteLater(); delete ui; @@ -116,15 +119,11 @@ void ExtNetworkRequest::readConfiguration() QVariantHash ExtNetworkRequest::run() { - if ((!isActive()) || (m_isRunning)) + if (!canRun()) return m_values; - if (m_times == 1) { - qCInfo(LOG_LIB) << "Send request"; - m_isRunning = true; - QNetworkReply *reply = m_manager->get(QNetworkRequest(m_url)); - new QReplyTimeout(reply, REQUEST_TIMEOUT); - } + if (m_times == 1) + sendRequest(); // update value if (m_times >= interval()) @@ -193,6 +192,20 @@ void ExtNetworkRequest::networkReplyReceived(QNetworkReply *reply) } +void ExtNetworkRequest::sendRequest() +{ + m_isRunning = true; + QNetworkReply *reply = m_manager->get(QNetworkRequest(m_url)); + new QReplyTimeout(reply, REQUEST_TIMEOUT); +} + + +bool ExtNetworkRequest::canRun() const +{ + return ((isActive()) && (!m_isRunning) && (socket().isEmpty())); +} + + void ExtNetworkRequest::initUrl() { m_url = QUrl(m_stringUrl); diff --git a/sources/awesomewidgets/extnetworkrequest.h b/sources/awesomewidgets/extnetworkrequest.h index 78afdd0..c32ea5f 100644 --- a/sources/awesomewidgets/extnetworkrequest.h +++ b/sources/awesomewidgets/extnetworkrequest.h @@ -52,12 +52,14 @@ public slots: private slots: void networkReplyReceived(QNetworkReply *reply); + void sendRequest(); private: QNetworkAccessManager *m_manager = nullptr; QUrl m_url; bool m_isRunning = false; Ui::ExtNetworkRequest *ui = nullptr; + bool canRun() const; void initUrl(); void translate(); // properties diff --git a/sources/awesomewidgets/extquotes.cpp b/sources/awesomewidgets/extquotes.cpp index 045d8ab..799db3f 100644 --- a/sources/awesomewidgets/extquotes.cpp +++ b/sources/awesomewidgets/extquotes.cpp @@ -59,6 +59,8 @@ ExtQuotes::ExtQuotes(QWidget *parent, const QString filePath) m_manager = new QNetworkAccessManager(nullptr); connect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(quotesReplyReceived(QNetworkReply *))); + + connect(this, SIGNAL(socketActivated()), this, SLOT(sendRequest())); } @@ -68,6 +70,7 @@ ExtQuotes::~ExtQuotes() disconnect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(quotesReplyReceived(QNetworkReply *))); + disconnect(this, SIGNAL(socketActivated()), this, SLOT(sendRequest())); m_manager->deleteLater(); delete ui; @@ -125,15 +128,11 @@ void ExtQuotes::readConfiguration() QVariantHash ExtQuotes::run() { - if ((!isActive()) || (m_isRunning)) + if (!canRun()) return m_values; - if (m_times == 1) { - qCInfo(LOG_LIB) << "Send request"; - m_isRunning = true; - QNetworkReply *reply = m_manager->get(QNetworkRequest(m_url)); - new QReplyTimeout(reply, REQUEST_TIMEOUT); - } + if (m_times == 1) + sendRequest(); // update value if (m_times >= interval()) @@ -245,6 +244,20 @@ void ExtQuotes::quotesReplyReceived(QNetworkReply *reply) } +void ExtQuotes::sendRequest() +{ + m_isRunning = true; + QNetworkReply *reply = m_manager->get(QNetworkRequest(m_url)); + new QReplyTimeout(reply, REQUEST_TIMEOUT); +} + + +bool ExtQuotes::canRun() const +{ + return ((isActive()) && (!m_isRunning) && (socket().isEmpty())); +} + + void ExtQuotes::initUrl() { // init query diff --git a/sources/awesomewidgets/extquotes.h b/sources/awesomewidgets/extquotes.h index fdea6c5..d9561ac 100644 --- a/sources/awesomewidgets/extquotes.h +++ b/sources/awesomewidgets/extquotes.h @@ -55,12 +55,14 @@ public slots: private slots: void quotesReplyReceived(QNetworkReply *reply); + void sendRequest(); private: QNetworkAccessManager *m_manager = nullptr; QUrl m_url; bool m_isRunning = false; Ui::ExtQuotes *ui = nullptr; + bool canRun() const; void initUrl(); void translate(); // properties diff --git a/sources/awesomewidgets/extscript.cpp b/sources/awesomewidgets/extscript.cpp index fd79c4f..c1557ff 100644 --- a/sources/awesomewidgets/extscript.cpp +++ b/sources/awesomewidgets/extscript.cpp @@ -48,6 +48,8 @@ ExtScript::ExtScript(QWidget *parent, const QString filePath) connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(updateValue())); m_process->waitForFinished(0); + + connect(this, SIGNAL(socketActivated()), this, SLOT(startProcess())); } @@ -55,8 +57,11 @@ ExtScript::~ExtScript() { qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; + disconnect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(updateValue())); m_process->kill(); m_process->deleteLater(); + disconnect(this, SIGNAL(socketActivated()), this, SLOT(startProcess())); delete ui; } @@ -260,22 +265,14 @@ void ExtScript::readJsonFilters() } +#include QVariantHash ExtScript::run() { - if (!isActive()) + if (!canRun()) return m_values; - if (m_process->state() != QProcess::NotRunning) - qCWarning(LOG_LIB) << "Another process is already running" - << m_process->state(); - if ((m_times == 1) && (m_process->state() == QProcess::NotRunning)) { - QStringList cmdList; - if (!prefix().isEmpty()) - cmdList.append(prefix()); - cmdList.append(executable()); - qCInfo(LOG_LIB) << "Run cmd" << cmdList.join(QChar(' ')); - m_process->start(cmdList.join(QChar(' '))); - } + if (m_times == 1) + startProcess(); // update value if (m_times >= interval()) @@ -350,6 +347,17 @@ void ExtScript::writeConfiguration() const } +void ExtScript::startProcess() +{ + QStringList cmdList; + if (!prefix().isEmpty()) + cmdList.append(prefix()); + cmdList.append(executable()); + qCInfo(LOG_LIB) << "Run cmd" << cmdList.join(QChar(' ')); + m_process->start(cmdList.join(QChar(' '))); +} + + void ExtScript::updateValue() { qCInfo(LOG_LIB) << "Cmd returns" << m_process->exitCode(); @@ -383,6 +391,13 @@ void ExtScript::updateValue() } +bool ExtScript::canRun() const +{ + return ((isActive()) && (m_process->state() == QProcess::NotRunning) + && (socket().isEmpty())); +} + + void ExtScript::translate() { ui->label_name->setText(i18n("Name")); diff --git a/sources/awesomewidgets/extscript.h b/sources/awesomewidgets/extscript.h index e9dd192..79c6bcf 100644 --- a/sources/awesomewidgets/extscript.h +++ b/sources/awesomewidgets/extscript.h @@ -73,11 +73,13 @@ public slots: void writeConfiguration() const; private slots: + void startProcess(); void updateValue(); private: QProcess *m_process = nullptr; Ui::ExtScript *ui = nullptr; + bool canRun() const; void translate(); // properties QString m_executable = QString("/usr/bin/true"); diff --git a/sources/awesomewidgets/extupgrade.cpp b/sources/awesomewidgets/extupgrade.cpp index 8afa5f4..630dde5 100644 --- a/sources/awesomewidgets/extupgrade.cpp +++ b/sources/awesomewidgets/extupgrade.cpp @@ -44,6 +44,8 @@ ExtUpgrade::ExtUpgrade(QWidget *parent, const QString filePath) m_process = new QProcess(nullptr); connect(m_process, SIGNAL(finished(int)), this, SLOT(updateValue())); m_process->waitForFinished(0); + + connect(this, SIGNAL(socketActivated()), this, SLOT(startProcess())); } @@ -53,6 +55,7 @@ ExtUpgrade::~ExtUpgrade() m_process->kill(); m_process->deleteLater(); + disconnect(this, SIGNAL(socketActivated()), this, SLOT(startProcess())); delete ui; } @@ -145,11 +148,8 @@ QVariantHash ExtUpgrade::run() if (!isActive()) return m_values; - if ((m_times == 1) && (m_process->state() == QProcess::NotRunning)) { - QString cmd = QString("sh -c \"%1\"").arg(executable()); - qCInfo(LOG_LIB) << "Run cmd" << cmd; - m_process->start(cmd); - } + if (m_times == 1) + startProcess(); // update value if (m_times >= interval()) @@ -209,6 +209,14 @@ void ExtUpgrade::writeConfiguration() const } +void ExtUpgrade::startProcess() +{ + QString cmd = QString("sh -c \"%1\"").arg(executable()); + qCInfo(LOG_LIB) << "Run cmd" << cmd; + m_process->start(cmd); +} + + void ExtUpgrade::updateValue() { qCInfo(LOG_LIB) << "Cmd returns" << m_process->exitCode(); @@ -230,6 +238,13 @@ void ExtUpgrade::updateValue() } +bool ExtUpgrade::canRun() +{ + return ((isActive()) && (m_process->state() == QProcess::NotRunning) + && (socket().isEmpty())); +} + + void ExtUpgrade::translate() { ui->label_name->setText(i18n("Name")); diff --git a/sources/awesomewidgets/extupgrade.h b/sources/awesomewidgets/extupgrade.h index 673a4a4..0e27c60 100644 --- a/sources/awesomewidgets/extupgrade.h +++ b/sources/awesomewidgets/extupgrade.h @@ -56,11 +56,13 @@ public slots: void writeConfiguration() const; private slots: + void startProcess(); void updateValue(); private: QProcess *m_process = nullptr; Ui::ExtUpgrade *ui = nullptr; + bool canRun(); void translate(); // properties QString m_executable = QString("/usr/bin/true"); diff --git a/sources/awesomewidgets/extweather.cpp b/sources/awesomewidgets/extweather.cpp index 9f5c403..1692570 100644 --- a/sources/awesomewidgets/extweather.cpp +++ b/sources/awesomewidgets/extweather.cpp @@ -58,6 +58,8 @@ ExtWeather::ExtWeather(QWidget *parent, const QString filePath) m_manager = new QNetworkAccessManager(nullptr); connect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(weatherReplyReceived(QNetworkReply *))); + + connect(this, SIGNAL(socketActivated()), this, SLOT(sendRequest())); } @@ -67,6 +69,7 @@ ExtWeather::~ExtWeather() disconnect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(weatherReplyReceived(QNetworkReply *))); + disconnect(this, SIGNAL(socketActivated()), this, SLOT(sendRequest())); m_manager->deleteLater(); delete m_providerObject; @@ -262,13 +265,8 @@ QVariantHash ExtWeather::run() if ((!isActive()) || (m_isRunning)) return m_values; - if (m_times == 1) { - qCInfo(LOG_LIB) << "Send request"; - m_isRunning = true; - QNetworkReply *reply - = m_manager->get(QNetworkRequest(m_providerObject->url())); - new QReplyTimeout(reply, REQUEST_TIMEOUT); - } + if (m_times == 1) + sendRequest(); // update value if (m_times >= interval()) @@ -334,6 +332,15 @@ void ExtWeather::writeConfiguration() const } +void ExtWeather::sendRequest() +{ + m_isRunning = true; + QNetworkReply *reply + = m_manager->get(QNetworkRequest(m_providerObject->url())); + new QReplyTimeout(reply, REQUEST_TIMEOUT); +} + + void ExtWeather::weatherReplyReceived(QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { @@ -362,6 +369,12 @@ void ExtWeather::weatherReplyReceived(QNetworkReply *reply) } +bool ExtWeather::canRun() +{ + return ((isActive()) && (!m_isRunning) && (socket().isEmpty())); +} + + void ExtWeather::initProvider() { delete m_providerObject; diff --git a/sources/awesomewidgets/extweather.h b/sources/awesomewidgets/extweather.h index 3a775ce..ea479d2 100644 --- a/sources/awesomewidgets/extweather.h +++ b/sources/awesomewidgets/extweather.h @@ -70,6 +70,7 @@ public slots: void writeConfiguration() const; private slots: + void sendRequest(); void weatherReplyReceived(QNetworkReply *reply); private: @@ -77,6 +78,7 @@ private: AbstractWeatherProvider *m_providerObject = nullptr; bool m_isRunning = false; Ui::ExtWeather *ui = nullptr; + bool canRun(); void initProvider(); void translate(); // properties diff --git a/sources/extsysmonsources/customsource.cpp b/sources/extsysmonsources/customsource.cpp index 2180b14..abfe4a0 100644 --- a/sources/extsysmonsources/customsource.cpp +++ b/sources/extsysmonsources/customsource.cpp @@ -30,6 +30,7 @@ CustomSource::CustomSource(QObject *parent, const QStringList args) m_extScripts = new ExtItemAggregator(nullptr, QString("scripts")); + m_extScripts->initSockets(); m_sources = getSources(); } diff --git a/sources/extsysmonsources/quotessource.cpp b/sources/extsysmonsources/quotessource.cpp index 2bc8950..1e57c89 100644 --- a/sources/extsysmonsources/quotessource.cpp +++ b/sources/extsysmonsources/quotessource.cpp @@ -29,6 +29,7 @@ QuotesSource::QuotesSource(QObject *parent, const QStringList args) qCDebug(LOG_ESS) << __PRETTY_FUNCTION__; m_extQuotes = new ExtItemAggregator(nullptr, QString("quotes")); + m_extQuotes->initSockets(); m_sources = getSources(); } diff --git a/sources/extsysmonsources/requestsource.cpp b/sources/extsysmonsources/requestsource.cpp index b52d8ea..976b9c8 100644 --- a/sources/extsysmonsources/requestsource.cpp +++ b/sources/extsysmonsources/requestsource.cpp @@ -30,6 +30,7 @@ RequestSource::RequestSource(QObject *parent, const QStringList args) m_extNetRequest = new ExtItemAggregator( nullptr, QString("requests")); + m_extNetRequest->initSockets(); m_sources = getSources(); } diff --git a/sources/extsysmonsources/upgradesource.cpp b/sources/extsysmonsources/upgradesource.cpp index 5d7a3e2..a402ff6 100644 --- a/sources/extsysmonsources/upgradesource.cpp +++ b/sources/extsysmonsources/upgradesource.cpp @@ -30,6 +30,7 @@ UpgradeSource::UpgradeSource(QObject *parent, const QStringList args) m_extUpgrade = new ExtItemAggregator(nullptr, QString("upgrade")); + m_extUpgrade->initSockets(); m_sources = getSources(); } diff --git a/sources/extsysmonsources/weathersource.cpp b/sources/extsysmonsources/weathersource.cpp index 9806c2b..1abe824 100644 --- a/sources/extsysmonsources/weathersource.cpp +++ b/sources/extsysmonsources/weathersource.cpp @@ -30,6 +30,7 @@ WeatherSource::WeatherSource(QObject *parent, const QStringList args) m_extWeather = new ExtItemAggregator(nullptr, QString("weather")); + m_extWeather->initSockets(); m_sources = getSources(); } diff --git a/sources/test/testextquotes.cpp b/sources/test/testextquotes.cpp index 095f5d4..288bb28 100644 --- a/sources/test/testextquotes.cpp +++ b/sources/test/testextquotes.cpp @@ -68,7 +68,6 @@ void TestExtQuotes::test_run() QCOMPARE(firstValue[extQuotes->tag(QString("bid"))].toDouble(), 0.0); QCOMPARE(firstValue[extQuotes->tag(QString("price"))].toDouble(), 0.0); for (auto type : types) { - qDebug() << "Test type" << type; QVERIFY((cache[type].toDouble() > price.first) && (cache[type].toDouble() < price.second)); } @@ -93,7 +92,6 @@ void TestExtQuotes::test_derivatives() = arguments.at(0).toHash()[extQuotes->tag(QString("price"))]; for (auto type : types) { - qDebug() << "Test type" << type; QCOMPARE(arguments.at(0) .toHash()[extQuotes->tag(QString("%1chg").arg(type))] .toDouble(),