mirror of
https://github.com/jwinterm/xmr-stak.git
synced 2026-10-19 05:58:41 +02:00
fix #340 With #90 the option to overwrite the pool settings via CLI options is broken. This pull request will allow to overwrite the options of the pool with the highest weighting.
1279 lines
33 KiB
C++
1279 lines
33 KiB
C++
/*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Additional permission under GNU GPL version 3 section 7
|
|
*
|
|
* If you modify this Program, or any covered work, by linking or combining
|
|
* it with OpenSSL (or a modified version of that library), containing parts
|
|
* covered by the terms of OpenSSL License and SSLeay License, the licensors
|
|
* of this Program grant you additional permission to convey the resulting work.
|
|
*
|
|
*/
|
|
|
|
#include "xmrstak/jconf.hpp"
|
|
#include "executor.hpp"
|
|
#include "xmrstak/net/jpsock.hpp"
|
|
|
|
#include "telemetry.hpp"
|
|
#include "xmrstak/backend/miner_work.hpp"
|
|
#include "xmrstak/backend/globalStates.hpp"
|
|
#include "xmrstak/backend/backendConnector.hpp"
|
|
#include "xmrstak/backend/iBackend.hpp"
|
|
|
|
#include "xmrstak/jconf.hpp"
|
|
#include "xmrstak/misc/console.hpp"
|
|
#include "xmrstak/donate-level.hpp"
|
|
#include "xmrstak/version.hpp"
|
|
#include "xmrstak/http/webdesign.hpp"
|
|
|
|
#include <thread>
|
|
#include <string>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
|
|
|
|
#ifdef _WIN32
|
|
#define strncasecmp _strnicmp
|
|
#endif // _WIN32
|
|
|
|
executor::executor()
|
|
{
|
|
}
|
|
|
|
void executor::push_timed_event(ex_event&& ev, size_t sec)
|
|
{
|
|
std::unique_lock<std::mutex> lck(timed_event_mutex);
|
|
lTimedEvents.emplace_back(std::move(ev), sec_to_ticks(sec));
|
|
}
|
|
|
|
void executor::ex_clock_thd()
|
|
{
|
|
size_t tick = 0;
|
|
while (true)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(size_t(iTickTime)));
|
|
|
|
push_event(ex_event(EV_PERF_TICK));
|
|
|
|
//Eval pool choice every fourth tick
|
|
if((tick++ & 0x03) == 0)
|
|
push_event(ex_event(EV_EVAL_POOL_CHOICE));
|
|
|
|
// Service timed events
|
|
std::unique_lock<std::mutex> lck(timed_event_mutex);
|
|
std::list<timed_event>::iterator ev = lTimedEvents.begin();
|
|
while (ev != lTimedEvents.end())
|
|
{
|
|
ev->ticks_left--;
|
|
if(ev->ticks_left == 0)
|
|
{
|
|
push_event(std::move(ev->event));
|
|
ev = lTimedEvents.erase(ev);
|
|
}
|
|
else
|
|
ev++;
|
|
}
|
|
lck.unlock();
|
|
}
|
|
}
|
|
|
|
bool executor::get_live_pools(std::vector<jpsock*>& eval_pools, bool is_dev)
|
|
{
|
|
size_t limit = jconf::inst()->GetGiveUpLimit();
|
|
size_t wait = jconf::inst()->GetNetRetry();
|
|
|
|
if(limit == 0 || is_dev) limit = (-1); //No limit = limit of 2^64-1
|
|
|
|
size_t pool_count = 0;
|
|
size_t over_limit = 0;
|
|
for(jpsock& pool : pools)
|
|
{
|
|
if(pool.is_dev_pool() != is_dev)
|
|
continue;
|
|
|
|
// Only eval live pools
|
|
size_t num, dtime;
|
|
if(pool.get_disconnects(num, dtime))
|
|
set_timestamp();
|
|
|
|
if(dtime == 0 || (dtime >= wait && num <= limit))
|
|
eval_pools.emplace_back(&pool);
|
|
|
|
pool_count++;
|
|
if(num > limit)
|
|
over_limit++;
|
|
}
|
|
|
|
if(eval_pools.size() == 0)
|
|
{
|
|
if(!is_dev)
|
|
{
|
|
if(xmrstak::globalStates::inst().pool_id != invalid_pool_id)
|
|
{
|
|
printer::inst()->print_msg(L0, "All pools are dead. Idling...");
|
|
auto work = xmrstak::miner_work();
|
|
xmrstak::pool_data dat;
|
|
xmrstak::globalStates::inst().switch_work(work, dat);
|
|
}
|
|
|
|
if(over_limit == pool_count)
|
|
{
|
|
printer::inst()->print_msg(L0, "All pools are over give up limit. Exitting.");
|
|
exit(0);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
return get_live_pools(eval_pools, false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* This event is called by the timer and whenever something relevant happens.
|
|
* The job here is to decide if we want to connect, disconnect, or switch jobs (or do nothing)
|
|
*/
|
|
void executor::eval_pool_choice()
|
|
{
|
|
std::vector<jpsock*> eval_pools;
|
|
eval_pools.reserve(pools.size());
|
|
|
|
bool dev_time = is_dev_time();
|
|
if(!get_live_pools(eval_pools, dev_time))
|
|
return;
|
|
|
|
size_t running = 0;
|
|
for(jpsock* pool : eval_pools)
|
|
{
|
|
if(pool->is_running())
|
|
running++;
|
|
}
|
|
|
|
// Special case - if we are without a pool, connect to all find a live pool asap
|
|
if(running == 0)
|
|
{
|
|
if(dev_time)
|
|
printer::inst()->print_msg(L1, "Fast-connecting to dev pool ...");
|
|
|
|
for(jpsock* pool : eval_pools)
|
|
{
|
|
if(pool->can_connect())
|
|
{
|
|
if(!dev_time)
|
|
printer::inst()->print_msg(L1, "Fast-connecting to %s pool ...", pool->get_pool_addr());
|
|
std::string error;
|
|
if(!pool->connect(error))
|
|
log_socket_error(pool, std::move(error));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
std::sort(eval_pools.begin(), eval_pools.end(), [](jpsock* a, jpsock* b) { return b->get_pool_weight(true) < a->get_pool_weight(true); });
|
|
jpsock* goal = eval_pools[0];
|
|
|
|
if(goal->get_pool_id() != xmrstak::globalStates::inst().pool_id)
|
|
{
|
|
if(!goal->is_running() && goal->can_connect())
|
|
{
|
|
if(dev_time)
|
|
printer::inst()->print_msg(L1, "Connecting to dev pool ...");
|
|
else
|
|
printer::inst()->print_msg(L1, "Connecting to %s pool ...", goal->get_pool_addr());
|
|
|
|
std::string error;
|
|
if(!goal->connect(error))
|
|
log_socket_error(goal, std::move(error));
|
|
return;
|
|
}
|
|
|
|
if(goal->is_logged_in())
|
|
{
|
|
pool_job oPoolJob;
|
|
if(!goal->get_current_job(oPoolJob))
|
|
{
|
|
goal->disconnect();
|
|
return;
|
|
}
|
|
|
|
size_t prev_pool_id = current_pool_id;
|
|
current_pool_id = goal->get_pool_id();
|
|
on_pool_have_job(current_pool_id, oPoolJob);
|
|
|
|
jpsock* prev_pool = pick_pool_by_id(prev_pool_id);
|
|
if(prev_pool == nullptr || (!prev_pool->is_dev_pool() && !goal->is_dev_pool()))
|
|
reset_stats();
|
|
|
|
if(goal->is_dev_pool() && (prev_pool != nullptr && !prev_pool->is_dev_pool()))
|
|
last_usr_pool_id = prev_pool_id;
|
|
else
|
|
last_usr_pool_id = invalid_pool_id;
|
|
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* All is good - but check if we can do better */
|
|
std::sort(eval_pools.begin(), eval_pools.end(), [](jpsock* a, jpsock* b) { return b->get_pool_weight(false) < a->get_pool_weight(false); });
|
|
jpsock* goal2 = eval_pools[0];
|
|
|
|
if(goal->get_pool_id() != goal2->get_pool_id())
|
|
{
|
|
if(!goal2->is_running() && goal2->can_connect())
|
|
{
|
|
printer::inst()->print_msg(L1, "Background-connect to %s pool ...", goal2->get_pool_addr());
|
|
std::string error;
|
|
if(!goal2->connect(error))
|
|
log_socket_error(goal2, std::move(error));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!dev_time)
|
|
{
|
|
for(jpsock& pool : pools)
|
|
{
|
|
if(goal->is_logged_in() && pool.is_logged_in() && pool.get_pool_id() != goal->get_pool_id())
|
|
pool.disconnect(true);
|
|
|
|
if(pool.is_dev_pool() && pool.is_logged_in())
|
|
pool.disconnect(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void executor::log_socket_error(jpsock* pool, std::string&& sError)
|
|
{
|
|
std::string pool_name;
|
|
pool_name.reserve(128);
|
|
pool_name.append("[").append(pool->get_pool_addr()).append("] ");
|
|
sError.insert(0, pool_name);
|
|
|
|
vSocketLog.emplace_back(std::move(sError));
|
|
printer::inst()->print_msg(L1, "SOCKET ERROR - %s", vSocketLog.back().msg.c_str());
|
|
|
|
push_event(ex_event(EV_EVAL_POOL_CHOICE));
|
|
}
|
|
|
|
void executor::log_result_error(std::string&& sError)
|
|
{
|
|
size_t i = 1, ln = vMineResults.size();
|
|
for(; i < ln; i++)
|
|
{
|
|
if(vMineResults[i].compare(sError))
|
|
{
|
|
vMineResults[i].increment();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(i == ln) //Not found
|
|
vMineResults.emplace_back(std::move(sError));
|
|
else
|
|
sError.clear();
|
|
}
|
|
|
|
void executor::log_result_ok(uint64_t iActualDiff)
|
|
{
|
|
iPoolHashes += iPoolDiff;
|
|
|
|
size_t ln = iTopDiff.size() - 1;
|
|
if(iActualDiff > iTopDiff[ln])
|
|
{
|
|
iTopDiff[ln] = iActualDiff;
|
|
std::sort(iTopDiff.rbegin(), iTopDiff.rend());
|
|
}
|
|
|
|
vMineResults[0].increment();
|
|
}
|
|
|
|
jpsock* executor::pick_pool_by_id(size_t pool_id)
|
|
{
|
|
if(pool_id == invalid_pool_id)
|
|
return nullptr;
|
|
|
|
for(jpsock& pool : pools)
|
|
if(pool.get_pool_id() == pool_id)
|
|
return &pool;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void executor::on_sock_ready(size_t pool_id)
|
|
{
|
|
jpsock* pool = pick_pool_by_id(pool_id);
|
|
|
|
if(pool->is_dev_pool())
|
|
printer::inst()->print_msg(L1, "Dev pool connected. Logging in...");
|
|
else
|
|
printer::inst()->print_msg(L1, "Pool %s connected. Logging in...", pool->get_pool_addr());
|
|
|
|
if(!pool->cmd_login())
|
|
{
|
|
if(!pool->have_sock_error())
|
|
{
|
|
log_socket_error(pool, pool->get_call_error());
|
|
pool->disconnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
void executor::on_sock_error(size_t pool_id, std::string&& sError, bool silent)
|
|
{
|
|
jpsock* pool = pick_pool_by_id(pool_id);
|
|
|
|
pool->disconnect();
|
|
|
|
if(pool_id == current_pool_id)
|
|
current_pool_id = invalid_pool_id;
|
|
|
|
if(silent)
|
|
return;
|
|
|
|
if(!pool->is_dev_pool())
|
|
log_socket_error(pool, std::move(sError));
|
|
else
|
|
printer::inst()->print_msg(L1, "Dev pool socket error - mining on user pool...");
|
|
}
|
|
|
|
void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
|
|
{
|
|
if(pool_id != current_pool_id)
|
|
return;
|
|
|
|
jpsock* pool = pick_pool_by_id(pool_id);
|
|
|
|
xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob, oPoolJob.iWorkLen, oPoolJob.iTarget, pool->is_nicehash(), pool_id);
|
|
|
|
xmrstak::pool_data dat;
|
|
dat.iSavedNonce = oPoolJob.iSavedNonce;
|
|
dat.pool_id = pool_id;
|
|
|
|
xmrstak::globalStates::inst().switch_work(oWork, dat);
|
|
|
|
if(dat.pool_id != pool_id)
|
|
{
|
|
jpsock* prev_pool;
|
|
if((prev_pool = pick_pool_by_id(dat.pool_id)) != nullptr)
|
|
prev_pool->save_nonce(dat.iSavedNonce);
|
|
}
|
|
|
|
if(pool->is_dev_pool())
|
|
return;
|
|
|
|
if(iPoolDiff != pool->get_current_diff())
|
|
{
|
|
iPoolDiff = pool->get_current_diff();
|
|
printer::inst()->print_msg(L2, "Difficulty changed. Now: %llu.", int_port(iPoolDiff));
|
|
}
|
|
|
|
if(dat.pool_id != pool_id)
|
|
{
|
|
jpsock* prev_pool;
|
|
if(dat.pool_id != invalid_pool_id && (prev_pool = pick_pool_by_id(dat.pool_id)) != nullptr)
|
|
{
|
|
if(prev_pool->is_dev_pool())
|
|
printer::inst()->print_msg(L2, "Switching back to user pool.");
|
|
else
|
|
printer::inst()->print_msg(L2, "Pool switched.");
|
|
}
|
|
else
|
|
printer::inst()->print_msg(L2, "Pool logged in.");
|
|
}
|
|
else
|
|
printer::inst()->print_msg(L3, "New block detected.");
|
|
}
|
|
|
|
void executor::on_miner_result(size_t pool_id, job_result& oResult)
|
|
{
|
|
jpsock* pool = pick_pool_by_id(pool_id);
|
|
bool is_monero = jconf::inst()->IsCurrencyMonero();
|
|
|
|
if(pool->is_dev_pool())
|
|
{
|
|
//Ignore errors silently
|
|
if(pool->is_running() && pool->is_logged_in())
|
|
pool->cmd_submit(oResult.sJobID, oResult.iNonce, oResult.bResult, pvThreads->at(oResult.iThreadId), is_monero);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!pool->is_running() || !pool->is_logged_in())
|
|
{
|
|
log_result_error("[NETWORK ERROR]");
|
|
return;
|
|
}
|
|
|
|
using namespace std::chrono;
|
|
size_t t_start = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count();
|
|
bool bResult = pool->cmd_submit(oResult.sJobID, oResult.iNonce, oResult.bResult, pvThreads->at(oResult.iThreadId), is_monero);
|
|
size_t t_len = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count() - t_start;
|
|
|
|
if(t_len > 0xFFFF)
|
|
t_len = 0xFFFF;
|
|
iPoolCallTimes.push_back((uint16_t)t_len);
|
|
|
|
if(bResult)
|
|
{
|
|
uint64_t* targets = (uint64_t*)oResult.bResult;
|
|
log_result_ok(jpsock::t64_to_diff(targets[3]));
|
|
printer::inst()->print_msg(L3, "Result accepted by the pool.");
|
|
}
|
|
else
|
|
{
|
|
if(!pool->have_sock_error())
|
|
{
|
|
printer::inst()->print_msg(L3, "Result rejected by the pool.");
|
|
|
|
std::string error = pool->get_call_error();
|
|
|
|
if(strncasecmp(error.c_str(), "Unauthenticated", 15) == 0)
|
|
{
|
|
printer::inst()->print_msg(L2, "Your miner was unable to find a share in time. Either the pool difficulty is too high, or the pool timeout is too low.");
|
|
pool->disconnect();
|
|
}
|
|
|
|
log_result_error(std::move(error));
|
|
}
|
|
else
|
|
log_result_error("[NETWORK ERROR]");
|
|
}
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <signal.h>
|
|
void disable_sigpipe()
|
|
{
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_handler = SIG_IGN;
|
|
sa.sa_flags = 0;
|
|
if (sigaction(SIGPIPE, &sa, 0) == -1)
|
|
printer::inst()->print_msg(L1, "ERROR: Call to sigaction failed!");
|
|
}
|
|
|
|
#else
|
|
inline void disable_sigpipe() {}
|
|
#endif
|
|
|
|
void executor::ex_main()
|
|
{
|
|
disable_sigpipe();
|
|
|
|
assert(1000 % iTickTime == 0);
|
|
|
|
xmrstak::miner_work oWork = xmrstak::miner_work();
|
|
|
|
// \todo collect all backend threads
|
|
pvThreads = xmrstak::BackendConnector::thread_starter(oWork);
|
|
|
|
if(pvThreads->size()==0)
|
|
{
|
|
printer::inst()->print_msg(L1, "ERROR: No miner backend enabled.");
|
|
win_exit();
|
|
}
|
|
|
|
telem = new xmrstak::telemetry(pvThreads->size());
|
|
|
|
set_timestamp();
|
|
size_t pc = jconf::inst()->GetPoolCount();
|
|
bool tls = true;
|
|
for(size_t i=0; i < pc; i++)
|
|
{
|
|
jconf::pool_cfg cfg;
|
|
jconf::inst()->GetPoolConfig(i, cfg);
|
|
#ifdef CONF_NO_TLS
|
|
if(cfg.tls)
|
|
{
|
|
printer::inst()->print_msg(L1, "ERROR: No miner was compiled without TLS support.");
|
|
win_exit();
|
|
}
|
|
#endif
|
|
if(!cfg.tls) tls = false;
|
|
pools.emplace_back(i+1, cfg.sPoolAddr, cfg.sWalletAddr, cfg.sPasswd, cfg.weight, false, cfg.tls, cfg.tls_fingerprint, cfg.nicehash);
|
|
}
|
|
|
|
if(jconf::inst()->IsCurrencyMonero())
|
|
{
|
|
if(tls)
|
|
pools.emplace_front(0, "donate.xmr-stak.net:6666", "", "", 0.0, true, true, "", false);
|
|
else
|
|
pools.emplace_front(0, "donate.xmr-stak.net:3333", "", "", 0.0, true, false, "", false);
|
|
}
|
|
else
|
|
{
|
|
if(tls)
|
|
pools.emplace_front(0, "donate.xmr-stak.net:7777", "", "", 0.0, true, true, "", true);
|
|
else
|
|
pools.emplace_front(0, "donate.xmr-stak.net:4444", "", "", 0.0, true, false, "", true);
|
|
}
|
|
|
|
/* find the pool with the highest weighting to allow overwriting of the
|
|
* pool settings via command line options.
|
|
*/
|
|
std::vector<jpsock*> sorted_pools;
|
|
sorted_pools.reserve(pools.size());
|
|
for(jpsock& pool : pools)
|
|
sorted_pools.emplace_back(&pool);
|
|
std::sort(sorted_pools.begin(), sorted_pools.end(), [](jpsock* a, jpsock* b) { return b->get_pool_weight(true) < a->get_pool_weight(true); });
|
|
|
|
// overwrite pool address if cli option is used
|
|
auto& poolURL = xmrstak::params::inst().poolURL;
|
|
if(!poolURL.empty())
|
|
{
|
|
sorted_pools[0]->set_pool_addr(poolURL.c_str());
|
|
}
|
|
// overwrite user pool login name if cli option is used
|
|
auto& poolUsername = xmrstak::params::inst().poolUsername;
|
|
if(!poolUsername.empty())
|
|
sorted_pools[0]->set_user_login(poolUsername.c_str());
|
|
// overwrite user pool login password if cli option is used
|
|
auto& poolPasswd = xmrstak::params::inst().poolPasswd;
|
|
if(!poolPasswd.empty())
|
|
sorted_pools[0]->set_user_passwd(poolPasswd.c_str());
|
|
|
|
ex_event ev;
|
|
std::thread clock_thd(&executor::ex_clock_thd, this);
|
|
|
|
eval_pool_choice();
|
|
|
|
// Place the default success result at position 0, it needs to
|
|
// be here even if our first result is a failure
|
|
vMineResults.emplace_back();
|
|
|
|
// If the user requested it, start the autohash printer
|
|
if(jconf::inst()->GetVerboseLevel() >= 4)
|
|
push_timed_event(ex_event(EV_HASHRATE_LOOP), jconf::inst()->GetAutohashTime());
|
|
|
|
size_t cnt = 0, i;
|
|
while (true)
|
|
{
|
|
ev = oEventQ.pop();
|
|
switch (ev.iName)
|
|
{
|
|
case EV_SOCK_READY:
|
|
on_sock_ready(ev.iPoolId);
|
|
break;
|
|
|
|
case EV_SOCK_ERROR:
|
|
on_sock_error(ev.iPoolId, std::move(ev.oSocketError.sSocketError), ev.oSocketError.silent);
|
|
break;
|
|
|
|
case EV_POOL_HAVE_JOB:
|
|
on_pool_have_job(ev.iPoolId, ev.oPoolJob);
|
|
break;
|
|
|
|
case EV_MINER_HAVE_RESULT:
|
|
on_miner_result(ev.iPoolId, ev.oJobResult);
|
|
break;
|
|
|
|
case EV_EVAL_POOL_CHOICE:
|
|
eval_pool_choice();
|
|
break;
|
|
|
|
case EV_GPU_RES_ERROR:
|
|
log_result_error(std::string(ev.oGpuError.error_str));
|
|
break;
|
|
|
|
case EV_PERF_TICK:
|
|
for (i = 0; i < pvThreads->size(); i++)
|
|
telem->push_perf_value(i, pvThreads->at(i)->iHashCount.load(std::memory_order_relaxed),
|
|
pvThreads->at(i)->iTimestamp.load(std::memory_order_relaxed));
|
|
|
|
if((cnt++ & 0xF) == 0) //Every 16 ticks
|
|
{
|
|
double fHps = 0.0;
|
|
double fTelem;
|
|
bool normal = true;
|
|
|
|
for (i = 0; i < pvThreads->size(); i++)
|
|
{
|
|
fTelem = telem->calc_telemetry_data(10000, i);
|
|
if(std::isnormal(fTelem))
|
|
{
|
|
fHps += fTelem;
|
|
}
|
|
else
|
|
{
|
|
normal = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(normal && fHighestHps < fHps)
|
|
fHighestHps = fHps;
|
|
}
|
|
break;
|
|
|
|
case EV_USR_HASHRATE:
|
|
case EV_USR_RESULTS:
|
|
case EV_USR_CONNSTAT:
|
|
print_report(ev.iName);
|
|
break;
|
|
|
|
case EV_HTML_HASHRATE:
|
|
case EV_HTML_RESULTS:
|
|
case EV_HTML_CONNSTAT:
|
|
case EV_HTML_JSON:
|
|
http_report(ev.iName);
|
|
break;
|
|
|
|
case EV_HASHRATE_LOOP:
|
|
print_report(EV_USR_HASHRATE);
|
|
push_timed_event(ex_event(EV_HASHRATE_LOOP), jconf::inst()->GetAutohashTime());
|
|
break;
|
|
|
|
case EV_INVALID_VAL:
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline const char* hps_format(double h, char* buf, size_t l)
|
|
{
|
|
if(std::isnormal(h) || h == 0.0)
|
|
{
|
|
snprintf(buf, l, " %6.1f", h);
|
|
return buf;
|
|
}
|
|
else
|
|
return " (na)";
|
|
}
|
|
|
|
bool executor::motd_filter_console(std::string& motd)
|
|
{
|
|
if(motd.size() > motd_max_length)
|
|
return false;
|
|
|
|
motd.erase(std::remove_if(motd.begin(), motd.end(), [](int chr)->bool { return !((chr >= 0x20 && chr <= 0x7e) || chr == '\n');}), motd.end());
|
|
return motd.size() > 0;
|
|
}
|
|
|
|
bool executor::motd_filter_web(std::string& motd)
|
|
{
|
|
if(!motd_filter_console(motd))
|
|
return false;
|
|
|
|
std::string tmp;
|
|
tmp.reserve(motd.size() + 128);
|
|
|
|
for(size_t i=0; i < motd.size(); i++)
|
|
{
|
|
char c = motd[i];
|
|
switch(c)
|
|
{
|
|
case '&':
|
|
tmp.append("&");
|
|
break;
|
|
case '"':
|
|
tmp.append(""");
|
|
break;
|
|
case '\'':
|
|
tmp.append("'");
|
|
break;
|
|
case '<':
|
|
tmp.append("<");
|
|
break;
|
|
case '>':
|
|
tmp.append(">");
|
|
break;
|
|
case '\n':
|
|
tmp.append("<br>");
|
|
break;
|
|
default:
|
|
tmp.append(1, c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
motd = std::move(tmp);
|
|
return true;
|
|
}
|
|
|
|
void executor::hashrate_report(std::string& out)
|
|
{
|
|
out.reserve(2048 + pvThreads->size() * 64);
|
|
|
|
if(jconf::inst()->PrintMotd())
|
|
{
|
|
std::string motd;
|
|
for(jpsock& pool : pools)
|
|
{
|
|
motd.empty();
|
|
if(pool.get_pool_motd(motd) && motd_filter_console(motd))
|
|
{
|
|
out.append("Message from ").append(pool.get_pool_addr()).append(":\n");
|
|
out.append(motd).append("\n");
|
|
out.append("-----------------------------------------------------\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
char num[32];
|
|
double fTotal[3] = { 0.0, 0.0, 0.0};
|
|
|
|
for( uint32_t b = 0; b < 4u; ++b)
|
|
{
|
|
std::vector<xmrstak::iBackend*> backEnds;
|
|
std::copy_if(pvThreads->begin(), pvThreads->end(), std::back_inserter(backEnds),
|
|
[&](xmrstak::iBackend* backend)
|
|
{
|
|
return backend->backendType == b;
|
|
}
|
|
);
|
|
|
|
size_t nthd = backEnds.size();
|
|
if(nthd != 0)
|
|
{
|
|
size_t i;
|
|
auto bType = static_cast<xmrstak::iBackend::BackendType>(b);
|
|
std::string name(xmrstak::iBackend::getName(bType));
|
|
std::transform(name.begin(), name.end(), name.begin(), ::toupper);
|
|
|
|
out.append("HASHRATE REPORT - ").append(name).append("\n");
|
|
out.append("| ID | 10s | 60s | 15m |");
|
|
if(nthd != 1)
|
|
out.append(" ID | 10s | 60s | 15m |\n");
|
|
else
|
|
out.append(1, '\n');
|
|
|
|
for (i = 0; i < nthd; i++)
|
|
{
|
|
double fHps[3];
|
|
|
|
uint32_t tid = backEnds[i]->iThreadNo;
|
|
fHps[0] = telem->calc_telemetry_data(10000, tid);
|
|
fHps[1] = telem->calc_telemetry_data(60000, tid);
|
|
fHps[2] = telem->calc_telemetry_data(900000, tid);
|
|
|
|
snprintf(num, sizeof(num), "| %2u |", (unsigned int)i);
|
|
out.append(num);
|
|
out.append(hps_format(fHps[0], num, sizeof(num))).append(" |");
|
|
out.append(hps_format(fHps[1], num, sizeof(num))).append(" |");
|
|
out.append(hps_format(fHps[2], num, sizeof(num))).append(1, ' ');
|
|
|
|
fTotal[0] += fHps[0];
|
|
fTotal[1] += fHps[1];
|
|
fTotal[2] += fHps[2];
|
|
|
|
if((i & 0x1) == 1) //Odd i's
|
|
out.append("|\n");
|
|
}
|
|
|
|
if((i & 0x1) == 1) //We had odd number of threads
|
|
out.append("|\n");
|
|
|
|
if(nthd != 1)
|
|
out.append("-----------------------------------------------------\n");
|
|
else
|
|
out.append("---------------------------\n");
|
|
}
|
|
}
|
|
|
|
out.append("Totals: ");
|
|
out.append(hps_format(fTotal[0], num, sizeof(num)));
|
|
out.append(hps_format(fTotal[1], num, sizeof(num)));
|
|
out.append(hps_format(fTotal[2], num, sizeof(num)));
|
|
out.append(" H/s\nHighest: ");
|
|
out.append(hps_format(fHighestHps, num, sizeof(num)));
|
|
out.append(" H/s\n");
|
|
}
|
|
|
|
char* time_format(char* buf, size_t len, std::chrono::system_clock::time_point time)
|
|
{
|
|
time_t ctime = std::chrono::system_clock::to_time_t(time);
|
|
tm stime;
|
|
|
|
/*
|
|
* Oh for god's sake... this feels like we are back to the 90's...
|
|
* and don't get me started on lack strcpy_s because NIH - use non-standard strlcpy...
|
|
* And of course C++ implements unsafe version because... reasons
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
localtime_s(&stime, &ctime);
|
|
#else
|
|
localtime_r(&ctime, &stime);
|
|
#endif // __WIN32
|
|
strftime(buf, len, "%F %T", &stime);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void executor::result_report(std::string& out)
|
|
{
|
|
char num[128];
|
|
char date[32];
|
|
|
|
out.reserve(1024);
|
|
|
|
size_t iGoodRes = vMineResults[0].count, iTotalRes = iGoodRes;
|
|
size_t ln = vMineResults.size();
|
|
|
|
for(size_t i=1; i < ln; i++)
|
|
iTotalRes += vMineResults[i].count;
|
|
|
|
out.append("RESULT REPORT\n");
|
|
if(iTotalRes == 0)
|
|
{
|
|
out.append("You haven't found any results yet.\n");
|
|
return;
|
|
}
|
|
|
|
double dConnSec;
|
|
{
|
|
using namespace std::chrono;
|
|
dConnSec = (double)duration_cast<seconds>(system_clock::now() - tPoolConnTime).count();
|
|
}
|
|
|
|
snprintf(num, sizeof(num), " (%.1f %%)\n", 100.0 * iGoodRes / iTotalRes);
|
|
|
|
out.append("Difficulty : ").append(std::to_string(iPoolDiff)).append(1, '\n');
|
|
out.append("Good results : ").append(std::to_string(iGoodRes)).append(" / ").
|
|
append(std::to_string(iTotalRes)).append(num);
|
|
|
|
if(iPoolCallTimes.size() != 0)
|
|
{
|
|
// Here we use iPoolCallTimes since it also gets reset when we disconnect
|
|
snprintf(num, sizeof(num), "%.1f sec\n", dConnSec / iPoolCallTimes.size());
|
|
out.append("Avg result time : ").append(num);
|
|
}
|
|
out.append("Pool-side hashes : ").append(std::to_string(iPoolHashes)).append(2, '\n');
|
|
out.append("Top 10 best results found:\n");
|
|
|
|
for(size_t i=0; i < 10; i += 2)
|
|
{
|
|
snprintf(num, sizeof(num), "| %2llu | %16llu | %2llu | %16llu |\n",
|
|
int_port(i), int_port(iTopDiff[i]), int_port(i+1), int_port(iTopDiff[i+1]));
|
|
out.append(num);
|
|
}
|
|
|
|
out.append("\nError details:\n");
|
|
if(ln > 1)
|
|
{
|
|
out.append("| Count | Error text | Last seen |\n");
|
|
for(size_t i=1; i < ln; i++)
|
|
{
|
|
snprintf(num, sizeof(num), "| %5llu | %-32.32s | %s |\n", int_port(vMineResults[i].count),
|
|
vMineResults[i].msg.c_str(), time_format(date, sizeof(date), vMineResults[i].time));
|
|
out.append(num);
|
|
}
|
|
}
|
|
else
|
|
out.append("Yay! No errors.\n");
|
|
}
|
|
|
|
void executor::connection_report(std::string& out)
|
|
{
|
|
char num[128];
|
|
char date[32];
|
|
|
|
out.reserve(512);
|
|
|
|
jpsock* pool = pick_pool_by_id(current_pool_id);
|
|
if(pool != nullptr && pool->is_dev_pool())
|
|
pool = pick_pool_by_id(last_usr_pool_id);
|
|
|
|
out.append("CONNECTION REPORT\n");
|
|
out.append("Pool address : ").append(pool != nullptr ? pool->get_pool_addr() : "<not connected>").append(1, '\n');
|
|
if(pool != nullptr && pool->is_running() && pool->is_logged_in())
|
|
out.append("Connected since : ").append(time_format(date, sizeof(date), tPoolConnTime)).append(1, '\n');
|
|
else
|
|
out.append("Connected since : <not connected>\n");
|
|
|
|
size_t n_calls = iPoolCallTimes.size();
|
|
if (n_calls > 1)
|
|
{
|
|
//Not-really-but-good-enough median
|
|
std::nth_element(iPoolCallTimes.begin(), iPoolCallTimes.begin() + n_calls/2, iPoolCallTimes.end());
|
|
out.append("Pool ping time : ").append(std::to_string(iPoolCallTimes[n_calls/2])).append(" ms\n");
|
|
}
|
|
else
|
|
out.append("Pool ping time : (n/a)\n");
|
|
|
|
out.append("\nNetwork error log:\n");
|
|
size_t ln = vSocketLog.size();
|
|
if(ln > 0)
|
|
{
|
|
out.append("| Date | Error text |\n");
|
|
for(size_t i=0; i < ln; i++)
|
|
{
|
|
snprintf(num, sizeof(num), "| %s | %-54.54s |\n",
|
|
time_format(date, sizeof(date), vSocketLog[i].time), vSocketLog[i].msg.c_str());
|
|
out.append(num);
|
|
}
|
|
}
|
|
else
|
|
out.append("Yay! No errors.\n");
|
|
}
|
|
|
|
void executor::print_report(ex_event_name ev)
|
|
{
|
|
std::string out;
|
|
switch(ev)
|
|
{
|
|
case EV_USR_HASHRATE:
|
|
hashrate_report(out);
|
|
break;
|
|
|
|
case EV_USR_RESULTS:
|
|
result_report(out);
|
|
break;
|
|
|
|
case EV_USR_CONNSTAT:
|
|
connection_report(out);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
printer::inst()->print_str(out.c_str());
|
|
}
|
|
|
|
void executor::http_hashrate_report(std::string& out)
|
|
{
|
|
char num_a[32], num_b[32], num_c[32], num_d[32];
|
|
char buffer[4096];
|
|
size_t nthd = pvThreads->size();
|
|
|
|
out.reserve(4096);
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Hashrate Report", ver_html, "Hashrate Report");
|
|
out.append(buffer);
|
|
|
|
bool have_motd = false;
|
|
if(jconf::inst()->PrintMotd())
|
|
{
|
|
std::string motd;
|
|
for(jpsock& pool : pools)
|
|
{
|
|
motd.empty();
|
|
if(pool.get_pool_motd(motd) && motd_filter_web(motd))
|
|
{
|
|
if(!have_motd)
|
|
{
|
|
out.append(sHtmlMotdBoxStart);
|
|
have_motd = true;
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlMotdEntry, pool.get_pool_addr(), motd.c_str());
|
|
out.append(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(have_motd)
|
|
out.append(sHtmlMotdBoxEnd);
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlHashrateBodyHigh, (unsigned int)nthd + 3);
|
|
out.append(buffer);
|
|
|
|
double fTotal[3] = { 0.0, 0.0, 0.0};
|
|
for(size_t i=0; i < nthd; i++)
|
|
{
|
|
double fHps[3];
|
|
|
|
fHps[0] = telem->calc_telemetry_data(10000, i);
|
|
fHps[1] = telem->calc_telemetry_data(60000, i);
|
|
fHps[2] = telem->calc_telemetry_data(900000, i);
|
|
|
|
num_a[0] = num_b[0] = num_c[0] ='\0';
|
|
hps_format(fHps[0], num_a, sizeof(num_a));
|
|
hps_format(fHps[1], num_b, sizeof(num_b));
|
|
hps_format(fHps[2], num_c, sizeof(num_c));
|
|
|
|
fTotal[0] += fHps[0];
|
|
fTotal[1] += fHps[1];
|
|
fTotal[2] += fHps[2];
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlHashrateTableRow, (unsigned int)i, num_a, num_b, num_c);
|
|
out.append(buffer);
|
|
}
|
|
|
|
num_a[0] = num_b[0] = num_c[0] = num_d[0] ='\0';
|
|
hps_format(fTotal[0], num_a, sizeof(num_a));
|
|
hps_format(fTotal[1], num_b, sizeof(num_b));
|
|
hps_format(fTotal[2], num_c, sizeof(num_c));
|
|
hps_format(fHighestHps, num_d, sizeof(num_d));
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlHashrateBodyLow, num_a, num_b, num_c, num_d);
|
|
out.append(buffer);
|
|
}
|
|
|
|
void executor::http_result_report(std::string& out)
|
|
{
|
|
char date[128];
|
|
char buffer[4096];
|
|
|
|
out.reserve(4096);
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Result Report", ver_html, "Result Report");
|
|
out.append(buffer);
|
|
|
|
size_t iGoodRes = vMineResults[0].count, iTotalRes = iGoodRes;
|
|
size_t ln = vMineResults.size();
|
|
|
|
for(size_t i=1; i < ln; i++)
|
|
iTotalRes += vMineResults[i].count;
|
|
|
|
double fGoodResPrc = 0.0;
|
|
if(iTotalRes > 0)
|
|
fGoodResPrc = 100.0 * iGoodRes / iTotalRes;
|
|
|
|
double fAvgResTime = 0.0;
|
|
if(iPoolCallTimes.size() > 0)
|
|
{
|
|
using namespace std::chrono;
|
|
fAvgResTime = ((double)duration_cast<seconds>(system_clock::now() - tPoolConnTime).count())
|
|
/ iPoolCallTimes.size();
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlResultBodyHigh,
|
|
iPoolDiff, iGoodRes, iTotalRes, fGoodResPrc, fAvgResTime, iPoolHashes,
|
|
int_port(iTopDiff[0]), int_port(iTopDiff[1]), int_port(iTopDiff[2]), int_port(iTopDiff[3]),
|
|
int_port(iTopDiff[4]), int_port(iTopDiff[5]), int_port(iTopDiff[6]), int_port(iTopDiff[7]),
|
|
int_port(iTopDiff[8]), int_port(iTopDiff[9]));
|
|
|
|
out.append(buffer);
|
|
|
|
for(size_t i=1; i < vMineResults.size(); i++)
|
|
{
|
|
snprintf(buffer, sizeof(buffer), sHtmlResultTableRow, vMineResults[i].msg.c_str(),
|
|
int_port(vMineResults[i].count), time_format(date, sizeof(date), vMineResults[i].time));
|
|
out.append(buffer);
|
|
}
|
|
|
|
out.append(sHtmlResultBodyLow);
|
|
}
|
|
|
|
void executor::http_connection_report(std::string& out)
|
|
{
|
|
char date[128];
|
|
char buffer[4096];
|
|
|
|
out.reserve(4096);
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Connection Report", ver_html, "Connection Report");
|
|
out.append(buffer);
|
|
|
|
jpsock* pool = pick_pool_by_id(current_pool_id);
|
|
if(pool != nullptr && pool->is_dev_pool())
|
|
pool = pick_pool_by_id(last_usr_pool_id);
|
|
|
|
const char* cdate = "not connected";
|
|
if (pool != nullptr && pool->is_running() && pool->is_logged_in())
|
|
cdate = time_format(date, sizeof(date), tPoolConnTime);
|
|
|
|
size_t n_calls = iPoolCallTimes.size();
|
|
unsigned int ping_time = 0;
|
|
if (n_calls > 1)
|
|
{
|
|
//Not-really-but-good-enough median
|
|
std::nth_element(iPoolCallTimes.begin(), iPoolCallTimes.begin() + n_calls/2, iPoolCallTimes.end());
|
|
ping_time = iPoolCallTimes[n_calls/2];
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer), sHtmlConnectionBodyHigh,
|
|
pool != nullptr ? pool->get_pool_addr() : "not connected",
|
|
cdate, ping_time);
|
|
out.append(buffer);
|
|
|
|
|
|
for(size_t i=0; i < vSocketLog.size(); i++)
|
|
{
|
|
snprintf(buffer, sizeof(buffer), sHtmlConnectionTableRow,
|
|
time_format(date, sizeof(date), vSocketLog[i].time), vSocketLog[i].msg.c_str());
|
|
out.append(buffer);
|
|
}
|
|
|
|
out.append(sHtmlConnectionBodyLow);
|
|
}
|
|
|
|
inline const char* hps_format_json(double h, char* buf, size_t l)
|
|
{
|
|
if(std::isnormal(h) || h == 0.0)
|
|
{
|
|
snprintf(buf, l, "%.1f", h);
|
|
return buf;
|
|
}
|
|
else
|
|
return "null";
|
|
}
|
|
|
|
void executor::http_json_report(std::string& out)
|
|
{
|
|
const char *a, *b, *c;
|
|
char num_a[32], num_b[32], num_c[32];
|
|
char hr_buffer[64];
|
|
std::string hr_thds, res_error, cn_error;
|
|
|
|
size_t nthd = pvThreads->size();
|
|
double fTotal[3] = { 0.0, 0.0, 0.0};
|
|
hr_thds.reserve(nthd * 32);
|
|
|
|
for(size_t i=0; i < nthd; i++)
|
|
{
|
|
if(i != 0) hr_thds.append(1, ',');
|
|
|
|
double fHps[3];
|
|
fHps[0] = telem->calc_telemetry_data(10000, i);
|
|
fHps[1] = telem->calc_telemetry_data(60000, i);
|
|
fHps[2] = telem->calc_telemetry_data(900000, i);
|
|
|
|
fTotal[0] += fHps[0];
|
|
fTotal[1] += fHps[1];
|
|
fTotal[2] += fHps[2];
|
|
|
|
a = hps_format_json(fHps[0], num_a, sizeof(num_a));
|
|
b = hps_format_json(fHps[1], num_b, sizeof(num_b));
|
|
c = hps_format_json(fHps[2], num_c, sizeof(num_c));
|
|
snprintf(hr_buffer, sizeof(hr_buffer), sJsonApiThdHashrate, a, b, c);
|
|
hr_thds.append(hr_buffer);
|
|
}
|
|
|
|
a = hps_format_json(fTotal[0], num_a, sizeof(num_a));
|
|
b = hps_format_json(fTotal[1], num_b, sizeof(num_b));
|
|
c = hps_format_json(fTotal[2], num_c, sizeof(num_c));
|
|
snprintf(hr_buffer, sizeof(hr_buffer), sJsonApiThdHashrate, a, b, c);
|
|
|
|
a = hps_format_json(fHighestHps, num_a, sizeof(num_a));
|
|
|
|
size_t iGoodRes = vMineResults[0].count, iTotalRes = iGoodRes;
|
|
size_t ln = vMineResults.size();
|
|
|
|
for(size_t i=1; i < ln; i++)
|
|
iTotalRes += vMineResults[i].count;
|
|
|
|
jpsock* pool = pick_pool_by_id(current_pool_id);
|
|
if(pool != nullptr && pool->is_dev_pool())
|
|
pool = pick_pool_by_id(last_usr_pool_id);
|
|
|
|
size_t iConnSec = 0;
|
|
if(pool != nullptr && pool->is_running() && pool->is_logged_in())
|
|
{
|
|
using namespace std::chrono;
|
|
iConnSec = duration_cast<seconds>(system_clock::now() - tPoolConnTime).count();
|
|
}
|
|
|
|
double fAvgResTime = 0.0;
|
|
if(iPoolCallTimes.size() > 0)
|
|
fAvgResTime = double(iConnSec) / iPoolCallTimes.size();
|
|
|
|
char buffer[2048];
|
|
res_error.reserve((vMineResults.size() - 1) * 128);
|
|
for(size_t i=1; i < vMineResults.size(); i++)
|
|
{
|
|
using namespace std::chrono;
|
|
if(i != 1) res_error.append(1, ',');
|
|
|
|
snprintf(buffer, sizeof(buffer), sJsonApiResultError, int_port(vMineResults[i].count),
|
|
int_port(duration_cast<seconds>(vMineResults[i].time.time_since_epoch()).count()),
|
|
vMineResults[i].msg.c_str());
|
|
res_error.append(buffer);
|
|
}
|
|
|
|
size_t n_calls = iPoolCallTimes.size();
|
|
size_t iPoolPing = 0;
|
|
if (n_calls > 1)
|
|
{
|
|
//Not-really-but-good-enough median
|
|
std::nth_element(iPoolCallTimes.begin(), iPoolCallTimes.begin() + n_calls/2, iPoolCallTimes.end());
|
|
iPoolPing = iPoolCallTimes[n_calls/2];
|
|
}
|
|
|
|
cn_error.reserve(vSocketLog.size() * 256);
|
|
for(size_t i=0; i < vSocketLog.size(); i++)
|
|
{
|
|
using namespace std::chrono;
|
|
if(i != 0) cn_error.append(1, ',');
|
|
|
|
snprintf(buffer, sizeof(buffer), sJsonApiConnectionError,
|
|
int_port(duration_cast<seconds>(vMineResults[i].time.time_since_epoch()).count()),
|
|
vSocketLog[i].msg.c_str());
|
|
cn_error.append(buffer);
|
|
}
|
|
|
|
size_t bb_size = 2048 + hr_thds.size() + res_error.size() + cn_error.size();
|
|
std::unique_ptr<char[]> bigbuf( new char[ bb_size ] );
|
|
|
|
int bb_len = snprintf(bigbuf.get(), bb_size, sJsonApiFormat,
|
|
get_version_str().c_str(), hr_thds.c_str(), hr_buffer, a,
|
|
int_port(iPoolDiff), int_port(iGoodRes), int_port(iTotalRes), fAvgResTime, int_port(iPoolHashes),
|
|
int_port(iTopDiff[0]), int_port(iTopDiff[1]), int_port(iTopDiff[2]), int_port(iTopDiff[3]), int_port(iTopDiff[4]),
|
|
int_port(iTopDiff[5]), int_port(iTopDiff[6]), int_port(iTopDiff[7]), int_port(iTopDiff[8]), int_port(iTopDiff[9]),
|
|
res_error.c_str(), pool != nullptr ? pool->get_pool_addr() : "not connected", int_port(iConnSec), int_port(iPoolPing), cn_error.c_str());
|
|
|
|
out = std::string(bigbuf.get(), bigbuf.get() + bb_len);
|
|
}
|
|
|
|
void executor::http_report(ex_event_name ev)
|
|
{
|
|
assert(pHttpString != nullptr);
|
|
|
|
switch(ev)
|
|
{
|
|
case EV_HTML_HASHRATE:
|
|
http_hashrate_report(*pHttpString);
|
|
break;
|
|
|
|
case EV_HTML_RESULTS:
|
|
http_result_report(*pHttpString);
|
|
break;
|
|
|
|
case EV_HTML_CONNSTAT:
|
|
http_connection_report(*pHttpString);
|
|
break;
|
|
|
|
case EV_HTML_JSON:
|
|
http_json_report(*pHttpString);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
httpReady.set_value();
|
|
}
|
|
|
|
void executor::get_http_report(ex_event_name ev_id, std::string& data)
|
|
{
|
|
std::lock_guard<std::mutex> lck(httpMutex);
|
|
|
|
assert(pHttpString == nullptr);
|
|
assert(ev_id == EV_HTML_HASHRATE || ev_id == EV_HTML_RESULTS
|
|
|| ev_id == EV_HTML_CONNSTAT || ev_id == EV_HTML_JSON);
|
|
|
|
pHttpString = &data;
|
|
httpReady = std::promise<void>();
|
|
std::future<void> ready = httpReady.get_future();
|
|
|
|
push_event(ex_event(ev_id));
|
|
|
|
ready.wait();
|
|
pHttpString = nullptr;
|
|
}
|