From e8901a4b2ab9318abaa0833540beba6ed589cc45 Mon Sep 17 00:00:00 2001 From: "moneromooo.monero" Date: Wed, 28 Dec 2016 21:20:05 +0000 Subject: [PATCH] Add pool mining screen Wa want to make it easy for people to mine --- LeftPanel.qml | 36 ++-- MiddlePanel.qml | 3 +- components/PoolMinerManagerDialog.qml | 138 +++++++++++++ main.cpp | 7 + main.qml | 37 +++- monero-wallet-gui.pro | 2 + pages/Mining.qml | 266 +++++++++++++++++++++++++- qml.qrc | 1 + src/poolminer/PoolMinerManager.cpp | 156 +++++++++++++++ src/poolminer/PoolMinerManager.h | 44 +++++ 10 files changed, 667 insertions(+), 23 deletions(-) create mode 100644 components/PoolMinerManagerDialog.qml create mode 100644 src/poolminer/PoolMinerManager.cpp create mode 100644 src/poolminer/PoolMinerManager.h diff --git a/LeftPanel.qml b/LeftPanel.qml index d487105934..1b6de44f3c 100644 --- a/LeftPanel.qml +++ b/LeftPanel.qml @@ -316,50 +316,50 @@ Rectangle { height: 1 } - /* // ------------- Mining tab --------------- + // ------------- Advanced tab --------------- MenuButton { - id: miningButton + id: advancedButton anchors.left: parent.left anchors.right: parent.right - text: qsTr("Mining") + translationManager.emptyString - symbol: qsTr("M") + translationManager.emptyString - dotColor: "#FFD781" + text: qsTr("Advanced") + translationManager.emptyString + symbol: qsTr("A") + translationManager.emptyString + dotColor: "#AAFFBB" onClicked: { parent.previousButton.checked = false - parent.previousButton = miningButton - panel.miningClicked() + parent.previousButton = advancedButton } } - Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.leftMargin: 16 - color: miningButton.checked || settingsButton.checked ? "#1C1C1C" : "#505050" + color: "#505050" height: 1 } - */ - // ------------- Advanced tab --------------- + + // ------------- Mining tab --------------- MenuButton { - id: advancedButton + id: miningButton anchors.left: parent.left anchors.right: parent.right - text: qsTr("Advanced") + translationManager.emptyString - symbol: qsTr("A") + translationManager.emptyString - dotColor: "#AAFFBB" + text: qsTr("Pool mining") + translationManager.emptyString + symbol: qsTr("M") + translationManager.emptyString + dotColor: "#FFD781" + under: advancedButton onClicked: { parent.previousButton.checked = false - parent.previousButton = advancedButton + parent.previousButton = miningButton + panel.miningClicked() } } + Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.leftMargin: 16 - color: "#505050" + color: miningButton.checked || settingsButton.checked ? "#1C1C1C" : "#505050" height: 1 } - // ------------- TxKey tab --------------- MenuButton { id: txkeyButton diff --git a/MiddlePanel.qml b/MiddlePanel.qml index b21da0c0ec..f8e7c8ad8b 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -51,6 +51,7 @@ Rectangle { property History historyView: History { } property Sign signView: Sign { } property Settings settingsView: Settings { } + property Mining miningView: Mining { } property AddressBook addressBookView: AddressBook { } @@ -143,7 +144,7 @@ Rectangle { PropertyChanges { target: root; currentView: settingsView } }, State { name: "Mining" - PropertyChanges { /*TODO*/ } + PropertyChanges { target: root; currentView: miningView } } ] diff --git a/components/PoolMinerManagerDialog.qml b/components/PoolMinerManagerDialog.qml new file mode 100644 index 0000000000..ab48d91339 --- /dev/null +++ b/components/PoolMinerManagerDialog.qml @@ -0,0 +1,138 @@ +// Copyright (c) 2014-2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Window 2.0 + +import "../components" as MoneroComponents + +Window { + id: root + modality: Qt.ApplicationModal + flags: Qt.Window | Qt.FramelessWindowHint + + signal rejected() + signal started(); + + function open() { + show() + } + + // TODO: implement without hardcoding sizes + width: 480 + height: 200 + + ColumnLayout { + id: mainLayout + spacing: 10 + anchors { fill: parent; margins: 35 } + + ColumnLayout { + id: column + //anchors {fill: parent; margins: 16 } + Layout.alignment: Qt.AlignHCenter + + Label { + text: qsTr("Pool miner doesn't appear to be running") + Layout.alignment: Qt.AlignHCenter + Layout.columnSpan: 2 + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 24 + font.family: "Arial" + color: "#555555" + } + + } + + RowLayout { + id: buttons + spacing: 60 + Layout.alignment: Qt.AlignHCenter + + MoneroComponents.StandardButton { + id: okButton + width: 120 + fontSize: 14 + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + text: qsTr("Start pool miner") + KeyNavigation.tab: cancelButton + onClicked: { + root.close() + appWindow.startPoolMiner(poolMinerFlags.text); + root.started() + } + } + + MoneroComponents.StandardButton { + id: cancelButton + width: 120 + fontSize: 14 + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + text: qsTr("Cancel") + + onClicked: { + root.close() + root.rejected() + } + } + } + RowLayout { + id: advancedRow + MoneroComponents.Label { + id: poolMinerFlagsLabel + color: "#4A4949" + text: qsTr("Pool miner startup flags") + translationManager.emptyString + fontSize: 16 + } + + MoneroComponents.LineEdit { + id: poolMinerFlags + Layout.preferredWidth: 200 + Layout.fillWidth: true + text: appWindow.persistentSettings.poolMinerFlags; + placeholderText: qsTr("(optional)") + } + + } + } + +} + + + diff --git a/main.cpp b/main.cpp index 9f87124f23..a1c621d186 100644 --- a/main.cpp +++ b/main.cpp @@ -46,6 +46,7 @@ #include "model/TransactionHistoryModel.h" #include "model/TransactionHistorySortFilterModel.h" #include "daemon/DaemonManager.h" +#include "poolminer/PoolMinerManager.h" #include "AddressBook.h" #include "model/AddressBookModel.h" @@ -95,6 +96,9 @@ int main(int argc, char *argv[]) qmlRegisterUncreatableType("moneroComponents.DaemonManager", 1, 0, "DaemonManager", "DaemonManager can't be instantiated directly"); + qmlRegisterUncreatableType("moneroComponents.PoolMinerManager", 1, 0, "PoolMinerManager", + "PoolMinerManager can't be instantiated directly"); + qmlRegisterUncreatableType("moneroComponents.AddressBookModel", 1, 0, "AddressBookModel", "AddressBookModel can't be instantiated directly"); @@ -121,6 +125,9 @@ int main(int argc, char *argv[]) DaemonManager * daemonManager = DaemonManager::instance(&arguments); QObject::connect(&app, SIGNAL(aboutToQuit()), daemonManager, SLOT(closing())); engine.rootContext()->setContextProperty("daemonManager", daemonManager); + PoolMinerManager * poolMinerManager = PoolMinerManager::instance(&arguments); + QObject::connect(&app, SIGNAL(aboutToQuit()), poolMinerManager, SLOT(closing())); + engine.rootContext()->setContextProperty("poolMinerManager", poolMinerManager); // export to QML monero accounts root directory // wizard is talking about where diff --git a/main.qml b/main.qml index e3bc187128..a95bf62cf3 100644 --- a/main.qml +++ b/main.qml @@ -60,6 +60,7 @@ ApplicationWindow { property bool daemonSynced: false property int maxWindowHeight: (Screen.height < 900)? 720 : 800; property bool daemonRunning: false + property bool poolMinerRunning: false property alias toolTip: toolTip // true if wallet ever synchronized @@ -362,6 +363,29 @@ ApplicationWindow { daemonRunning = false; } + function startPoolMiner(flags){ + appWindow.showProcessingSplash(qsTr("Waiting for pool miner to start...")) + poolMinerManager.start(flags); + appWindow.hideProcessingSplash(); + persistentSettings.poolMinerFlags = flags + } + + function stopPoolMiner(){ + appWindow.showProcessingSplash(qsTr("Waiting for pool miner to stop...")) + poolMinerManager.stop(); + appWindow.hideProcessingSplash(); + } + + function onPoolMinerStarted(){ + console.log("pool miner started"); + poolMinerRunning = true; + } + function onPoolMinerStopped(){ + console.log("pool miner stopped"); + hideProcessingSplash(); + poolMinerRunning = false; + } + function onWalletNewBlock(blockHeight) { @@ -684,6 +708,9 @@ ApplicationWindow { daemonManager.daemonStarted.connect(onDaemonStarted); daemonManager.daemonStopped.connect(onDaemonStopped); + poolMinerManager.poolMinerStarted.connect(onPoolMinerStarted); + poolMinerManager.poolMinerStopped.connect(onPoolMinerStopped); + if(!walletsFound()) { rootItem.state = "wizard" } else { @@ -716,6 +743,7 @@ ApplicationWindow { property bool is_recovering : false property bool customDecorations : true property string daemonFlags + property string poolMinerFlags } // Information dialog @@ -776,6 +804,10 @@ ApplicationWindow { } + PoolMinerManagerDialog { + id: poolMinerManagerDialog + } + ProcessingSplash { id: splash width: appWindow.width / 1.5 @@ -838,7 +870,7 @@ ApplicationWindow { onTxkeyClicked: middlePanel.state = "TxKey" onHistoryClicked: middlePanel.state = "History" onAddressBookClicked: middlePanel.state = "AddressBook" - onMiningClicked: middlePanel.state = "Minning" + onMiningClicked: middlePanel.state = "Mining" onSignClicked: middlePanel.state = "Sign" onSettingsClicked: middlePanel.state = "Settings" } @@ -1120,7 +1152,8 @@ ApplicationWindow { onClosing: { // Close wallet non async on exit walletManager.closeWallet(); - // Stop daemon + // Stop daemon and pool miner daemonManager.stop(); + poolMinerManager.stop(); } } diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 1f3b402472..1283a4f795 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -33,6 +33,7 @@ HEADERS += \ src/QR-Code-generator/QrCode.hpp \ src/QR-Code-generator/QrSegment.hpp \ src/daemon/DaemonManager.h \ + src/poolminer/PoolMinerManager.h \ src/model/AddressBookModel.h \ src/libwalletqt/AddressBook.h \ src/zxcvbn-c/zxcvbn.h @@ -56,6 +57,7 @@ SOURCES += main.cpp \ src/QR-Code-generator/QrCode.cpp \ src/QR-Code-generator/QrSegment.cpp \ src/daemon/DaemonManager.cpp \ + src/poolminer/PoolMinerManager.cpp \ src/model/AddressBookModel.cpp \ src/libwalletqt/AddressBook.cpp \ src/zxcvbn-c/zxcvbn.c diff --git a/pages/Mining.qml b/pages/Mining.qml index e9a5047602..ad23d04d2d 100644 --- a/pages/Mining.qml +++ b/pages/Mining.qml @@ -27,8 +27,270 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 +import "../components" +import moneroComponents.Wallet 1.0 Rectangle { - width: 100 - height: 62 + id: root + color: "#F0EEEE" + property string currentPool: "" + property var currentHashRate: 0 + + function getRandomPool() { + // select random entry, skipping the first ("Automatic") + var idx = 1 + Math.floor(Math.random() * ((miningPoolModel.rowCount() - 1))) + return miningPoolModel.get(idx).uri + } + + function getSelectedPool() { + var uri = miningPoolModel.get(miningPoolDropdown.currentIndex).uri + if (uri === "") // Automatic + uri = getRandomPool() + return uri + } + + /* main layout */ + ColumnLayout { + id: mainLayout + anchors.margins: 40 + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + + Label { + id: titleLabel + fontSize: 24 + text: qsTr("Monero pool mining") + } + + Text { + id: mainLabel + text: qsTr("This screen allows you to pool mine.
") + + qsTr("Pool mining will get you some small amount of Monero every once in a while.
") + + qsTr("Your computer will search for Monero block solutions, and report its work to a third party server.
") + + qsTr("That server will then pay you some Momero based on your work, when it finds a block.
") + + qsTr("
") + + qsTr("If using the Automatic pool selection, you will mine on a random pool. If the pool becomes
") + + qsTr("unavailable for any reason, you will automatically switch to another pool, to avoid wasted mining.
") + + qsTr("
") + + qsTr("Note that the Monero you will earn mining is paid by the third party pool, not the Monero network itself.
") + + qsTr("
") + + qsTr("Mining is both important for the health of the network, and a source of Monero for you.
") + + qsTr("Thank for you mining!
") + + translationManager.emptyString + wrapMode: Text.Wrap + } + + RowLayout { + z: 1 + ListModel { + id: miningPoolModel + ListElement { column1: qsTr("Automatic (recommended)") ; column2: ""; uri: "" } // must be first + ListElement { column1: qsTr("moneroworld.com") ; column2: ""; uri: "stratum+tcp://drill.moneroworld.com:7777" } + ListElement { column1: qsTr("monerohash.com") ; column2: ""; uri: "stratum+tcp://monerohash.com:7777" } + ListElement { column1: qsTr("poolto.be") ; column2: ""; uri: "stratum+tcp://xmr.poolto.be:3000" } + ListElement { column1: qsTr("supportxmr.com") ; column2: ""; uri: "stratum+tcp://pool.supportxmr.com:5555" } + ListElement { column1: qsTr("xmrpool.eu") ; column2: ""; uri: "stratum+tcp://xmrpool.eu:5555" } + } + + Label { + id: miningPoolLabel + fontSize: 14 + text: qsTr("Mining pool") + translationManager.emptyString + } + + StandardDropdown { + id: miningPoolDropdown + anchors.leftMargin: 17 + anchors.rightMargin: 17 + Layout.fillWidth: true + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + dataModel: miningPoolModel + } + } + + RowLayout { + id: poolMinerThreadsRow + Label { + id: poolMinerThreadsLabel + color: "#4A4949" + text: qsTr("Pool miner threads") + translationManager.emptyString + fontSize: 16 + } + LineEdit { + id: poolMinerThreadsLine + Layout.preferredWidth: 200 + Layout.fillWidth: true + text: "1" + placeholderText: qsTr("(optional)") + translationManager.emptyString + validator: IntValidator { bottom: 1 } + } + } + +/* + RowLayout { + id: poolMinerFlagsRow + Label { + id: poolMinerFlagsLabel + color: "#4A4949" + text: qsTr("Pool miner startup flags") + translationManager.emptyString + fontSize: 16 + } + LineEdit { + id: poolMinerFlags + Layout.preferredWidth: 200 + Layout.fillWidth: true + text: appWindow.persistentSettings.poolMinerFlags; + placeholderText: qsTr("(optional)") + translationManager.emptyString + } + } +*/ + + RowLayout { + Label { + id: managePoolMinerLabel + color: "#4A4949" + text: qsTr("Manage pool miner") + translationManager.emptyString + fontSize: 16 + } + + StandardButton { + visible: true + enabled: !appWindow.poolMinerRunning + id: startPoolMinerButton + width: 110 + text: qsTr("Start poolMiner") + translationManager.emptyString + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + onClicked: { + start() + } + } + + StandardButton { + visible: true + enabled: appWindow.poolMinerRunning + id: stopPoolMinerButton + width: 110 + text: qsTr("Stop poolMiner") + translationManager.emptyString + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + onClicked: { + appWindow.stopPoolMiner() + } + } + + StandardButton { + visible: true + // enabled: appWindow.poolMinerRunning + id: poolMinerConsolePopupButton + width: 110 + text: qsTr("Show log") + translationManager.emptyString + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + onClicked: { + poolMinerConsolePopup.open(); + } + } + + } + + Text { + id: statusText + anchors.topMargin: 17 + text: qsTr("Status: not mining") + wrapMode: Text.Wrap + } + } + + // minerd console + StandardDialog { + id: poolMinerConsolePopup + height:500 + width:800 + cancelVisible: false + title: qsTr("Pool miner log") + onAccepted: { + close(); + } + } + + // fires only once + Component.onCompleted: { + poolMinerManager.poolMinerConsoleUpdated.connect(onPoolMinerConsoleUpdated) + } + + function start() { + var address = appWindow.currentWallet.address + var flags = " -a cryptonight -u " + address + " -p x -o " + getSelectedPool() + " -t " + poolMinerThreadsLine.text; + appWindow.startPoolMiner(flags) + } + + function updateStatusText() { + if (currentPool) { + if (currentHashRate == 0) { + statusText.text = qsTr("Status: mining on %1 (wait for hash rate estimation)").arg(currentPool) + translationManager.emptyString; + } + else { + statusText.text = qsTr("Status: mining on %1 at %2 H/s").arg(currentPool).arg(currentHashRate) + translationManager.emptyString; + } + } + else { + statusText.text = qsTr("Status: not mining") + translationManager.emptyString; + } + } + + function onFailure() { + currentPool = "" + currentHashRate = 0 + updateStatusText() + var uri = miningPoolModel.get(miningPoolDropdown.currentIndex).uri + if (uri === "") { // Automatic + appWindow.stopPoolMiner() + start() + } + } + + function onNewPool(pool) { + currentPool = pool + currentHashRate = 0 + updateStatusText() + } + + function onHashRate(hr) { + currentHashRate = hr + updateStatusText() + } + + function onPoolMinerConsoleUpdated(message){ + // Update pool miner console + poolMinerConsolePopup.textArea.append(message) + + // look for well known strings + var offset + if (message.search("...retry after 10 seconds") >= 0) + onFailure(); + else if (message.search("Stratum authentication failed") >= 0) + onFailure(); + else if ((offset = message.search("Starting Stratum on")) >= 0) + onNewPool(message.substr(offset + 20)); + else if (message.search("accepted:") >= 0 && (offset = message.search("%\\), ")) >= 0) { + var hr = message.substr(offset + 4) + offset = hr.search(" ") + hr = hr.substr(0, offset) + onHashRate(hr) + } + } + } diff --git a/qml.qrc b/qml.qrc index a952af0f93..f1c2ce2a19 100644 --- a/qml.qrc +++ b/qml.qrc @@ -121,6 +121,7 @@ components/StandardDialog.qml pages/Sign.qml components/DaemonManagerDialog.qml + components/PoolMinerManagerDialog.qml version.js diff --git a/src/poolminer/PoolMinerManager.cpp b/src/poolminer/PoolMinerManager.cpp new file mode 100644 index 0000000000..5ad080808d --- /dev/null +++ b/src/poolminer/PoolMinerManager.cpp @@ -0,0 +1,156 @@ +#include "PoolMinerManager.h" +#include +#include +#include +#include +#include +#include +#include +#include + +PoolMinerManager * PoolMinerManager::m_instance = nullptr; +QStringList PoolMinerManager::m_clArgs; + +PoolMinerManager *PoolMinerManager::instance(const QStringList *args) +{ + if (!m_instance) { + m_instance = new PoolMinerManager; + // store command line arguments for later use + m_clArgs = *args; + m_clArgs.removeFirst(); + } + + return m_instance; +} + +bool PoolMinerManager::start(const QString &flags) +{ + // + QString process; +#ifdef Q_OS_WIN + process = QApplication::applicationDirPath() + "/minerd.exe"; +#elif defined(Q_OS_UNIX) + process = QApplication::applicationDirPath() + "/minerd"; +#endif + + if (process.length() == 0) { + qDebug() << "no pool miner binary defined for current platform"; + return false; + } + + + // prepare command line arguments and pass to monerod + QStringList arguments; + foreach (const QString &str, m_clArgs) { + qDebug() << QString(" [%1] ").arg(str); + if (!str.isEmpty()) + arguments << str; + } + + // Custom startup flags for pool miner + foreach (const QString &str, flags.split(" ")) { + qDebug() << QString(" [%1] ").arg(str); + if (!str.isEmpty()) + arguments << str; + } + + + qDebug() << "starting minerd " + process; + qDebug() << "With command line arguments " << arguments; + + m_poolMiner = new QProcess(); + initialized = true; + + // Connect output slots + connect (m_poolMiner, SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput())); + connect (m_poolMiner, SIGNAL(readyReadStandardError()), this, SLOT(printError())); + + // Start minerd + m_poolMiner->start(process,arguments); + bool started = m_poolMiner->waitForStarted(2500); // 2.5 seconds is enough, this is a fast program + qDebug() << "started: " << started; + + // add state changed listener + connect(m_poolMiner,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState))); + + if (!started) { + qDebug() << "minerd start error: " + m_poolMiner->errorString(); + } else { + emit poolMinerStarted(); + } + + return started; +} + +bool PoolMinerManager::stop() +{ + if (initialized) { + qDebug() << "stopping pool miner"; + // we can't use QProcess::terminate() on windows console process + // write exit command to stdin + m_poolMiner->kill(); + } + + return true; +} + +void PoolMinerManager::stateChanged(QProcess::ProcessState state) +{ + qDebug() << "STATE CHANGED: " << state; + if (state == QProcess::NotRunning) { + emit poolMinerStopped(); + } +} + +void PoolMinerManager::printOutput() +{ + QByteArray byteArray = m_poolMiner->readAllStandardOutput(); + QStringList strLines = QString(byteArray).split("\n"); + + foreach (QString line, strLines) { + emit poolMinerConsoleUpdated(line); + qDebug() << "PoolMiner: " + line; + } +} + +void PoolMinerManager::printError() +{ + QByteArray byteArray = m_poolMiner->readAllStandardError(); + QStringList strLines = QString(byteArray).split("\n"); + + foreach (QString line, strLines) { + emit poolMinerConsoleUpdated(line); + qDebug() << "PoolMiner ERROR: " + line; + } +} + +void PoolMinerManager::onError(QProcess::ProcessError error) +{ + qDebug() << "Error starting pool miner"; +} + +bool PoolMinerManager::running() const +{ + if (initialized) { + qDebug() << m_poolMiner->state(); + qDebug() << QProcess::NotRunning; + return m_poolMiner->state() > QProcess::NotRunning; + } + return false; +} + +PoolMinerManager::PoolMinerManager(QObject *parent) + : QObject(parent) +{ + +} + +void PoolMinerManager::closing() +{ + qDebug() << __FUNCTION__; + stop(); + // Wait for pool miner to stop before exiting (max 2 secs) + if (initialized) { + m_poolMiner->waitForFinished(2000); + } +} diff --git a/src/poolminer/PoolMinerManager.h b/src/poolminer/PoolMinerManager.h new file mode 100644 index 0000000000..c246d4195b --- /dev/null +++ b/src/poolminer/PoolMinerManager.h @@ -0,0 +1,44 @@ +#ifndef POOLMINERMANAGER_H +#define POOLMINERMANAGER_H + +#include +#include +#include + +class PoolMinerManager : public QObject +{ + Q_OBJECT + +public: + + static PoolMinerManager * instance(const QStringList *args); + + Q_INVOKABLE bool start(const QString &flags); + Q_INVOKABLE bool stop(); + + // return true if pool miner process is started + Q_INVOKABLE bool running() const; + +signals: + void poolMinerStarted(); + void poolMinerStopped(); + void poolMinerConsoleUpdated(QString message); + +public slots: + void printOutput(); + void printError(); + void closing(); + void onError(QProcess::ProcessError error); + void stateChanged(QProcess::ProcessState state); + +private: + + explicit PoolMinerManager(QObject *parent = 0); + static PoolMinerManager * m_instance; + static QStringList m_clArgs; + QProcess *m_poolMiner; + bool initialized = false; + +}; + +#endif // POOLMINERMANAGER_H