687 lines
21 KiB
C++
687 lines
21 KiB
C++
/*
|
|
This file is part of progminer.
|
|
|
|
progminer 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
|
|
(at your option) any later version.
|
|
|
|
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include <libethcore/Farm.h>
|
|
|
|
#if ETH_ETHASHCL
|
|
#include <libethash-cl/CLMiner.h>
|
|
#endif
|
|
|
|
#if ETH_ETHASHCUDA
|
|
#include <libethash-cuda/CUDAMiner.h>
|
|
#endif
|
|
|
|
#if ETH_ETHASHCPU
|
|
#include <libethash-cpu/CPUMiner.h>
|
|
#endif
|
|
|
|
namespace dev
|
|
{
|
|
namespace eth
|
|
{
|
|
Farm* Farm::m_this = nullptr;
|
|
|
|
Farm::Farm(std::map<std::string, DeviceDescriptor>& _DevicesCollection,
|
|
FarmSettings _settings, CUSettings _CUSettings, CLSettings _CLSettings, CPSettings _CPSettings)
|
|
: m_Settings(std::move(_settings)),
|
|
m_CUSettings(std::move(_CUSettings)),
|
|
m_CLSettings(std::move(_CLSettings)),
|
|
m_CPSettings(std::move(_CPSettings)),
|
|
m_io_strand(g_io_service),
|
|
m_collectTimer(g_io_service),
|
|
m_DevicesCollection(_DevicesCollection)
|
|
{
|
|
m_this = this;
|
|
|
|
// Init HWMON if needed
|
|
if (m_Settings.hwMon)
|
|
{
|
|
m_telemetry.hwmon = true;
|
|
|
|
#if defined(__linux)
|
|
bool need_sysfsh = false;
|
|
#else
|
|
bool need_adlh = false;
|
|
#endif
|
|
bool need_nvmlh = false;
|
|
|
|
// Scan devices collection to identify which hw monitors to initialize
|
|
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
|
|
{
|
|
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cuda)
|
|
{
|
|
need_nvmlh = true;
|
|
continue;
|
|
}
|
|
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::OpenCL)
|
|
{
|
|
if (it->second.clPlatformType == ClPlatformTypeEnum::Nvidia)
|
|
{
|
|
need_nvmlh = true;
|
|
continue;
|
|
}
|
|
if (it->second.clPlatformType == ClPlatformTypeEnum::Amd)
|
|
{
|
|
#if defined(__linux)
|
|
need_sysfsh = true;
|
|
#else
|
|
need_adlh = true;
|
|
#endif
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(__linux)
|
|
if (need_sysfsh)
|
|
sysfsh = wrap_amdsysfs_create();
|
|
if (sysfsh)
|
|
{
|
|
// Build Pci identification mapping as done in miners.
|
|
for (int i = 0; i < sysfsh->sysfs_gpucount; i++)
|
|
{
|
|
std::ostringstream oss;
|
|
std::string uniqueId;
|
|
oss << std::setfill('0') << std::setw(2) << std::hex
|
|
<< (unsigned int)sysfsh->sysfs_pci_bus_id[i] << ":" << std::setw(2)
|
|
<< (unsigned int)(sysfsh->sysfs_pci_device_id[i]) << ".0";
|
|
uniqueId = oss.str();
|
|
map_amdsysfs_handle[uniqueId] = i;
|
|
}
|
|
}
|
|
|
|
#else
|
|
if (need_adlh)
|
|
adlh = wrap_adl_create();
|
|
if (adlh)
|
|
{
|
|
// Build Pci identification as done in miners.
|
|
for (int i = 0; i < adlh->adl_gpucount; i++)
|
|
{
|
|
std::ostringstream oss;
|
|
std::string uniqueId;
|
|
oss << std::setfill('0') << std::setw(2) << std::hex
|
|
<< (unsigned int)adlh->devs[adlh->phys_logi_device_id[i]].iBusNumber << ":"
|
|
<< std::setw(2)
|
|
<< (unsigned int)(adlh->devs[adlh->phys_logi_device_id[i]].iDeviceNumber)
|
|
<< ".0";
|
|
uniqueId = oss.str();
|
|
map_adl_handle[uniqueId] = i;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
if (need_nvmlh)
|
|
nvmlh = wrap_nvml_create();
|
|
if (nvmlh)
|
|
{
|
|
// Build Pci identification as done in miners.
|
|
for (int i = 0; i < nvmlh->nvml_gpucount; i++)
|
|
{
|
|
std::ostringstream oss;
|
|
std::string uniqueId;
|
|
oss << std::setfill('0') << std::setw(2) << std::hex
|
|
<< (unsigned int)nvmlh->nvml_pci_bus_id[i] << ":" << std::setw(2)
|
|
<< (unsigned int)(nvmlh->nvml_pci_device_id[i] >> 3) << ".0";
|
|
uniqueId = oss.str();
|
|
map_nvml_handle[uniqueId] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize nonce_scrambler
|
|
shuffle();
|
|
|
|
// Start data collector timer
|
|
// It should work for the whole lifetime of Farm
|
|
// regardless it's mining state
|
|
m_collectTimer.expires_from_now(boost::posix_time::milliseconds(m_collectInterval));
|
|
m_collectTimer.async_wait(
|
|
m_io_strand.wrap(boost::bind(&Farm::collectData, this, boost::asio::placeholders::error)));
|
|
}
|
|
|
|
Farm::~Farm()
|
|
{
|
|
// Stop data collector (before monitors !!!)
|
|
m_collectTimer.cancel();
|
|
|
|
// Deinit HWMON
|
|
#if defined(__linux)
|
|
if (sysfsh)
|
|
wrap_amdsysfs_destroy(sysfsh);
|
|
#else
|
|
if (adlh)
|
|
wrap_adl_destroy(adlh);
|
|
#endif
|
|
if (nvmlh)
|
|
wrap_nvml_destroy(nvmlh);
|
|
|
|
// Stop mining (if needed)
|
|
if (m_isMining.load(std::memory_order_relaxed))
|
|
stop();
|
|
}
|
|
|
|
/**
|
|
* @brief Randomizes the nonce scrambler
|
|
*/
|
|
void Farm::shuffle()
|
|
{
|
|
// Given that all nonces are equally likely to solve the problem
|
|
// we could reasonably always start the nonce search ranges
|
|
// at a fixed place, but that would be boring. Provide a once
|
|
// per run randomized start place, without creating much overhead.
|
|
random_device engine;
|
|
m_nonce_scrambler = uniform_int_distribution<uint64_t>()(engine);
|
|
}
|
|
|
|
void Farm::setWork(WorkPackage const& _newWp)
|
|
{
|
|
// Set work to each miner giving it's own starting nonce
|
|
Guard l(x_minerWork);
|
|
|
|
// Retrieve appropriate EpochContext
|
|
if (m_currentWp.epoch != _newWp.epoch)
|
|
{
|
|
ethash::epoch_context _ec = ethash::get_global_epoch_context(_newWp.epoch);
|
|
m_currentEc.epochNumber = _newWp.epoch;
|
|
m_currentEc.lightNumItems = _ec.light_cache_num_items;
|
|
m_currentEc.lightSize = ethash::get_light_cache_size(_ec.light_cache_num_items);
|
|
m_currentEc.dagNumItems = ethash::calculate_full_dataset_num_items(_newWp.epoch);
|
|
m_currentEc.dagSize = ethash::get_full_dataset_size(m_currentEc.dagNumItems);
|
|
m_currentEc.lightCache = _ec.light_cache;
|
|
|
|
for (auto const& miner : m_miners)
|
|
miner->setEpoch(m_currentEc);
|
|
}
|
|
|
|
m_currentWp = _newWp;
|
|
|
|
// Check if we need to shuffle per work (ergodicity == 2)
|
|
if (m_Settings.ergodicity == 2 && m_currentWp.exSizeBytes == 0)
|
|
shuffle();
|
|
|
|
uint64_t _startNonce;
|
|
if (m_currentWp.exSizeBytes > 0)
|
|
{
|
|
// Equally divide the residual segment among miners
|
|
_startNonce = m_currentWp.startNonce;
|
|
m_nonce_segment_with =
|
|
(unsigned int)log2(pow(2, 64 - (m_currentWp.exSizeBytes * 4)) / m_miners.size());
|
|
}
|
|
else
|
|
{
|
|
// Get the randomly selected nonce
|
|
_startNonce = m_nonce_scrambler;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < m_miners.size(); i++)
|
|
{
|
|
m_currentWp.startNonce = _startNonce + ((uint64_t)i << m_nonce_segment_with);
|
|
m_miners.at(i)->setWork(m_currentWp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Start a number of miners.
|
|
*/
|
|
bool Farm::start()
|
|
{
|
|
// Prevent recursion
|
|
if (m_isMining.load(std::memory_order_relaxed))
|
|
return true;
|
|
|
|
Guard l(x_minerWork);
|
|
|
|
// Start all subscribed miners if none yet
|
|
if (!m_miners.size())
|
|
{
|
|
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
|
|
{
|
|
TelemetryAccountType minerTelemetry;
|
|
#if ETH_ETHASHCUDA
|
|
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cuda)
|
|
{
|
|
minerTelemetry.prefix = "cu";
|
|
m_miners.push_back(std::shared_ptr<Miner>(
|
|
new CUDAMiner(m_miners.size(), m_CUSettings, it->second)));
|
|
}
|
|
#endif
|
|
#if ETH_ETHASHCL
|
|
|
|
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::OpenCL)
|
|
{
|
|
minerTelemetry.prefix = "cl";
|
|
m_miners.push_back(std::shared_ptr<Miner>(
|
|
new CLMiner(m_miners.size(), m_CLSettings, it->second)));
|
|
}
|
|
#endif
|
|
#if ETH_ETHASHCPU
|
|
|
|
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cpu)
|
|
{
|
|
minerTelemetry.prefix = "cp";
|
|
m_miners.push_back(std::shared_ptr<Miner>(
|
|
new CPUMiner(m_miners.size(), m_CPSettings, it->second)));
|
|
}
|
|
#endif
|
|
if (minerTelemetry.prefix.empty())
|
|
continue;
|
|
m_telemetry.miners.push_back(minerTelemetry);
|
|
m_miners.back()->startWorking();
|
|
}
|
|
|
|
// Initialize DAG Load mode
|
|
Miner::setDagLoadInfo(m_Settings.dagLoadMode, (unsigned int)m_miners.size());
|
|
|
|
m_isMining.store(true, std::memory_order_relaxed);
|
|
}
|
|
else
|
|
{
|
|
for (auto const& miner : m_miners)
|
|
miner->startWorking();
|
|
m_isMining.store(true, std::memory_order_relaxed);
|
|
}
|
|
|
|
return m_isMining.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
/**
|
|
* @brief Stop all mining activities.
|
|
*/
|
|
void Farm::stop()
|
|
{
|
|
// Avoid re-entering if not actually mining.
|
|
// This, in fact, is also called by destructor
|
|
if (isMining())
|
|
{
|
|
{
|
|
Guard l(x_minerWork);
|
|
for (auto const& miner : m_miners)
|
|
{
|
|
miner->triggerStopWorking();
|
|
miner->kick_miner();
|
|
}
|
|
|
|
m_miners.clear();
|
|
m_isMining.store(false, std::memory_order_relaxed);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Pauses the whole collection of miners
|
|
*/
|
|
void Farm::pause()
|
|
{
|
|
// Signal each miner to suspend mining
|
|
Guard l(x_minerWork);
|
|
m_paused.store(true, std::memory_order_relaxed);
|
|
for (auto const& m : m_miners)
|
|
m->pause(MinerPauseEnum::PauseDueToFarmPaused);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns whether or not this farm is paused for any reason
|
|
*/
|
|
bool Farm::paused()
|
|
{
|
|
return m_paused.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
/**
|
|
* @brief Resumes from a pause condition
|
|
*/
|
|
void Farm::resume()
|
|
{
|
|
// Signal each miner to resume mining
|
|
// Note ! Miners may stay suspended if other reasons
|
|
Guard l(x_minerWork);
|
|
m_paused.store(false, std::memory_order_relaxed);
|
|
for (auto const& m : m_miners)
|
|
m->resume(MinerPauseEnum::PauseDueToFarmPaused);
|
|
}
|
|
|
|
/**
|
|
* @brief Stop all mining activities and Starts them again
|
|
*/
|
|
void Farm::restart()
|
|
{
|
|
if (m_onMinerRestart)
|
|
m_onMinerRestart();
|
|
}
|
|
|
|
/**
|
|
* @brief Stop all mining activities and Starts them again (async post)
|
|
*/
|
|
void Farm::restart_async()
|
|
{
|
|
g_io_service.post(m_io_strand.wrap(boost::bind(&Farm::restart, this)));
|
|
}
|
|
|
|
/**
|
|
* @brief Spawn a reboot script (reboot.bat/reboot.sh)
|
|
* @return false if no matching file was found
|
|
*/
|
|
bool Farm::reboot(const std::vector<std::string>& args)
|
|
{
|
|
#if defined(_WIN32)
|
|
const char* filename = "reboot.bat";
|
|
#else
|
|
const char* filename = "reboot.sh";
|
|
#endif
|
|
|
|
return spawn_file_in_bin_dir(filename, args);
|
|
}
|
|
|
|
/**
|
|
* @brief Account solutions for miner and for farm
|
|
*/
|
|
void Farm::accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting)
|
|
{
|
|
if (_accounting == SolutionAccountingEnum::Accepted)
|
|
{
|
|
m_telemetry.farm.solutions.accepted++;
|
|
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
|
m_telemetry.miners.at(_minerIdx).solutions.accepted++;
|
|
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
|
return;
|
|
}
|
|
if (_accounting == SolutionAccountingEnum::Wasted)
|
|
{
|
|
m_telemetry.farm.solutions.wasted++;
|
|
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
|
m_telemetry.miners.at(_minerIdx).solutions.wasted++;
|
|
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
|
return;
|
|
}
|
|
if (_accounting == SolutionAccountingEnum::Rejected)
|
|
{
|
|
m_telemetry.farm.solutions.rejected++;
|
|
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
|
m_telemetry.miners.at(_minerIdx).solutions.rejected++;
|
|
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
|
return;
|
|
}
|
|
if (_accounting == SolutionAccountingEnum::Failed)
|
|
{
|
|
m_telemetry.farm.solutions.failed++;
|
|
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
|
|
m_telemetry.miners.at(_minerIdx).solutions.failed++;
|
|
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the solutions account for the whole farm
|
|
*/
|
|
|
|
SolutionAccountType Farm::getSolutions()
|
|
{
|
|
return m_telemetry.farm.solutions;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the solutions account for single miner
|
|
*/
|
|
SolutionAccountType Farm::getSolutions(unsigned _minerIdx)
|
|
{
|
|
try
|
|
{
|
|
return m_telemetry.miners.at(_minerIdx).solutions;
|
|
}
|
|
catch (const std::exception&)
|
|
{
|
|
return SolutionAccountType();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the description of segments each miner is working on
|
|
* @return a JsonObject
|
|
*/
|
|
Json::Value Farm::get_nonce_scrambler_json()
|
|
{
|
|
Json::Value jRes;
|
|
jRes["start_nonce"] = toHex(m_nonce_scrambler, HexPrefix::Add);
|
|
jRes["device_width"] = m_nonce_segment_with;
|
|
jRes["device_count"] = (uint64_t)m_miners.size();
|
|
|
|
return jRes;
|
|
}
|
|
|
|
void Farm::setTStartTStop(unsigned tstart, unsigned tstop)
|
|
{
|
|
m_Settings.tempStart = tstart;
|
|
m_Settings.tempStop = tstop;
|
|
}
|
|
|
|
void Farm::submitProof(Solution const& _s)
|
|
{
|
|
g_io_service.post(m_io_strand.wrap(boost::bind(&Farm::submitProofAsync, this, _s)));
|
|
}
|
|
|
|
void Farm::submitProofAsync(Solution const& _s)
|
|
{
|
|
#ifdef DEV_BUILD
|
|
const bool dbuild = true;
|
|
#else
|
|
const bool dbuild = false;
|
|
#endif
|
|
if (!m_Settings.noEval || dbuild)
|
|
{
|
|
Result r = EthashAux::eval(_s.work.epoch, _s.work.block, _s.work.header, _s.nonce);
|
|
if (r.value > _s.work.boundary)
|
|
{
|
|
accountSolution(_s.midx, SolutionAccountingEnum::Failed);
|
|
cwarn << "GPU " << _s.midx
|
|
<< " gave incorrect result. Lower overclocking values if it happens frequently.";
|
|
return;
|
|
}
|
|
if (dbuild && (_s.mixHash != r.mixHash))
|
|
cwarn << "GPU " << _s.midx << " mix missmatch";
|
|
m_onSolutionFound(Solution{_s.nonce, r.mixHash, _s.work, _s.tstamp, _s.midx});
|
|
}
|
|
else
|
|
m_onSolutionFound(_s);
|
|
|
|
#ifdef DEV_BUILD
|
|
if (g_logOptions & LOG_SUBMIT)
|
|
cnote << "Submit time: "
|
|
<< std::chrono::duration_cast<std::chrono::microseconds>(
|
|
std::chrono::steady_clock::now() - _s.tstamp)
|
|
.count()
|
|
<< " us.";
|
|
#endif
|
|
}
|
|
|
|
// Collects data about hashing and hardware status
|
|
void Farm::collectData(const boost::system::error_code& ec)
|
|
{
|
|
if (ec)
|
|
return;
|
|
|
|
// Reset hashrate (it will accumulate from miners)
|
|
float farm_hr = 0.0f;
|
|
|
|
// Process miners
|
|
for (auto const& miner : m_miners)
|
|
{
|
|
int minerIdx = miner->Index();
|
|
float hr = (miner->paused() ? 0.0f : miner->RetrieveHashRate());
|
|
farm_hr += hr;
|
|
m_telemetry.miners.at(minerIdx).hashrate = hr;
|
|
m_telemetry.miners.at(minerIdx).paused = miner->paused();
|
|
|
|
|
|
if (m_Settings.hwMon)
|
|
{
|
|
HwMonitorInfo hwInfo = miner->hwmonInfo();
|
|
|
|
unsigned int tempC = 0, fanpcnt = 0, powerW = 0;
|
|
|
|
if (hwInfo.deviceType == HwMonitorInfoType::NVIDIA && nvmlh)
|
|
{
|
|
int devIdx = hwInfo.deviceIndex;
|
|
if (devIdx == -1 && !hwInfo.devicePciId.empty())
|
|
{
|
|
if (map_nvml_handle.find(hwInfo.devicePciId) != map_nvml_handle.end())
|
|
{
|
|
devIdx = map_nvml_handle[hwInfo.devicePciId];
|
|
miner->setHwmonDeviceIndex(devIdx);
|
|
}
|
|
else
|
|
{
|
|
// This will prevent further tries to map
|
|
miner->setHwmonDeviceIndex(-2);
|
|
}
|
|
}
|
|
|
|
if (devIdx >= 0)
|
|
{
|
|
wrap_nvml_get_tempC(nvmlh, devIdx, &tempC);
|
|
wrap_nvml_get_fanpcnt(nvmlh, devIdx, &fanpcnt);
|
|
|
|
if (m_Settings.hwMon == 2)
|
|
wrap_nvml_get_power_usage(nvmlh, devIdx, &powerW);
|
|
}
|
|
}
|
|
else if (hwInfo.deviceType == HwMonitorInfoType::AMD)
|
|
{
|
|
#if defined(__linux)
|
|
if (sysfsh)
|
|
{
|
|
int devIdx = hwInfo.deviceIndex;
|
|
if (devIdx == -1 && !hwInfo.devicePciId.empty())
|
|
{
|
|
if (map_amdsysfs_handle.find(hwInfo.devicePciId) !=
|
|
map_amdsysfs_handle.end())
|
|
{
|
|
devIdx = map_amdsysfs_handle[hwInfo.devicePciId];
|
|
miner->setHwmonDeviceIndex(devIdx);
|
|
}
|
|
else
|
|
{
|
|
// This will prevent further tries to map
|
|
miner->setHwmonDeviceIndex(-2);
|
|
}
|
|
}
|
|
|
|
if (devIdx >= 0)
|
|
{
|
|
wrap_amdsysfs_get_tempC(sysfsh, devIdx, &tempC);
|
|
wrap_amdsysfs_get_fanpcnt(sysfsh, devIdx, &fanpcnt);
|
|
|
|
if (m_Settings.hwMon == 2)
|
|
wrap_amdsysfs_get_power_usage(sysfsh, devIdx, &powerW);
|
|
}
|
|
}
|
|
#else
|
|
if (adlh) // Windows only for AMD
|
|
{
|
|
int devIdx = hwInfo.deviceIndex;
|
|
if (devIdx == -1 && !hwInfo.devicePciId.empty())
|
|
{
|
|
if (map_adl_handle.find(hwInfo.devicePciId) != map_adl_handle.end())
|
|
{
|
|
devIdx = map_adl_handle[hwInfo.devicePciId];
|
|
miner->setHwmonDeviceIndex(devIdx);
|
|
}
|
|
else
|
|
{
|
|
// This will prevent further tries to map
|
|
miner->setHwmonDeviceIndex(-2);
|
|
}
|
|
}
|
|
|
|
if (devIdx >= 0)
|
|
{
|
|
wrap_adl_get_tempC(adlh, devIdx, &tempC);
|
|
wrap_adl_get_fanpcnt(adlh, devIdx, &fanpcnt);
|
|
|
|
if (m_Settings.hwMon == 2)
|
|
wrap_adl_get_power_usage(adlh, devIdx, &powerW);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// If temperature control has been enabled call
|
|
// check threshold
|
|
if (m_Settings.tempStop)
|
|
{
|
|
bool paused = miner->pauseTest(MinerPauseEnum::PauseDueToOverHeating);
|
|
if (!paused && (tempC >= m_Settings.tempStop))
|
|
miner->pause(MinerPauseEnum::PauseDueToOverHeating);
|
|
if (paused && (tempC <= m_Settings.tempStart))
|
|
miner->resume(MinerPauseEnum::PauseDueToOverHeating);
|
|
}
|
|
|
|
m_telemetry.miners.at(minerIdx).sensors.tempC = tempC;
|
|
m_telemetry.miners.at(minerIdx).sensors.fanP = fanpcnt;
|
|
m_telemetry.miners.at(minerIdx).sensors.powerW = powerW / ((double)1000.0);
|
|
}
|
|
m_telemetry.farm.hashrate = farm_hr;
|
|
miner->TriggerHashRateUpdate();
|
|
}
|
|
|
|
// Resubmit timer for another loop
|
|
m_collectTimer.expires_from_now(boost::posix_time::milliseconds(m_collectInterval));
|
|
m_collectTimer.async_wait(
|
|
m_io_strand.wrap(boost::bind(&Farm::collectData, this, boost::asio::placeholders::error)));
|
|
}
|
|
|
|
bool Farm::spawn_file_in_bin_dir(const char* filename, const std::vector<std::string>& args)
|
|
{
|
|
std::string fn = boost::dll::program_location().parent_path().string() +
|
|
"/" + // boost::filesystem::path::preferred_separator
|
|
filename;
|
|
try
|
|
{
|
|
if (!boost::filesystem::exists(fn))
|
|
return false;
|
|
|
|
/* anything in the file */
|
|
if (!boost::filesystem::file_size(fn))
|
|
return false;
|
|
|
|
#if defined(__linux)
|
|
struct stat sb;
|
|
if (stat(fn.c_str(), &sb) != 0)
|
|
return false;
|
|
/* just check if any exec flag is set.
|
|
still execution can fail (not the uid, not in the group, selinux, ...)
|
|
*/
|
|
if ((sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
|
|
return false;
|
|
#endif
|
|
/* spawn it (no wait,...) - fire and forget! */
|
|
boost::process::spawn(fn, args);
|
|
return true;
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
} // namespace eth
|
|
} // namespace dev
|