<commit_before>// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "main.h"
#include "db.h"
#include "txdb.h"
#include "init.h"
#include "miner.h"
#include "bitcoinrpc.h"

using namespace json_spirit;
using namespace std;

Value getsubsidy(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getsubsidy [nTarget]\n"
            "Returns proof-of-work subsidy value for the specified value of target.");

    return (uint64_t)GetProofOfWorkReward(0);
}

Value getmininginfo(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 0)
        throw runtime_error(
            "getmininginfo\n"
            "Returns an object containing mining-related information.");

    uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
    pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);

    Object obj, diff, weight;
    obj.push_back(Pair("blocks",        (int)nBestHeight));
    obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize));
    obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx));

    diff.push_back(Pair("proof-of-work",        GetDifficulty()));
    diff.push_back(Pair("proof-of-stake",       GetDifficulty(GetLastBlockIndex(pindexBest, true))));
    diff.push_back(Pair("search-interval",      (int)nLastCoinStakeSearchInterval));
    obj.push_back(Pair("difficulty",    diff));

    obj.push_back(Pair("blockvalue",    (uint64_t)GetProofOfWorkReward(0)));
    obj.push_back(Pair("netmhashps",     GetPoWMHashPS()));
    obj.push_back(Pair("netstakeweight", GetPoSKernelPS()));
    obj.push_back(Pair("errors",        GetWarnings("statusbar")));
    obj.push_back(Pair("pooledtx",      (uint64_t)mempool.size()));

    weight.push_back(Pair("minimum",    (uint64_t)nMinWeight));
    weight.push_back(Pair("maximum",    (uint64_t)nMaxWeight));
    weight.push_back(Pair("combined",  (uint64_t)nWeight));
    obj.push_back(Pair("stakeweight", weight));

    obj.push_back(Pair("stakeinterest",    (uint64_t)COIN_YEAR_REWARD));
    obj.push_back(Pair("testnet",       fTestNet));
    return obj;
}

Value getstakinginfo(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 0)
        throw runtime_error(
            "getstakinginfo\n"
            "Returns an object containing staking-related information.");

    uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
    pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);

    uint64_t nNetworkWeight = GetPoSKernelPS();
    bool staking = nLastCoinStakeSearchInterval && nWeight;
    int nExpectedTime = staking ? (nTargetSpacing * nNetworkWeight / nWeight) : -1;

    Object obj;

    obj.push_back(Pair("enabled", GetBoolArg("-staking", true)));
    obj.push_back(Pair("staking", staking));
    obj.push_back(Pair("errors", GetWarnings("statusbar")));

    obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
    obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
    obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));

    obj.push_back(Pair("difficulty", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
    obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval));

    obj.push_back(Pair("weight", (uint64_t)nWeight));
    obj.push_back(Pair("netstakeweight", (uint64_t)nNetworkWeight));

    obj.push_back(Pair("expectedtime", nExpectedTime));

    return obj;
}

Value getworkex(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 2)
        throw runtime_error(
            "getworkex [data, coinbase]\n"
            "If [data, coinbase] is not specified, returns extended work data.\n"
        );

    if (vNodes.empty())
        throw JSONRPCError(-9, "Zorbacoin is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(-10, "Zorbacoin is downloading blocks...");

    if (pindexBest->nHeight >= LAST_POW_BLOCK)
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
    static mapNewBlock_t mapNewBlock;
    static vector<CBlock*> vNewBlock;
    static CReserveKey reservekey(pwalletMain);

    if (params.size() == 0)
    {
        // Update block
        static unsigned int nTransactionsUpdatedLast;
        static CBlockIndex* pindexPrev;
        static int64_t nStart;
        static CBlock* pblock;
        if (pindexPrev != pindexBest ||
            (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
        {
            if (pindexPrev != pindexBest)
            {
                // Deallocate old blocks since they're obsolete now
                mapNewBlock.clear();
                BOOST_FOREACH(CBlock* pblock, vNewBlock)
                    delete pblock;
                vNewBlock.clear();
            }
            nTransactionsUpdatedLast = nTransactionsUpdated;
            pindexPrev = pindexBest;
            nStart = GetTime();

            // Create new block
            pblock = CreateNewBlock(pwalletMain);
            if (!pblock)
                throw JSONRPCError(-7, "Out of memory");
            vNewBlock.push_back(pblock);
        }

        // Update nTime
        pblock->nTime = max(pindexPrev->GetPastTimeLimit()+1, GetAdjustedTime());
        pblock->nNonce = 0;

        // Update nExtraNonce
        static unsigned int nExtraNonce = 0;
        IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        // Save
        mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);

        // Prebuild hash buffers
        char pmidstate[32];
        char pdata[128];
        char phash1[64];
        FormatHashBuffers(pblock, pmidstate, pdata, phash1);

        uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();

        CTransaction coinbaseTx = pblock->vtx[0];
        std::vector<uint256> merkle = pblock->GetMerkleBranch(0);

        Object result;
        result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
        result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));

        CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
        ssTx << coinbaseTx;
        result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end())));

        Array merkle_arr;

        BOOST_FOREACH(uint256 merkleh, merkle) {
            merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh)));
        }

        result.push_back(Pair("merkle", merkle_arr));

        return result;
    }
    else
    {
        // Parse parameters
        vector<unsigned char> vchData = ParseHex(params[0].get_str());
        vector<unsigned char> coinbase;

        if(params.size() == 2)
            coinbase = ParseHex(params[1].get_str());

        if (vchData.size() != 128)
            throw JSONRPCError(-8, "Invalid parameter");

        CBlock* pdata = (CBlock*)&vchData[0];

        // Byte reverse
        for (int i = 0; i < 128/4; i++)
            ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);

        // Get saved block
        if (!mapNewBlock.count(pdata->hashMerkleRoot))
            return false;
        CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;

        pblock->nTime = pdata->nTime;
        pblock->nNonce = pdata->nNonce;

        if(coinbase.size() == 0)
            pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
        else
            CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - HACK!

        pblock->hashMerkleRoot = pblock->BuildMerkleTree();

        return CheckWork(pblock, *pwalletMain, reservekey);
    }
}

Value getwork(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getwork [data]\n"
            "If [data] is not specified, returns formatted hash data to work on:\n"
            "  \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
            "  \"data\" : block data\n"
            "  \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
            "  \"target\" : little endian hash target\n"
            "If [data] is specified, tries to solve the block and returns true if it was successful.");

    if (vNodes.empty())
        throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Zorbacoin is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zorbacoin is downloading blocks...");

    if (pindexBest->nHeight >= LAST_POW_BLOCK)
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
    static mapNewBlock_t mapNewBlock;    // FIXME: thread safety
    static vector<CBlock*> vNewBlock;
    static CReserveKey reservekey(pwalletMain);

    if (params.size() == 0)
    {
        // Update block
        static unsigned int nTransactionsUpdatedLast;
        static CBlockIndex* pindexPrev;
        static int64_t nStart;
        static CBlock* pblock;
        if (pindexPrev != pindexBest ||
            (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
        {
            if (pindexPrev != pindexBest)
            {
                // Deallocate old blocks since they're obsolete now
                mapNewBlock.clear();
                BOOST_FOREACH(CBlock* pblock, vNewBlock)
                    delete pblock;
                vNewBlock.clear();
            }

            // Clear pindexPrev so future getworks make a new block, despite any failures from here on
            pindexPrev = NULL;

            // Store the pindexBest used before CreateNewBlock, to avoid races
            nTransactionsUpdatedLast = nTransactionsUpdated;
            CBlockIndex* pindexPrevNew = pindexBest;
            nStart = GetTime();

            // Create new block
            pblock = CreateNewBlock(pwalletMain);
            if (!pblock)
                throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
            vNewBlock.push_back(pblock);

            // Need to update only after we know CreateNewBlock succeeded
            pindexPrev = pindexPrevNew;
        }

        // Update nTime
        pblock->UpdateTime(pindexPrev);
        pblock->nNonce = 0;

        // Update nExtraNonce
        static unsigned int nExtraNonce = 0;
        IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        // Save
        mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);

        // Pre-build hash buffers
        char pmidstate[32];
        char pdata[128];
        char phash1[64];
        FormatHashBuffers(pblock, pmidstate, pdata, phash1);

        uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();

        Object result;
        result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
        result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
        result.push_back(Pair("hash1",    HexStr(BEGIN(phash1), END(phash1)))); // deprecated
        result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));
        return result;
    }
    else
    {
        // Parse parameters
        vector<unsigned char> vchData = ParseHex(params[0].get_str());
        if (vchData.size() != 128)
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
        CBlock* pdata = (CBlock*)&vchData[0];

        // Byte reverse
        for (int i = 0; i < 128/4; i++)
            ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);

        // Get saved block
        if (!mapNewBlock.count(pdata->hashMerkleRoot))
            return false;
        CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;

        pblock->nTime = pdata->nTime;
        pblock->nNonce = pdata->nNonce;
        pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
        pblock->hashMerkleRoot = pblock->BuildMerkleTree();

        return CheckWork(pblock, *pwalletMain, reservekey);
    }
}

Value getblocktemplate(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getblocktemplate [params]\n"
            "Returns data needed to construct a block to work on:\n"
            "  \"version\" : block version\n"
            "  \"previousblockhash\" : hash of current highest block\n"
            "  \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
            "  \"coinbaseaux\" : data that should be included in coinbase\n"
            "  \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
            "  \"target\" : hash target\n"
            "  \"mintime\" : minimum timestamp appropriate for next block\n"
            "  \"curtime\" : current timestamp\n"
            "  \"mutable\" : list of ways the block template may be changed\n"
            "  \"noncerange\" : range of valid nonces\n"
            "  \"sigoplimit\" : limit of sigops in blocks\n"
            "  \"sizelimit\" : limit of block size\n"
            "  \"bits\" : compressed target of next block\n"
            "  \"height\" : height of the next block\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");

    std::string strMode = "template";
    if (params.size() > 0)
    {
        const Object& oparam = params[0].get_obj();
        const Value& modeval = find_value(oparam, "mode");
        if (modeval.type() == str_type)
            strMode = modeval.get_str();
        else if (modeval.type() == null_type)
        {
            /* Do nothing */
        }
        else
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
    }

    if (strMode != "template")
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");

    if (vNodes.empty())
        throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Zorbacoin is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zorbacoin is downloading blocks...");

    if (pindexBest->nHeight >= LAST_POW_BLOCK)
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    static CReserveKey reservekey(pwalletMain);

    // Update block
    static unsigned int nTransactionsUpdatedLast;
    static CBlockIndex* pindexPrev;
    static int64_t nStart;
    static CBlock* pblock;
    if (pindexPrev != pindexBest ||
        (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
    {
        // Clear pindexPrev so future calls make a new block, despite any failures from here on
        pindexPrev = NULL;

        // Store the pindexBest used before CreateNewBlock, to avoid races
        nTransactionsUpdatedLast = nTransactionsUpdated;
        CBlockIndex* pindexPrevNew = pindexBest;
        nStart = GetTime();

        // Create new block
        if(pblock)
        {
            delete pblock;
            pblock = NULL;
        }
        pblock = CreateNewBlock(pwalletMain);
        if (!pblock)
            throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");

        // Need to update only after we know CreateNewBlock succeeded
        pindexPrev = pindexPrevNew;
    }

    // Update nTime
    pblock->UpdateTime(pindexPrev);
    pblock->nNonce = 0;

    Array transactions;
    map<uint256, int64_t> setTxIndex;
    int i = 0;
    CTxDB txdb("r");
    BOOST_FOREACH (CTransaction& tx, pblock->vtx)
    {
        uint256 txHash = tx.GetHash();
        setTxIndex[txHash] = i++;

        if (tx.IsCoinBase() || tx.IsCoinStake())
            continue;

        Object entry;

        CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
        ssTx << tx;
        entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));

        entry.push_back(Pair("hash", txHash.GetHex()));

        MapPrevTx mapInputs;
        map<uint256, CTxIndex> mapUnused;
        bool fInvalid = false;
        if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
        {
            entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut())));

            Array deps;
            BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs)
            {
                if (setTxIndex.count(inp.first))
                    deps.push_back(setTxIndex[inp.first]);
            }
            entry.push_back(Pair("depends", deps));

            int64_t nSigOps = tx.GetLegacySigOpCount();
            nSigOps += tx.GetP2SHSigOpCount(mapInputs);
            entry.push_back(Pair("sigops", nSigOps));
        }

        transactions.push_back(entry);
    }

    Object aux;
    aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));

    uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();

    static Array aMutable;
    if (aMutable.empty())
    {
        aMutable.push_back("time");
        aMutable.push_back("transactions");
        aMutable.push_back("prevblock");
    }

    Object result;
    result.push_back(Pair("version", pblock->nVersion));
    result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
    result.push_back(Pair("transactions", transactions));
    result.push_back(Pair("coinbaseaux", aux));
    result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
    result.push_back(Pair("target", hashTarget.GetHex()));
    result.push_back(Pair("mintime", (int64_t)pindexPrev->GetPastTimeLimit()+1));
    result.push_back(Pair("mutable", aMutable));
    result.push_back(Pair("noncerange", "00000000ffffffff"));
    result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
    result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
    result.push_back(Pair("curtime", (int64_t)pblock->nTime));
    result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
    result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));

    return result;
}

Value submitblock(const Array& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 2)
        throw runtime_error(
            "submitblock <hex data> [optional-params-obj]\n"
            "[optional-params-obj] parameter is currently ignored.\n"
            "Attempts to submit new block to network.\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");

    vector<unsigned char> blockData(ParseHex(params[0].get_str()));
    CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
    CBlock block;
    try {
        ssBlock >> block;
    }
    catch (std::exception &e) {
        throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
    }

    bool fAccepted = ProcessBlock(NULL, &block);
    if (!fAccepted)
        return "rejected";

    return Value::null;
}

<commit_msg>Delete rpcmining.cpp<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <scheduler.h>

//#include <random.h>
#include "util.h"
#include <reverselock.h>

#include <assert.h>
#include <utility>

CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false)
{
}

CScheduler::~CScheduler()
{
    assert(nThreadsServicingQueue == 0);
}

#if BOOST_VERSION < 105000
static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t)
{
    // Creating the posix_time using from_time_t loses sub-second precision. So rather than exporting the time_point to time_t,
    // start with a posix_time at the epoch (0) and add the milliseconds that have passed since then.
    return boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(boost::chrono::duration_cast<boost::chrono::milliseconds>(t.time_since_epoch()).count());
}
#endif

void CScheduler::serviceQueue()
{
    boost::unique_lock<boost::mutex> lock(newTaskMutex);
    ++nThreadsServicingQueue;

    // newTaskMutex is locked throughout this loop EXCEPT
    // when the thread is waiting or when the user's function
    // is called.
    while (!shouldStop()) {
        try {
            if (!shouldStop() && taskQueue.empty()) {
                reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
                // Use this chance to get more entropy - this is when more rebasing to Bitcoin is done...
                //RandAddSeedSleep();
                MilliSleep(1);
            }
            while (!shouldStop() && taskQueue.empty()) {
                // Wait until there is something to do.
                newTaskScheduled.wait(lock);
            }

            // Wait until either there is a new task, or until
            // the time of the first item on the queue:

// wait_until needs boost 1.50 or later; older versions have timed_wait:
#if BOOST_VERSION < 105000
            while (!shouldStop() && !taskQueue.empty() &&
                   newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) {
                // Keep waiting until timeout
            }
#else
            // Some boost versions have a conflicting overload of wait_until that returns void.
            // Explicitly use a template here to avoid hitting that overload.
            while (!shouldStop() && !taskQueue.empty()) {
                boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;
                try {
                    if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout) {
                        break; // Exit loop after timeout, it means we reached the time of the event
                    }
                } catch (boost::thread_interrupted) {
                    // We need to make sure we don't ignore this, or the thread won't end
                    throw;
                } catch (...) {
                    // Some boost versions have a bug that can cause a time prior to system boot (or wake from sleep) to throw an exception instead of return timeout
                    // See https://github.com/boostorg/thread/issues/308
                    // Check if the time has passed and, if so, break gracefully
                    if (timeToWaitFor <= boost::chrono::system_clock::now()) break;
                    throw;
                }
            }
#endif
            // If there are multiple threads, the queue can empty while we're waiting (another
            // thread may service the task we were waiting on).
            if (shouldStop() || taskQueue.empty())
                continue;

            Function f = taskQueue.begin()->second;
            taskQueue.erase(taskQueue.begin());

            {
                // Unlock before calling f, so it can reschedule itself or another task
                // without deadlocking:
                reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
                f();
            }
        } catch (...) {
            --nThreadsServicingQueue;
            throw;
        }
    }
    --nThreadsServicingQueue;
    newTaskScheduled.notify_one();
}

void CScheduler::stop(bool drain)
{
    {
        boost::unique_lock<boost::mutex> lock(newTaskMutex);
        if (drain)
            stopWhenEmpty = true;
        else
            stopRequested = true;
    }
    newTaskScheduled.notify_all();
}

void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t)
{
    {
        boost::unique_lock<boost::mutex> lock(newTaskMutex);
        taskQueue.insert(std::make_pair(t, f));
    }
    newTaskScheduled.notify_one();
}

void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds)
{
    schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds));
}

static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds)
{
    f();
    s->scheduleFromNow(std::bind(&Repeat, s, f, deltaMilliSeconds), deltaMilliSeconds);
}

void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds)
{
    scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds);
}

size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
                             boost::chrono::system_clock::time_point &last) const
{
    boost::unique_lock<boost::mutex> lock(newTaskMutex);
    size_t result = taskQueue.size();
    if (!taskQueue.empty()) {
        first = taskQueue.begin()->first;
        last = taskQueue.rbegin()->first;
    }
    return result;
}

bool CScheduler::AreThreadsServicingQueue() const {
    boost::unique_lock<boost::mutex> lock(newTaskMutex);
    return nThreadsServicingQueue;
}

void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
    {
        LOCK(m_cs_callbacks_pending);
        // Try to avoid scheduling too many copies here, but if we
        // accidentally have two ProcessQueue's scheduled at once its
        // not a big deal.
        if (m_are_callbacks_running) return;
        if (m_callbacks_pending.empty()) return;
    }
    m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this));
}

void SingleThreadedSchedulerClient::ProcessQueue() {
    std::function<void ()> callback;
    {
        LOCK(m_cs_callbacks_pending);
        if (m_are_callbacks_running) return;
        if (m_callbacks_pending.empty()) return;
        m_are_callbacks_running = true;

        callback = std::move(m_callbacks_pending.front());
        m_callbacks_pending.pop_front();
    }

    // RAII the setting of fCallbacksRunning and calling MaybeScheduleProcessQueue
    // to ensure both happen safely even if callback() throws.
    struct RAIICallbacksRunning {
        SingleThreadedSchedulerClient* instance;
        explicit RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {}
        ~RAIICallbacksRunning() {
            {
                LOCK(instance->m_cs_callbacks_pending);
                instance->m_are_callbacks_running = false;
            }
            instance->MaybeScheduleProcessQueue();
        }
    } raiicallbacksrunning(this);

    callback();
}

void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> func) {
    assert(m_pscheduler);

    {
        LOCK(m_cs_callbacks_pending);
        m_callbacks_pending.emplace_back(std::move(func));
    }
    MaybeScheduleProcessQueue();
}

void SingleThreadedSchedulerClient::EmptyQueue() {
    assert(!m_pscheduler->AreThreadsServicingQueue());
    bool should_continue = true;
    while (should_continue) {
        ProcessQueue();
        LOCK(m_cs_callbacks_pending);
        should_continue = !m_callbacks_pending.empty();
    }
}

size_t SingleThreadedSchedulerClient::CallbacksPending() {
    LOCK(m_cs_callbacks_pending);
    return m_callbacks_pending.size();
}
<commit_msg>timed_wait is deprecated<commit_after>// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <scheduler.h>

//#include <random.h>
#include "util.h"
#include <reverselock.h>

#include <assert.h>
#include <utility>

CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false)
{
}

CScheduler::~CScheduler()
{
    assert(nThreadsServicingQueue == 0);
}

#if BOOST_VERSION < 105000
static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t)
{
    // Creating the posix_time using from_time_t loses sub-second precision. So rather than exporting the time_point to time_t,
    // start with a posix_time at the epoch (0) and add the milliseconds that have passed since then.
    return boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(boost::chrono::duration_cast<boost::chrono::milliseconds>(t.time_since_epoch()).count());
}
#endif

void CScheduler::serviceQueue()
{
    boost::unique_lock<boost::mutex> lock(newTaskMutex);
    ++nThreadsServicingQueue;

    // newTaskMutex is locked throughout this loop EXCEPT
    // when the thread is waiting or when the user's function
    // is called.
    while (!shouldStop()) {
        try {
            if (!shouldStop() && taskQueue.empty()) {
                reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
                // Use this chance to get more entropy - this is when more rebasing to Bitcoin is done...
                //RandAddSeedSleep();
                MilliSleep(1);
            }
            while (!shouldStop() && taskQueue.empty()) {
                // Wait until there is something to do.
                newTaskScheduled.wait(lock);
            }

            // Wait until either there is a new task, or until
            // the time of the first item on the queue:

            // Some boost versions have a conflicting overload of wait_until that returns void.
            // Explicitly use a template here to avoid hitting that overload.
            while (!shouldStop() && !taskQueue.empty()) {
                boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;
                try {
                    if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout) {
                        break; // Exit loop after timeout, it means we reached the time of the event
                    }
                } catch (boost::thread_interrupted) {
                    // We need to make sure we don't ignore this, or the thread won't end
                    throw;
                } catch (...) {
                    // Some boost versions have a bug that can cause a time prior to system boot (or wake from sleep) to throw an exception instead of return timeout
                    // See https://github.com/boostorg/thread/issues/308
                    // Check if the time has passed and, if so, break gracefully
                    if (timeToWaitFor <= boost::chrono::system_clock::now()) break;
                    throw;
                }
            }

            // If there are multiple threads, the queue can empty while we're waiting (another
            // thread may service the task we were waiting on).
            if (shouldStop() || taskQueue.empty())
                continue;

            Function f = taskQueue.begin()->second;
            taskQueue.erase(taskQueue.begin());

            {
                // Unlock before calling f, so it can reschedule itself or another task
                // without deadlocking:
                reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
                f();
            }
        } catch (...) {
            --nThreadsServicingQueue;
            throw;
        }
    }
    --nThreadsServicingQueue;
    newTaskScheduled.notify_one();
}

void CScheduler::stop(bool drain)
{
    {
        boost::unique_lock<boost::mutex> lock(newTaskMutex);
        if (drain)
            stopWhenEmpty = true;
        else
            stopRequested = true;
    }
    newTaskScheduled.notify_all();
}

void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t)
{
    {
        boost::unique_lock<boost::mutex> lock(newTaskMutex);
        taskQueue.insert(std::make_pair(t, f));
    }
    newTaskScheduled.notify_one();
}

void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds)
{
    schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds));
}

static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds)
{
    f();
    s->scheduleFromNow(std::bind(&Repeat, s, f, deltaMilliSeconds), deltaMilliSeconds);
}

void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds)
{
    scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds);
}

size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
                             boost::chrono::system_clock::time_point &last) const
{
    boost::unique_lock<boost::mutex> lock(newTaskMutex);
    size_t result = taskQueue.size();
    if (!taskQueue.empty()) {
        first = taskQueue.begin()->first;
        last = taskQueue.rbegin()->first;
    }
    return result;
}

bool CScheduler::AreThreadsServicingQueue() const {
    boost::unique_lock<boost::mutex> lock(newTaskMutex);
    return nThreadsServicingQueue;
}

void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
    {
        LOCK(m_cs_callbacks_pending);
        // Try to avoid scheduling too many copies here, but if we
        // accidentally have two ProcessQueue's scheduled at once its
        // not a big deal.
        if (m_are_callbacks_running) return;
        if (m_callbacks_pending.empty()) return;
    }
    m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this));
}

void SingleThreadedSchedulerClient::ProcessQueue() {
    std::function<void ()> callback;
    {
        LOCK(m_cs_callbacks_pending);
        if (m_are_callbacks_running) return;
        if (m_callbacks_pending.empty()) return;
        m_are_callbacks_running = true;

        callback = std::move(m_callbacks_pending.front());
        m_callbacks_pending.pop_front();
    }

    // RAII the setting of fCallbacksRunning and calling MaybeScheduleProcessQueue
    // to ensure both happen safely even if callback() throws.
    struct RAIICallbacksRunning {
        SingleThreadedSchedulerClient* instance;
        explicit RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {}
        ~RAIICallbacksRunning() {
            {
                LOCK(instance->m_cs_callbacks_pending);
                instance->m_are_callbacks_running = false;
            }
            instance->MaybeScheduleProcessQueue();
        }
    } raiicallbacksrunning(this);

    callback();
}

void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> func) {
    assert(m_pscheduler);

    {
        LOCK(m_cs_callbacks_pending);
        m_callbacks_pending.emplace_back(std::move(func));
    }
    MaybeScheduleProcessQueue();
}

void SingleThreadedSchedulerClient::EmptyQueue() {
    assert(!m_pscheduler->AreThreadsServicingQueue());
    bool should_continue = true;
    while (should_continue) {
        ProcessQueue();
        LOCK(m_cs_callbacks_pending);
        should_continue = !m_callbacks_pending.empty();
    }
}

size_t SingleThreadedSchedulerClient::CallbacksPending() {
    LOCK(m_cs_callbacks_pending);
    return m_callbacks_pending.size();
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright 2014-2015 David Simmons-Duffin.
// Distributed under the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include "SDP_Solver_Parameters.hxx"
#include "Block_Info.hxx"
#include "../Timers.hxx"

#include <El.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>

Timers
solve(const Block_Info &block_info, const SDP_Solver_Parameters &parameters);

void write_timing(const boost::filesystem::path &checkpoint_out,
                  const Block_Info &block_info, const Timers &timers,
                  const bool &debug);

int main(int argc, char **argv)
{
  El::Environment env(argc, argv);

  try
    {
      SDP_Solver_Parameters parameters(argc, argv);
      if(!parameters.is_valid())
        {
          return 0;
        }

      El::gmp::SetPrecision(parameters.precision);
      if(parameters.verbosity >= Verbosity::regular && El::mpi::Rank() == 0)
        {
          std::cout << "SDPB started at "
                    << boost::posix_time::second_clock::local_time() << '\n'
                    << parameters << '\n';
        }

      Block_Info block_info(parameters.sdp_directory, parameters.checkpoint_in,
                            parameters.procs_per_node,
                            parameters.proc_granularity, parameters.verbosity);
      // Only generate a block_timings file if
      // 1) We are running in parallel
      // 2) We did not load a block_timings file
      // 3) We are not going to load a checkpoint.
      if(El::mpi::Size(El::mpi::COMM_WORLD) > 1
         && block_info.block_timings_filename.empty()
         && !exists(parameters.checkpoint_in
                    / ("checkpoint." + std::to_string(El::mpi::Rank()))))
        {
          if(parameters.verbosity >= Verbosity::regular
             && El::mpi::Rank() == 0)
            {
              std::cout << "Performing a timing run\n";
            }
          SDP_Solver_Parameters timing_parameters(parameters);
          timing_parameters.max_iterations = 2;
          timing_parameters.no_final_checkpoint = true;
          timing_parameters.checkpoint_interval
            = std::numeric_limits<int64_t>::max();
          timing_parameters.max_runtime
            = std::numeric_limits<int64_t>::max();
          timing_parameters.duality_gap_threshold=0;
          timing_parameters.primal_error_threshold=0;
          timing_parameters.dual_error_threshold=0;
          if(timing_parameters.verbosity != Verbosity::debug)
            {
              timing_parameters.verbosity = Verbosity::none;
            }
          Timers timers(solve(block_info, timing_parameters));

          write_timing(timing_parameters.checkpoint_out, block_info, timers,
                       timing_parameters.verbosity >= Verbosity::debug);
          El::mpi::Barrier(El::mpi::COMM_WORLD);
          block_info
            = Block_Info(parameters.sdp_directory, parameters.checkpoint_out,
                         parameters.procs_per_node,
                         parameters.proc_granularity, parameters.verbosity);

          parameters.max_runtime -= timers.front().second.elapsed_seconds();
        }
      else if(!block_info.block_timings_filename.empty()
              && block_info.block_timings_filename
                   != (parameters.checkpoint_out / "block_timings"))
        {
          if(El::mpi::Rank() == 0)
            {
              create_directories(parameters.checkpoint_out);
              copy_file(block_info.block_timings_filename,
                        parameters.checkpoint_out / "block_timings",
                        boost::filesystem::copy_option::overwrite_if_exists);
            }
        }
      solve(block_info, parameters);
    }
  catch(std::exception &e)
    {
      El::ReportException(e);
      El::mpi::Abort(El::mpi::COMM_WORLD, 1);
    }
  catch(...)
    {
      El::mpi::Abort(El::mpi::COMM_WORLD, 1);
    }
}
<commit_msg>Make decision to compute block timings consistent for all ranks<commit_after>//=======================================================================
// Copyright 2014-2015 David Simmons-Duffin.
// Distributed under the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include "SDP_Solver_Parameters.hxx"
#include "Block_Info.hxx"
#include "../Timers.hxx"

#include <El.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>

Timers
solve(const Block_Info &block_info, const SDP_Solver_Parameters &parameters);

void write_timing(const boost::filesystem::path &checkpoint_out,
                  const Block_Info &block_info, const Timers &timers,
                  const bool &debug);

int main(int argc, char **argv)
{
  El::Environment env(argc, argv);

  try
    {
      SDP_Solver_Parameters parameters(argc, argv);
      if(!parameters.is_valid())
        {
          return 0;
        }

      El::gmp::SetPrecision(parameters.precision);
      if(parameters.verbosity >= Verbosity::regular && El::mpi::Rank() == 0)
        {
          std::cout << "SDPB started at "
                    << boost::posix_time::second_clock::local_time() << '\n'
                    << parameters << '\n';
        }

      Block_Info block_info(parameters.sdp_directory, parameters.checkpoint_in,
                            parameters.procs_per_node,
                            parameters.proc_granularity, parameters.verbosity);
      // Only generate a block_timings file if
      // 1) We are running in parallel
      // 2) We did not load a block_timings file
      // 3) We are not going to load a checkpoint.
      if(El::mpi::Size(El::mpi::COMM_WORLD) > 1
         && block_info.block_timings_filename.empty()
         && !exists(parameters.checkpoint_in / "checkpoint.0"))
        {
          if(parameters.verbosity >= Verbosity::regular
             && El::mpi::Rank() == 0)
            {
              std::cout << "Performing a timing run\n";
            }
          SDP_Solver_Parameters timing_parameters(parameters);
          timing_parameters.max_iterations = 2;
          timing_parameters.no_final_checkpoint = true;
          timing_parameters.checkpoint_interval
            = std::numeric_limits<int64_t>::max();
          timing_parameters.max_runtime = std::numeric_limits<int64_t>::max();
          timing_parameters.duality_gap_threshold = 0;
          timing_parameters.primal_error_threshold = 0;
          timing_parameters.dual_error_threshold = 0;
          if(timing_parameters.verbosity != Verbosity::debug)
            {
              timing_parameters.verbosity = Verbosity::none;
            }
          Timers timers(solve(block_info, timing_parameters));

          write_timing(timing_parameters.checkpoint_out, block_info, timers,
                       timing_parameters.verbosity >= Verbosity::debug);
          El::mpi::Barrier(El::mpi::COMM_WORLD);
          block_info
            = Block_Info(parameters.sdp_directory, parameters.checkpoint_out,
                         parameters.procs_per_node,
                         parameters.proc_granularity, parameters.verbosity);

          parameters.max_runtime -= timers.front().second.elapsed_seconds();
        }
      else if(!block_info.block_timings_filename.empty()
              && block_info.block_timings_filename
                   != (parameters.checkpoint_out / "block_timings"))
        {
          if(El::mpi::Rank() == 0)
            {
              create_directories(parameters.checkpoint_out);
              copy_file(block_info.block_timings_filename,
                        parameters.checkpoint_out / "block_timings",
                        boost::filesystem::copy_option::overwrite_if_exists);
            }
        }
      solve(block_info, parameters);
    }
  catch(std::exception &e)
    {
      El::ReportException(e);
      El::mpi::Abort(El::mpi::COMM_WORLD, 1);
    }
  catch(...)
    {
      El::mpi::Abort(El::mpi::COMM_WORLD, 1);
    }
}
<|endoftext|>


<commit_before><commit_msg>ftrace_controller: Fix modulo math for computing drain delay<commit_after><|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 19.11.2018
/// @brief Kernel config subsystem implementation
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/defaults.h>
#include <bs/log.h>
#include <bs/kernel/radio.h>

#include "config_subsyst.h"
#include "radio_subsyst.h"

#include <caf/config_option_adder.hpp>
#include <caf/parser_state.hpp>
#include <caf/detail/ini_consumer.hpp>
#include <caf/detail/parser/read_ini.hpp>
#include <caf/detail/parser/read_string.hpp>
#include <caf/io/middleman.hpp>

#include <fmt/ostream.h>

#include <atomic>
#include <sstream>
#include <fstream>
#include <stdlib.h>

NAMESPACE_BEGIN(blue_sky::kernel::detail)
using namespace caf;
namespace fs = std::filesystem;
using string_list = config_subsyst::string_list;

// hidden helper functions
NAMESPACE_BEGIN()

struct ini_iter {
	std::istream* ini;
	char ch;

	explicit ini_iter(std::istream* istr) : ini(istr) {
		ini->get(ch);
	}

	ini_iter() : ini(nullptr), ch('\0') {
		// nop
	}

	ini_iter(const ini_iter&) = default;

	ini_iter& operator=(const ini_iter&) = default;

	inline char operator*() const {
		return ch;
	}

	inline ini_iter& operator++() {
		ini->get(ch);
		return *this;
	}
};

struct ini_sentinel { };

bool operator!=(ini_iter iter, ini_sentinel) {
	return !iter.ini->fail();
}

auto extract_config_file_path(caf::config_option_set& opts, caf::settings& S, string_list& args)
-> std::string {
	auto ptr = opts.qualified_name_lookup("global.config-file");
	CAF_ASSERT(ptr != nullptr);
	string_list::iterator i;
	string_view path;
	std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
	// [TODO] add CAF errors processing
	if(i == args.end())
		return "";
		//return none;
	if(path.empty()) {
		args.erase(i);
		return "";
		//return make_error(pec::missing_argument, std::string{*i});
	}
	if(auto evalue = ptr->parse(path); evalue) {
		put(S, "config-file", *evalue);
		ptr->store(*evalue);
		return caf::get<std::string>(*evalue);
	}
	return "";
	//return std::move(evalue.error());
}

// separetely store configured timeouts for faster access
// returns { normal timeout, long op timeout }
using timeouts_pair = std::pair<std::atomic<timespan>, std::atomic<timespan>>;
inline auto timeouts() -> timeouts_pair& {
	static auto ts_ = timeouts_pair{};
	return ts_;
}

auto reset_timeouts(timespan typical, timespan slow) -> void {
	auto& [normal_to, long_to] = timeouts();
	normal_to.store(typical, std::memory_order_relaxed);
	long_to.store(slow, std::memory_order_relaxed);
}

NAMESPACE_END() // eof hidden ns

/*-----------------------------------------------------------------------------
 *  kernel_config_subsyst impl
 *-----------------------------------------------------------------------------*/
config_subsyst::config_subsyst() {
	///////////////////////////////////////////////////////////////////////////////
	//  add config options
	//
	using opt_group = config_option_adder;
	opt_group{confopt_, "global"}
		.add<bool>("help,h?", "print help and exit")
		.add<bool>("long-help", "print all help options and exit")
		.add<bool>("dump-config", "print configuration in INI format and exit")
		.add<std::string>("config-file", "BS config file path")
	;
	opt_group{confopt_, "path"}
		.add<std::string>("kernel", "Path to blue-sky kernel library")
		.add<string_list>("plugins", "Paths list to blue-sky plugins")
	;
	opt_group{confopt_, "logger"}
		.add<std::string>("out-file-name", "Path to stdout log file")
		.add<std::string>("err-file-name", "Path to stderr log file")
		.add<std::uint64_t>("out-file-size", "Size of rotating stdout log file")
		.add<std::uint64_t>("err-file-size", "Size of rotating stderr log file")
		.add<std::string>("out-file-format", "Format of stdout log messages")
		.add<std::string>("err-file-format", "Format of stderr log messages")
		.add<std::string>("out-console-format", "Format of stdout log messages in console")
		.add<std::string>("err-console-format", "Format of stdout log messages in console")
		.add<std::uint8_t>("out-flush-level", "Minimum message level that triggers out log flush")
		.add<std::uint8_t>("err-flush-level", "Minimum message level that triggers err log flush")
		.add<std::uint32_t>("flush-interval", "Multithreaded logs background flush interval")
	;
	opt_group(confopt_, "radio")
		.add<std::uint16_t>("port", "Port number for main BS network interface")
		.add<std::uint16_t>("groups-port", "Port number for publishing actor groups")
		.add<timespan>("timeout", "Generic default timeout for actor operations")
		.add<timespan>("long-timeout", "Timeout for long resource-consuming tasks")
		.add<bool>("await_actors_before_shutdown",
			"Do we have to wait until all actors terminate on kernel shutdown?")
	;

	/*-----------------------------------------------------------------------------
	*  Logic here is the following
	*  1. Conf file from the latter path override previous one
	*  2. For UNIX order is the following:
	*  	/etc/blue-sky/blue-sky.conf
	*  	/home/$USER/.blue-sky/blue-sky.conf
	*  3. For Windows order is the following:
	*  	%ALLUSERSPROFILE%\blue-sky\blue-sky.conf (C:\ProgramData\...)
	*  	%APPDATA%\blue-sky\blue-sky.conf (C:\Users\%USER%\AppData\Roaming\...)
	*  4. blue-sky.conf from . dir is the last one
	*-----------------------------------------------------------------------------*/
	// as fallback add possibility to read config from current path
	conf_path_.push_back("blue-sky.ini");
#ifdef UNIX
	conf_path_.emplace_back("/etc/blue-sky/blue-sky.ini");
	conf_path_.push_back( fs::path(::getenv("HOME")) / ".blue-sky/blue-sky.ini" );
#else // WINDOWS
	conf_path_.push_back( fs::path(::getenv("ALLUSERSPROFILE")) / "blue-sky" / "blue-sky.ini" );
	conf_path_.push_back( fs::path(::getenv("USERPROFILE")) / "blue-sky" / "blue-sky.ini" );
#endif // UNIX
}

auto config_subsyst::configure(string_list args, std::string ini_fname, bool force) -> void {
	// build list of config files to parse
	std::vector<fs::path> ini2parse;
	// first read predefined configs
	if(!kernel_configured || force)
		std::copy(conf_path_.begin(), conf_path_.end(), std::back_inserter(ini2parse));
	// then custom configs passed from CLI and `ini_fname` - overrides predefined
	for(auto& p : std::array<std::string, 2>{
		extract_config_file_path(confopt_, confdata_, args), std::move(ini_fname)
	}) {
		if( !p.empty() && std::find(conf_path_.begin(), conf_path_.end(), p) == conf_path_.end() )
			ini2parse.emplace_back(std::move(p));
	};

	// in force mode remove existing content of config data
	if(force) confdata_.clear();

	// read each INI file
	if(!ini2parse.empty()) {
		bsout() << "--------> [configure]" << bs_end;
		bsout() << "Try to load following config files:" << bs_end;
		for(const auto& ini_path : ini2parse) {
			auto ini = std::ifstream(ini_path);
			bool status = false;
			if ((status = ini.good())) {
				caf::detail::ini_consumer consumer{confopt_, confdata_};
				caf::parser_state<ini_iter, ini_sentinel> res{ini_iter{&ini}};
				caf::detail::parser::read_ini(res, consumer);
				if (res.i != res.e) {
					status = false;
					bserr() << log::W("*** error in {} [line {} col {}]: {}")
						<< ini_path.string() << res.line << res.column << to_string(res.code) << log::end;
				}
			}
			bsout() << "{} - {}" << ini_path.string() << (status ? "OK" : "Fail") << log::end;
			// try to read CAF config from the same dir as BS config
			const auto caf_ini_path = ini_path.parent_path() / "caf.ini";
			if(( ini = std::ifstream(caf_ini_path) )) {
				actor_cfg_.parse({}, ini);
				bsout() << "{} - {}" << caf_ini_path.string() << "CAF" << log::end;
			}
		}
	}

	// CLI options override the content of the INI file.
	using std::make_move_iterator;
	auto res = confopt_.parse(confdata_, args);
	if (res.second != args.end()) {
		if (res.first != pec::success && starts_with(*res.second, "-")) {
			bserr() << log::W("error: at CLI config argument \"{}\": {}") << *res.second
				<< to_string(res.first) << log::end;
		}
	};

	// Generate help text if needed.
	// These options are one-shot
	if(get_or(confdata_, "help", false) || get_or(confdata_, "long-help", false)) {
		bool long_help = get_or(confdata_, "long-help", false);
		bsout() << confopt_.help_text(!long_help) << log::end;
		put(confdata_, "help", false);
		put(confdata_, "long-help", false);
	}
	// Generate INI dump if needed.
	if(get_or(confdata_, "dump-config", false)) {
		std::stringstream confdump;
		confdump << '\n';
		for (auto& category : confdata_) {
			if (auto dict = get_if<config_value::dictionary>(&category.second)) {
				confdump << '[' << category.first << "]\n";
				for (auto& kvp : *dict)
					if (kvp.first != "dump-config")
						confdump << kvp.first << '=' << to_string(kvp.second) << '\n';
			}
		}
		bsout() << confdump.str() << log::end;
		put(confdata_, "dump-config", false);
	}

	// update timeout values
	reset_timeouts(
		get_or( confdata_, "radio.timeout", defaults::radio::timeout ),
		get_or( confdata_, "radio.long-timeout", defaults::radio::long_timeout )
	);

	// [NOTE] load networking module after kernel & CAF are configured (do it only once!)
	if(!kernel_configured)
		actor_cfg_.load<caf::io::middleman>();

	kernel_configured = true;
}

auto config_subsyst::clear_confdata() -> void {
	confdata_.clear();
	kernel_configured = false;
}

auto config_subsyst::is_configured() -> bool {
	return kernel_configured;
}

bool config_subsyst::kernel_configured = false;

auto radio_subsyst::reset_timeouts(timespan typical, timespan slow) -> void {
	kernel::detail::reset_timeouts(typical, slow);
}

NAMESPACE_END(blue_sky::kernel::detail)

NAMESPACE_BEGIN(blue_sky::kernel::radio)

auto timeout(bool for_long_task) -> caf::duration {
	auto& [normal_to, long_to] = kernel::detail::timeouts();
	return caf::duration{
		for_long_task ? long_to.load(std::memory_order_relaxed) : normal_to.load(std::memory_order_relaxed)
	};
}

NAMESPACE_END(blue_sky::kernel::radio)
<commit_msg>config: change in config paths `blue-sky` -> `bs`, `.ini` -> `.conf`<commit_after>/// @file
/// @author uentity
/// @date 19.11.2018
/// @brief Kernel config subsystem implementation
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/defaults.h>
#include <bs/log.h>
#include <bs/kernel/radio.h>

#include "config_subsyst.h"
#include "radio_subsyst.h"

#include <caf/config_option_adder.hpp>
#include <caf/parser_state.hpp>
#include <caf/detail/ini_consumer.hpp>
#include <caf/detail/parser/read_ini.hpp>
#include <caf/detail/parser/read_string.hpp>
#include <caf/io/middleman.hpp>

#include <fmt/ostream.h>

#include <atomic>
#include <sstream>
#include <fstream>
#include <stdlib.h>

NAMESPACE_BEGIN(blue_sky::kernel::detail)
using namespace caf;
namespace fs = std::filesystem;
using string_list = config_subsyst::string_list;

// hidden helper functions
NAMESPACE_BEGIN()

struct ini_iter {
	std::istream* ini;
	char ch;

	explicit ini_iter(std::istream* istr) : ini(istr) {
		ini->get(ch);
	}

	ini_iter() : ini(nullptr), ch('\0') {
		// nop
	}

	ini_iter(const ini_iter&) = default;

	ini_iter& operator=(const ini_iter&) = default;

	inline char operator*() const {
		return ch;
	}

	inline ini_iter& operator++() {
		ini->get(ch);
		return *this;
	}
};

struct ini_sentinel { };

bool operator!=(ini_iter iter, ini_sentinel) {
	return !iter.ini->fail();
}

auto extract_config_file_path(caf::config_option_set& opts, caf::settings& S, string_list& args)
-> std::string {
	auto ptr = opts.qualified_name_lookup("global.config-file");
	CAF_ASSERT(ptr != nullptr);
	string_list::iterator i;
	string_view path;
	std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
	// [TODO] add CAF errors processing
	if(i == args.end())
		return "";
		//return none;
	if(path.empty()) {
		args.erase(i);
		return "";
		//return make_error(pec::missing_argument, std::string{*i});
	}
	if(auto evalue = ptr->parse(path); evalue) {
		put(S, "config-file", *evalue);
		ptr->store(*evalue);
		return caf::get<std::string>(*evalue);
	}
	return "";
	//return std::move(evalue.error());
}

// separetely store configured timeouts for faster access
// returns { normal timeout, long op timeout }
using timeouts_pair = std::pair<std::atomic<timespan>, std::atomic<timespan>>;
inline auto timeouts() -> timeouts_pair& {
	static auto ts_ = timeouts_pair{};
	return ts_;
}

auto reset_timeouts(timespan typical, timespan slow) -> void {
	auto& [normal_to, long_to] = timeouts();
	normal_to.store(typical, std::memory_order_relaxed);
	long_to.store(slow, std::memory_order_relaxed);
}

NAMESPACE_END() // eof hidden ns

/*-----------------------------------------------------------------------------
 *  kernel_config_subsyst impl
 *-----------------------------------------------------------------------------*/
config_subsyst::config_subsyst() {
	///////////////////////////////////////////////////////////////////////////////
	//  add config options
	//
	using opt_group = config_option_adder;
	opt_group{confopt_, "global"}
		.add<bool>("help,h?", "print help and exit")
		.add<bool>("long-help", "print all help options and exit")
		.add<bool>("dump-config", "print configuration in INI format and exit")
		.add<std::string>("config-file", "BS config file path")
	;
	opt_group{confopt_, "path"}
		.add<std::string>("kernel", "Path to blue-sky kernel library")
		.add<string_list>("plugins", "Paths list to blue-sky plugins")
	;
	opt_group{confopt_, "logger"}
		.add<std::string>("out-file-name", "Path to stdout log file")
		.add<std::string>("err-file-name", "Path to stderr log file")
		.add<std::uint64_t>("out-file-size", "Size of rotating stdout log file")
		.add<std::uint64_t>("err-file-size", "Size of rotating stderr log file")
		.add<std::string>("out-file-format", "Format of stdout log messages")
		.add<std::string>("err-file-format", "Format of stderr log messages")
		.add<std::string>("out-console-format", "Format of stdout log messages in console")
		.add<std::string>("err-console-format", "Format of stdout log messages in console")
		.add<std::uint8_t>("out-flush-level", "Minimum message level that triggers out log flush")
		.add<std::uint8_t>("err-flush-level", "Minimum message level that triggers err log flush")
		.add<std::uint32_t>("flush-interval", "Multithreaded logs background flush interval")
	;
	opt_group(confopt_, "radio")
		.add<std::uint16_t>("port", "Port number for main BS network interface")
		.add<std::uint16_t>("groups-port", "Port number for publishing actor groups")
		.add<timespan>("timeout", "Generic default timeout for actor operations")
		.add<timespan>("long-timeout", "Timeout for long resource-consuming tasks")
		.add<bool>("await_actors_before_shutdown",
			"Do we have to wait until all actors terminate on kernel shutdown?")
	;

	/*-----------------------------------------------------------------------------
	*  Logic here is the following
	*  1. Conf file from the latter path override previous one
	*  2. For UNIX order is the following:
	*  	/etc/bs/bs.conf
	*  	/home/$USER/.bs/bs.conf
	*  3. For Windows order is the following:
	*  	%ALLUSERSPROFILE%\bs\bs.conf (C:\ProgramData\...)
	*  	%APPDATA%\bs\bs.conf (C:\Users\%USER%\AppData\Roaming\...)
	*  4. bs.conf from . dir is the last one
	*-----------------------------------------------------------------------------*/
	// as fallback add possibility to read config from current path
	conf_path_.push_back("bs.conf");
#ifdef UNIX
	conf_path_.emplace_back("/etc/bs/bs.conf");
	conf_path_.push_back( fs::path(::getenv("HOME")) / ".bs/bs.conf" );
#else // WINDOWS
	conf_path_.push_back( fs::path(::getenv("ALLUSERSPROFILE")) / "bs" / "bs.conf" );
	conf_path_.push_back( fs::path(::getenv("USERPROFILE")) / "bs" / "bs.conf" );
#endif // UNIX
}

auto config_subsyst::configure(string_list args, std::string ini_fname, bool force) -> void {
	// build list of config files to parse
	std::vector<fs::path> ini2parse;
	// first read predefined configs
	if(!kernel_configured || force)
		std::copy(conf_path_.begin(), conf_path_.end(), std::back_inserter(ini2parse));
	// then custom configs passed from CLI and `ini_fname` - overrides predefined
	for(auto& p : std::array<std::string, 2>{
		extract_config_file_path(confopt_, confdata_, args), std::move(ini_fname)
	}) {
		if( !p.empty() && std::find(conf_path_.begin(), conf_path_.end(), p) == conf_path_.end() )
			ini2parse.emplace_back(std::move(p));
	};

	// in force mode remove existing content of config data
	if(force) confdata_.clear();

	// read each INI file
	if(!ini2parse.empty()) {
		bsout() << "--------> [configure]" << bs_end;
		bsout() << "Try to load following config files:" << bs_end;
		for(const auto& ini_path : ini2parse) {
			auto ini = std::ifstream(ini_path);
			bool status = false;
			if ((status = ini.good())) {
				caf::detail::ini_consumer consumer{confopt_, confdata_};
				caf::parser_state<ini_iter, ini_sentinel> res{ini_iter{&ini}};
				caf::detail::parser::read_ini(res, consumer);
				if (res.i != res.e) {
					status = false;
					bserr() << log::W("*** error in {} [line {} col {}]: {}")
						<< ini_path.string() << res.line << res.column << to_string(res.code) << log::end;
				}
			}
			bsout() << "{} - {}" << ini_path.string() << (status ? "OK" : "Fail") << log::end;
			// try to read CAF config from the same dir as BS config
			const auto caf_ini_path = ini_path.parent_path() / "caf.conf";
			if(( ini = std::ifstream(caf_ini_path) )) {
				actor_cfg_.parse({}, ini);
				bsout() << "{} - {}" << caf_ini_path.string() << "CAF" << log::end;
			}
		}
	}

	// CLI options override the content of the INI file.
	using std::make_move_iterator;
	auto res = confopt_.parse(confdata_, args);
	if (res.second != args.end()) {
		if (res.first != pec::success && starts_with(*res.second, "-")) {
			bserr() << log::W("error: at CLI config argument \"{}\": {}") << *res.second
				<< to_string(res.first) << log::end;
		}
	};

	// Generate help text if needed.
	// These options are one-shot
	if(get_or(confdata_, "help", false) || get_or(confdata_, "long-help", false)) {
		bool long_help = get_or(confdata_, "long-help", false);
		bsout() << confopt_.help_text(!long_help) << log::end;
		put(confdata_, "help", false);
		put(confdata_, "long-help", false);
	}
	// Generate INI dump if needed.
	if(get_or(confdata_, "dump-config", false)) {
		std::stringstream confdump;
		confdump << '\n';
		for (auto& category : confdata_) {
			if (auto dict = get_if<config_value::dictionary>(&category.second)) {
				confdump << '[' << category.first << "]\n";
				for (auto& kvp : *dict)
					if (kvp.first != "dump-config")
						confdump << kvp.first << '=' << to_string(kvp.second) << '\n';
			}
		}
		bsout() << confdump.str() << log::end;
		put(confdata_, "dump-config", false);
	}

	// update timeout values
	reset_timeouts(
		get_or( confdata_, "radio.timeout", defaults::radio::timeout ),
		get_or( confdata_, "radio.long-timeout", defaults::radio::long_timeout )
	);

	// [NOTE] load networking module after kernel & CAF are configured (do it only once!)
	if(!kernel_configured)
		actor_cfg_.load<caf::io::middleman>();

	kernel_configured = true;
}

auto config_subsyst::clear_confdata() -> void {
	confdata_.clear();
	kernel_configured = false;
}

auto config_subsyst::is_configured() -> bool {
	return kernel_configured;
}

bool config_subsyst::kernel_configured = false;

auto radio_subsyst::reset_timeouts(timespan typical, timespan slow) -> void {
	kernel::detail::reset_timeouts(typical, slow);
}

NAMESPACE_END(blue_sky::kernel::detail)

NAMESPACE_BEGIN(blue_sky::kernel::radio)

auto timeout(bool for_long_task) -> caf::duration {
	auto& [normal_to, long_to] = kernel::detail::timeouts();
	return caf::duration{
		for_long_task ? long_to.load(std::memory_order_relaxed) : normal_to.load(std::memory_order_relaxed)
	};
}

NAMESPACE_END(blue_sky::kernel::radio)
<|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 23.04.2019
/// @brief BS kernel Python subsystem impl
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/python/common.h>
#include <bs/log.h>
#include <bs/kernel/errors.h>

#include "kimpl.h"
#include "python_subsyst_impl.h"
#include "plugins_subsyst.h"

#include <functional>

#define WITH_KMOD \
[[maybe_unused]] auto res = pykmod(this).map([&](auto kmod_ptr) { \
[[maybe_unused]] auto& kmod = *kmod_ptr;

#define END_WITH });

#define RETURN_WITH END_WITH return res.value();

#define RETURN_WITH_FAIL(err_val) END_WITH return res.value_or(err_val);

NAMESPACE_BEGIN(blue_sky::kernel::detail)
using namespace blue_sky::detail;
namespace py = pybind11;

NAMESPACE_BEGIN()

auto pykmod(const python_subsyst* v) -> result_or_err<pybind11::module*> {
	if(v) if(auto kmod = v->py_kmod())
		return { static_cast<pybind11::module*>(kmod) };
	return tl::unexpected{Error::BadPymod};
}

std::string extract_root_name(const std::string& full_name) {
	using namespace std;

	//extract user-friendly lib name from full name
	string pyl_name = full_name;
	//cut path
	string::size_type pos = pyl_name.rfind('/');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	pos = pyl_name.rfind('\\');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	//cut extension
	pyl_name = pyl_name.substr(0, pyl_name.find('.'));
	//cut "lib" prefix if exists
	if(pyl_name.compare(0, 3, string("lib")) == 0)
		pyl_name = pyl_name.substr(3, string::npos);

	return pyl_name;
}

constexpr auto sp_obj_hash = std::hash<sp_obj>{};

NAMESPACE_END()

python_subsyst_impl::python_subsyst_impl(void* kmod_ptr) {
	setup_py_kmod(kmod_ptr);
}

auto python_subsyst_impl::py_kmod() const -> void* { return kmod_; }

auto python_subsyst_impl::setup_py_kmod(void* kmod_ptr) -> void {
	if(kmod_ptr) {
		kmod_ = kmod_ptr;
		auto& kmod = *static_cast<pybind11::module*>(kmod_);

		auto& kpd = plugins_subsyst::kernel_pd();
		kmod.doc() = kpd.description;
		kpd.py_namespace = PyModule_GetName(kmod.ptr());
	}
}

auto python_subsyst_impl::py_init_plugin(
	const blue_sky::detail::lib_descriptor& lib, plugin_descriptor& p_descr
) -> result_or_err<std::string> {
	return pykmod(this).and_then([&](auto kmod_ptr) -> result_or_err<std::string> {
		bs_init_py_fn init_py_fn;
		lib.load_sym("bs_init_py_subsystem", init_py_fn);
		if(!init_py_fn) return tl::make_unexpected(error{p_descr.name, kernel::Error::PythonDisabled});

		if(p_descr.py_namespace.empty())
			p_descr.py_namespace = extract_root_name(lib.fname_);

		// create submodule
		auto plugin_mod = kmod_ptr->def_submodule(p_descr.py_namespace.c_str(), p_descr.description.c_str());
		// invoke init function
		init_py_fn(&plugin_mod);

		return plugins_subsyst::kernel_pd().py_namespace + '.' + p_descr.py_namespace;
	});
}

auto python_subsyst_impl::py_add_error_closure() -> void {
WITH_KMOD
	auto err_class = (pybind11::class_<std::error_code>)kmod.attr("error_code");
	pybind11::init([](int ec) {
		return std::error_code(static_cast<Error>(ec));
	}).execute(err_class);
END_WITH
}

auto python_subsyst_impl::register_adapter(std::string obj_type_id, adapter_fn f) -> void {
	if(f)
		adapters_.insert_or_assign(std::move(obj_type_id), std::move(f));
	else {
		auto pa = adapters_.find(obj_type_id);
		if(pa != adapters_.end())
			adapters_.erase(pa);
	}
}

auto python_subsyst_impl::register_default_adapter(adapter_fn f) -> void {
	def_adapter_ = std::move(f);
}

auto python_subsyst_impl::clear_adapters() -> void {
	adapters_.clear();
	def_adapter_ = nullptr;
}

auto python_subsyst_impl::adapted_types() const -> std::vector<std::string> {
	std::vector<std::string> res(adapters_.size());
	std::transform(
		adapters_.begin(), adapters_.end(), res.begin(),
		[](const auto& A){ return A.first; }
	);
	if(def_adapter_) res.push_back("*");
	return res;
}

auto python_subsyst_impl::adapt(sp_obj source) -> py::object {
	if(!source) return py::none();
	// check if adpater already created for given object
	auto cached_A = acache_.find(sp_obj_hash(source));
	if(cached_A != acache_.end())
		return cached_A->second;
	// adapt or passthrough
	auto pf = adapters_.find(source->type_id());
	auto&& obj = std::move(source);
	auto A = pf != adapters_.end() ?
		pf->second(obj) :
		( def_adapter_ ? def_adapter_(obj) : py::cast(obj) );
	// cache adapter instance
	acache_[sp_obj_hash(source)] = A;
	return A;
}

auto python_subsyst_impl::drop_adapted_cache(const sp_obj& obj) -> std::size_t {
	std::size_t res = 0;
	if(!obj) {
		res = acache_.size();
		acache_.clear();
	}
	else {
		auto cached_A = acache_.find(sp_obj_hash(obj));
		if(cached_A != acache_.end()) {
			acache_.erase(cached_A);
			res = 1;
		}
	}
	return res;
}

auto python_subsyst_impl::self() -> python_subsyst_impl& {
	// [NOTE] unconditional cast, because if self() is called, then instance MUST be self for sure
	return static_cast<python_subsyst_impl&>(singleton<python_subsyst>::Instance());
}

NAMESPACE_END(blue_sky::kernel::detail)
<commit_msg>kernel/python: don't cache non-adapted (passthrough) objects<commit_after>/// @file
/// @author uentity
/// @date 23.04.2019
/// @brief BS kernel Python subsystem impl
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/python/common.h>
#include <bs/log.h>
#include <bs/kernel/errors.h>

#include "kimpl.h"
#include "python_subsyst_impl.h"
#include "plugins_subsyst.h"

#include <functional>

#define WITH_KMOD \
[[maybe_unused]] auto res = pykmod(this).map([&](auto kmod_ptr) { \
[[maybe_unused]] auto& kmod = *kmod_ptr;

#define END_WITH });

#define RETURN_WITH END_WITH return res.value();

#define RETURN_WITH_FAIL(err_val) END_WITH return res.value_or(err_val);

NAMESPACE_BEGIN(blue_sky::kernel::detail)
using namespace blue_sky::detail;
namespace py = pybind11;

NAMESPACE_BEGIN()

auto pykmod(const python_subsyst* v) -> result_or_err<pybind11::module*> {
	if(v) if(auto kmod = v->py_kmod())
		return { static_cast<pybind11::module*>(kmod) };
	return tl::unexpected{Error::BadPymod};
}

std::string extract_root_name(const std::string& full_name) {
	using namespace std;

	//extract user-friendly lib name from full name
	string pyl_name = full_name;
	//cut path
	string::size_type pos = pyl_name.rfind('/');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	pos = pyl_name.rfind('\\');
	if(pos != string::npos) pyl_name = pyl_name.substr(pos + 1, string::npos);
	//cut extension
	pyl_name = pyl_name.substr(0, pyl_name.find('.'));
	//cut "lib" prefix if exists
	if(pyl_name.compare(0, 3, string("lib")) == 0)
		pyl_name = pyl_name.substr(3, string::npos);

	return pyl_name;
}

constexpr auto sp_obj_hash = std::hash<sp_obj>{};

NAMESPACE_END()

python_subsyst_impl::python_subsyst_impl(void* kmod_ptr) {
	setup_py_kmod(kmod_ptr);
}

auto python_subsyst_impl::py_kmod() const -> void* { return kmod_; }

auto python_subsyst_impl::setup_py_kmod(void* kmod_ptr) -> void {
	if(kmod_ptr) {
		kmod_ = kmod_ptr;
		auto& kmod = *static_cast<pybind11::module*>(kmod_);

		auto& kpd = plugins_subsyst::kernel_pd();
		kmod.doc() = kpd.description;
		kpd.py_namespace = PyModule_GetName(kmod.ptr());
	}
}

auto python_subsyst_impl::py_init_plugin(
	const blue_sky::detail::lib_descriptor& lib, plugin_descriptor& p_descr
) -> result_or_err<std::string> {
	return pykmod(this).and_then([&](auto kmod_ptr) -> result_or_err<std::string> {
		bs_init_py_fn init_py_fn;
		lib.load_sym("bs_init_py_subsystem", init_py_fn);
		if(!init_py_fn) return tl::make_unexpected(error{p_descr.name, kernel::Error::PythonDisabled});

		if(p_descr.py_namespace.empty())
			p_descr.py_namespace = extract_root_name(lib.fname_);

		// create submodule
		auto plugin_mod = kmod_ptr->def_submodule(p_descr.py_namespace.c_str(), p_descr.description.c_str());
		// invoke init function
		init_py_fn(&plugin_mod);

		return plugins_subsyst::kernel_pd().py_namespace + '.' + p_descr.py_namespace;
	});
}

auto python_subsyst_impl::py_add_error_closure() -> void {
WITH_KMOD
	auto err_class = (pybind11::class_<std::error_code>)kmod.attr("error_code");
	pybind11::init([](int ec) {
		return std::error_code(static_cast<Error>(ec));
	}).execute(err_class);
END_WITH
}

auto python_subsyst_impl::register_adapter(std::string obj_type_id, adapter_fn f) -> void {
	if(f)
		adapters_.insert_or_assign(std::move(obj_type_id), std::move(f));
	else {
		auto pa = adapters_.find(obj_type_id);
		if(pa != adapters_.end())
			adapters_.erase(pa);
	}
}

auto python_subsyst_impl::register_default_adapter(adapter_fn f) -> void {
	def_adapter_ = std::move(f);
}

auto python_subsyst_impl::clear_adapters() -> void {
	adapters_.clear();
	def_adapter_ = nullptr;
}

auto python_subsyst_impl::adapted_types() const -> std::vector<std::string> {
	std::vector<std::string> res(adapters_.size());
	std::transform(
		adapters_.begin(), adapters_.end(), res.begin(),
		[](const auto& A){ return A.first; }
	);
	if(def_adapter_) res.push_back("*");
	return res;
}

auto python_subsyst_impl::adapt(sp_obj source) -> py::object {
	if(!source) return py::none();
	// check if adpater already created for given object
	const auto source_key = sp_obj_hash(source);
	auto cached_A = acache_.find(source_key);
	if(cached_A != acache_.end())
		return cached_A->second;

	// adapt or passthrough
	const auto adapt_and_cache = [this, source_key](auto&& obj, auto&& afn) {
		return acache_.try_emplace(
			source_key, afn(std::move(obj))
		).first->second;
	};
	auto pf = adapters_.find(source->type_id());
	auto&& obj = std::move(source);
	return pf != adapters_.end() ?
		adapt_and_cache(obj, pf->second) :
		( def_adapter_ ? adapt_and_cache(obj, def_adapter_) : py::cast(obj) );
}

auto python_subsyst_impl::drop_adapted_cache(const sp_obj& obj) -> std::size_t {
	std::size_t res = 0;
	if(!obj) {
		res = acache_.size();
		acache_.clear();
	}
	else {
		auto cached_A = acache_.find(sp_obj_hash(obj));
		if(cached_A != acache_.end()) {
			acache_.erase(cached_A);
			res = 1;
		}
	}
	return res;
}

auto python_subsyst_impl::self() -> python_subsyst_impl& {
	// [NOTE] unconditional cast, because if self() is called, then instance MUST be self for sure
	return static_cast<python_subsyst_impl&>(singleton<python_subsyst>::Instance());
}

NAMESPACE_END(blue_sky::kernel::detail)
<|endoftext|>


<commit_before>/**
 * @file si.cpp
 *
 * @date Feb 13, 2014
 * @author partio
 */

#include "si.h"
#include "plugin_factory.h"
#include "logger_factory.h"
#include <boost/lexical_cast.hpp>
#include "util.h"

#define HIMAN_AUXILIARY_INCLUDE

#include "neons.h"
#include "fetcher.h"
#include "querydata.h"

#undef HIMAN_AUXILIARY_INCLUDE

using namespace std;
using namespace himan::plugin;

#include "NFmiQueryData.h"
#include "NFmiSoundingIndexCalculator.h"

#if 0
// nicer way to convert between std::shared_ptr and boost::shared_ptr but does not work on gcc 4.4.7 :(

boost::shared_ptr<NFmiQueryData> make_shared_ptr(std::shared_ptr<NFmiQueryData>& ptr)
{
    return boost::shared_ptr<NFmiQueryData>(ptr.get(), [ptr](NFmiQueryData*) mutable {ptr.reset();});
}

std::shared_ptr<NFmiQueryData> make_shared_ptr(boost::shared_ptr<NFmiQueryData>& ptr)
{
    return std::shared_ptr<NFmiQueryData>(ptr.get(), [ptr](NFmiQueryData*) mutable {ptr.reset();});
}
#endif

// Define a null delete that's used when std::shared_ptr is converted to boost::shared_ptr or vice-versa.
// The idea is that the new shared_ptr does not call delete when it goes out of scope since the original
// shared_ptr does that.

struct nullDeleter
{
	template <typename T>
	void operator()(T*& ptr) {}
};

si::si() : itsBottomLevel(kHPMissingInt)
{
	itsClearTextFormula = "<multiple algorithms>";

	itsLogger = unique_ptr<logger> (logger_factory::Instance()->GetLog("si"));
}

void si::Process(std::shared_ptr<const plugin_configuration> conf)
{
	compiled_plugin_base::Init(conf);

	/*
	 * Set target parameters:
	 * - name
	 * - univ_id
	 * - grib2 descriptor 0'00'000
	 *
	 */

	vector<param> theParams;

	theParams.push_back(param("DUMMY"));

	// GRIB 1

	SetParams(theParams);

	shared_ptr<neons> theNeons = dynamic_pointer_cast <neons> (plugin_factory::Instance()->Plugin("neons"));

	itsBottomLevel = boost::lexical_cast<int> (theNeons->ProducerMetaData(itsConfiguration->SourceProducer().Id(), "last hybrid level number"));
	itsTopLevel = boost::lexical_cast<int> (theNeons->ProducerMetaData(itsConfiguration->SourceProducer().Id(), "first hybrid level number"));

	Start();

}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void si::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	auto f = dynamic_pointer_cast <fetcher> (plugin_factory::Instance()->Plugin("fetcher"));
	auto q = dynamic_pointer_cast <querydata> (plugin_factory::Instance()->Plugin("querydata"));

	// Required source parameters

	const param TParam("T-K");
	//const param TDParam("TD-C");
	const param PParam("P-HPA");
	const param RHParam("RH-PRCNT");
	const param HParam("HL-M");
	const param FFParam("FF-MS");

	unique_ptr<logger> myThreadedLogger = std::unique_ptr<logger> (logger_factory::Instance()->GetLog("siThread #" + boost::lexical_cast<string> (threadIndex)));

	ResetNonLeadingDimension(myTargetInfo);

	myTargetInfo->FirstParam();

	while (AdjustNonLeadingDimension(myTargetInfo))
	{

		myThreadedLogger->Debug("Calculating time " + myTargetInfo->Time().ValidDateTime()->String("%Y%m%d%H%M") +
								" level " + boost::lexical_cast<string> (myTargetInfo->Level().Value()));

		// Source infos

		shared_ptr<info> sourceInfo;

		bool haveData = true;

		for (int levelNumber = itsTopLevel; haveData && levelNumber <= itsBottomLevel; levelNumber++)
		{
			level curLevel(kHybrid, levelNumber, "HYBRID");

			shared_ptr<info> tempInfo;

			try
			{

				// Temperature

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 TParam);

				assert(tempInfo->Param().Unit() == kK);

				// grib-plugin does not set universal id number since it does
				// not know anything about it, but we need it in smarttools-library

				tempInfo->Param().UnivId(4);

				if (!sourceInfo)
				{
					sourceInfo = tempInfo;
				}
				else
				{
					sourceInfo->Merge(tempInfo);
				}

				tempInfo.reset();

				// Humidity

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 RHParam);

				tempInfo->Param().UnivId(13);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

				// Pressure

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 PParam);

				tempInfo->Param().UnivId(1);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

				// Height

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 HParam);

				tempInfo->Param().UnivId(3);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

				// Wind speed

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 FFParam);

				tempInfo->Param().UnivId(21);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

			}
			catch (HPExceptionType e)
			{
				switch (e)
				{
					case kFileDataNotFound:
						itsLogger->Warning("Skipping step " + boost::lexical_cast<string> (myTargetInfo->Time().Step()) + ", level " + boost::lexical_cast<string> (myTargetInfo->Level().Value()));
						myTargetInfo->Data()->Fill(kFloatMissing);

						if (itsConfiguration->StatisticsEnabled())
						{
							itsConfiguration->Statistics()->AddToMissingCount(myTargetInfo->Grid()->Size());
							itsConfiguration->Statistics()->AddToValueCount(myTargetInfo->Grid()->Size());
						}
						haveData = false;
						continue;
						break;

					default:
						throw runtime_error(ClassName() + ": Unable to proceed");
						break;
				}
			}
		}

		if (!haveData)
		{
			break;
		}

		size_t missingCount = 0;
		size_t count = 0;

		// Scale to correct units

		itsLogger->Info("Scaling source data");

		sourceInfo->First();

		bool ret = sourceInfo->Param(TParam);

#ifndef NDEBUG
		assert(ret);
#endif

		sourceInfo->FirstTime();
		assert(sourceInfo->SizeTimes() == 1);

		for (sourceInfo->ResetLevel(); sourceInfo->NextLevel();)
		{
			ScaleBase(sourceInfo, 1, -constants::kKelvin);
		}

		// data read from neons does not have correct fmi producer id, copy producer
		// info from target info

		sourceInfo->Producer(myTargetInfo->Producer());

		// info: convert to querydata

		shared_ptr<NFmiQueryData> qdata = q->CreateQueryData(sourceInfo, false);

		// querydata: std::shared_ptr to boost::shared_ptr
		// boost::shared_ptr<NFmiQueryData> bqdata = make_shared_ptr(qdata);
		boost::shared_ptr<NFmiQueryData> bqdata (qdata.get(), nullDeleter());

		myThreadedLogger->Info("Calculating sounding index");

		// got boost::shared_ptr
		boost::shared_ptr<NFmiQueryData> bsidata = NFmiSoundingIndexCalculator::CreateNewSoundingIndexData(bqdata, "ASDF", false, 0);

		// querydata: boost::shared_ptr to std::shared_ptr
		//shared_ptr<NFmiQueryData> sidata = make_shared_ptr(bsidata);
		shared_ptr<NFmiQueryData> sidata (bsidata.get(), nullDeleter());

		// querydata: convert to info
		myTargetInfo = q->CreateInfo(sidata);

		// Set correct target level to output data

		myTargetInfo->FirstLevel();
		myTargetInfo->Level().Type(kHeight);

		string deviceType = "CPU";

		/*
		 * Newbase normalizes scanning mode to bottom left -- if that's not what
		 * the target scanning mode is, we have to swap the data back.
		 */

		for (myTargetInfo->ResetParam(); myTargetInfo->NextParam(); )
		{
			SwapTo(myTargetInfo, kBottomLeft);

			param& p = myTargetInfo->Param();

			switch (p.UnivId())
			{
				case 4720:
					p.Name("LCL-HPA");
					break;

				case 4721:
					p.Name("LFC-HPA");
					break;

				case 4722:
					p.Name("EL-HPA");
					break;

				case 4723:
					p.Name("CAPE-JKG");
					break;

				case 4724:
					p.Name("CAPE-0-3");
					break;

				case 4725:
					p.Name("CIN-N");
					break;

				case 4726:
					p.Name("LCL-M");
					break;

				case 4727:
					p.Name("LFC-M");
					break;

				case 4728:
					p.Name("EL-M");
					break;

				case 4729:
					p.Name("CAPE1040");
					break;

				case 4730:
					p.Name("LCL-500-HPA");
					break;

				case 4731:
					p.Name("LFC-500-HPA");
					break;

				case 4732:
					p.Name("EL-500-HPA");
					break;

				case 4733:
					p.Name("CAPE-500");
					break;

				case 4734:
					p.Name("CAPE-0-3-500");
					break;

				case 4735:
					p.Name("CIN-500-N");
					break;

				case 4736:
					p.Name("LCL-500-M");
					break;

				case 4737:
					p.Name("LFC-500-M");
					break;

				case 4738:
					p.Name("LFC-500-M");
					break;

				case 4739:
					p.Name("CAPE1040-500");
					break;

				case 4740:
					p.Name("LCL-MU-HPA");
					break;

				case 4741:
					p.Name("LFC-MU-HPA");
					break;

				case 4742:
					p.Name("EL-MU-HPA");
					break;

				case 4743:
					p.Name("CAPE-MU-JKG");
					break;

				case 4744:
					p.Name("CAPE-0-3-MU");
					break;

				case 4745:
					p.Name("CIN-MU-N");
					break;

				case 4746:
					p.Name("LCL-MU-M");
					break;

				case 4747:
					p.Name("LFC-MU-M");
					break;

				case 4748:
					p.Name("EL-MU-M");
					break;

				case 4749:
					p.Name("CAPE1040-MU");
					break;

				case 4750:
					p.Name("SI-N");
					break;

				case 4751:
					p.Name("LI-N");
					break;

				case 4752:
					p.Name("KINDEX-N");
					break;

				case 4753:
					p.Name("CTI-N");
					break;

				case 4754:
					p.Name("VTI-N");
					break;

				case 4755:
					p.Name("TTI-N");
					break;

				case 4770:
					p.Name("WSH-KT");
					break;

				case 4771:
					p.Name("WSH-1-KT");
					break;

				case 4772:
					p.Name("HLCY-M2S2");
					break;

				case 4773:
					p.Name("HLCY-1-M2S2");
					break;

				case 4774:
					p.Name("FF1500-MS");
					break;

				case 4775:
					p.Name("TPE3-C");
					break;

				default:
					throw runtime_error("Unkown sounding parameter calculated: " + myTargetInfo->Param().Name());
					break;
			}

			for (myTargetInfo->ResetLocation(); myTargetInfo->NextLocation();)
			{
				count++;

				if (myTargetInfo->Value() == kFloatMissing)
				{
					missingCount++;
				}
			}
		}

		if (itsConfiguration->StatisticsEnabled())
		{
			itsConfiguration->Statistics()->AddToMissingCount(missingCount);
			itsConfiguration->Statistics()->AddToValueCount(count);
		}

		/*
		 * Now we are done for this level
		 *
		 * Clone info-instance to writer since it might change our descriptor places
		 */

		myThreadedLogger->Info("[" + deviceType + "] Missing values: " + boost::lexical_cast<string> (missingCount) + "/" + boost::lexical_cast<string> (count));

		if (itsConfiguration->FileWriteOption() != kSingleFile)
		{
			WriteToFile(myTargetInfo);
		}

	}
}

void si::ScaleBase(shared_ptr<info> anInfo, double scale, double base)
{
	if (scale == 1 && base == 0)
	{
		return;
	}

	for (anInfo->ResetLocation(); anInfo->NextLocation(); )
	{
		double v = anInfo->Value();
		anInfo->Value(v * scale + base);
	}
}
<commit_msg>YAF: yet another fix<commit_after>/**
 * @file si.cpp
 *
 * @date Feb 13, 2014
 * @author partio
 */

#include "si.h"
#include "plugin_factory.h"
#include "logger_factory.h"
#include <boost/lexical_cast.hpp>
#include "util.h"

#define HIMAN_AUXILIARY_INCLUDE

#include "neons.h"
#include "fetcher.h"
#include "querydata.h"

#undef HIMAN_AUXILIARY_INCLUDE

using namespace std;
using namespace himan::plugin;

#include "NFmiQueryData.h"
#include "NFmiSoundingIndexCalculator.h"

#if 0
// nicer way to convert between std::shared_ptr and boost::shared_ptr but does not work on gcc 4.4.7 :(

boost::shared_ptr<NFmiQueryData> make_shared_ptr(std::shared_ptr<NFmiQueryData>& ptr)
{
    return boost::shared_ptr<NFmiQueryData>(ptr.get(), [ptr](NFmiQueryData*) mutable {ptr.reset();});
}

std::shared_ptr<NFmiQueryData> make_shared_ptr(boost::shared_ptr<NFmiQueryData>& ptr)
{
    return std::shared_ptr<NFmiQueryData>(ptr.get(), [ptr](NFmiQueryData*) mutable {ptr.reset();});
}
#endif

// Define a null delete that's used when std::shared_ptr is converted to boost::shared_ptr or vice-versa.
// The idea is that the new shared_ptr does not call delete when it goes out of scope since the original
// shared_ptr does that.

struct nullDeleter
{
	template <typename T>
	void operator()(T*& ptr) {}
};

si::si() : itsBottomLevel(kHPMissingInt)
{
	itsClearTextFormula = "<multiple algorithms>";

	itsLogger = unique_ptr<logger> (logger_factory::Instance()->GetLog("si"));
}

void si::Process(std::shared_ptr<const plugin_configuration> conf)
{
	compiled_plugin_base::Init(conf);

	/*
	 * Set target parameters:
	 * - name
	 * - univ_id
	 * - grib2 descriptor 0'00'000
	 *
	 */

	vector<param> theParams;

	theParams.push_back(param("DUMMY"));

	// GRIB 1

	SetParams(theParams);

	shared_ptr<neons> theNeons = dynamic_pointer_cast <neons> (plugin_factory::Instance()->Plugin("neons"));

	itsBottomLevel = boost::lexical_cast<int> (theNeons->ProducerMetaData(itsConfiguration->SourceProducer().Id(), "last hybrid level number"));
	itsTopLevel = boost::lexical_cast<int> (theNeons->ProducerMetaData(itsConfiguration->SourceProducer().Id(), "first hybrid level number"));

	Start();

}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void si::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	auto f = dynamic_pointer_cast <fetcher> (plugin_factory::Instance()->Plugin("fetcher"));
	auto q = dynamic_pointer_cast <querydata> (plugin_factory::Instance()->Plugin("querydata"));

	// Required source parameters

	const param TParam("T-K");
	//const param TDParam("TD-C");
	const param PParam("P-HPA");
	const param RHParam("RH-PRCNT");
	const param HParam("HL-M");
	const param FFParam("FF-MS");

	unique_ptr<logger> myThreadedLogger = std::unique_ptr<logger> (logger_factory::Instance()->GetLog("siThread #" + boost::lexical_cast<string> (threadIndex)));

	ResetNonLeadingDimension(myTargetInfo);

	myTargetInfo->FirstParam();

	while (AdjustNonLeadingDimension(myTargetInfo))
	{

		myThreadedLogger->Debug("Calculating time " + myTargetInfo->Time().ValidDateTime()->String("%Y%m%d%H%M") +
								" level " + boost::lexical_cast<string> (myTargetInfo->Level().Value()));

		// Source infos

		shared_ptr<info> sourceInfo;

		bool haveData = true;

		for (int levelNumber = itsTopLevel; haveData && levelNumber <= itsBottomLevel; levelNumber++)
		{
			level curLevel(kHybrid, levelNumber, "HYBRID");

			shared_ptr<info> tempInfo;

			try
			{

				// Temperature

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 TParam);

				assert(tempInfo->Param().Unit() == kK);

				// grib-plugin does not set universal id number since it does
				// not know anything about it, but we need it in smarttools-library

				tempInfo->Param().UnivId(4);

				if (!sourceInfo)
				{
					sourceInfo = tempInfo;
				}
				else
				{
					sourceInfo->Merge(tempInfo);
				}

				tempInfo.reset();

				// Humidity

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 RHParam);

				tempInfo->Param().UnivId(13);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

				// Pressure

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 PParam);

				tempInfo->Param().UnivId(1);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

				// Height

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 HParam);

				tempInfo->Param().UnivId(3);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

				// Wind speed

				tempInfo = f->Fetch(itsConfiguration,
									 myTargetInfo->Time(),
									 curLevel,
									 FFParam);

				tempInfo->Param().UnivId(21);

				sourceInfo->Merge(tempInfo);

				tempInfo.reset();

			}
			catch (HPExceptionType e)
			{
				switch (e)
				{
					case kFileDataNotFound:
						itsLogger->Warning("Skipping step " + boost::lexical_cast<string> (myTargetInfo->Time().Step()) + ", level " + boost::lexical_cast<string> (myTargetInfo->Level().Value()));
						myTargetInfo->Data()->Fill(kFloatMissing);

						if (itsConfiguration->StatisticsEnabled())
						{
							itsConfiguration->Statistics()->AddToMissingCount(myTargetInfo->Grid()->Size());
							itsConfiguration->Statistics()->AddToValueCount(myTargetInfo->Grid()->Size());
						}
						haveData = false;
						continue;
						break;

					default:
						throw runtime_error(ClassName() + ": Unable to proceed");
						break;
				}
			}
		}

		if (!haveData)
		{
			break;
		}

		size_t missingCount = 0;
		size_t count = 0;

		// Scale to correct units

		itsLogger->Info("Scaling source data");

		sourceInfo->First();

#ifndef NDEBUG
		bool ret =
#endif

		sourceInfo->Param(TParam);

#ifndef NDEBUG
		assert(ret);
#endif

		sourceInfo->FirstTime();
		assert(sourceInfo->SizeTimes() == 1);

		for (sourceInfo->ResetLevel(); sourceInfo->NextLevel();)
		{
			ScaleBase(sourceInfo, 1, -constants::kKelvin);
		}

		// data read from neons does not have correct fmi producer id, copy producer
		// info from target info

		sourceInfo->Producer(myTargetInfo->Producer());

		// info: convert to querydata

		shared_ptr<NFmiQueryData> qdata = q->CreateQueryData(sourceInfo, false);

		// querydata: std::shared_ptr to boost::shared_ptr
		// boost::shared_ptr<NFmiQueryData> bqdata = make_shared_ptr(qdata);
		boost::shared_ptr<NFmiQueryData> bqdata (qdata.get(), nullDeleter());

		myThreadedLogger->Info("Calculating sounding index");

		// got boost::shared_ptr
		boost::shared_ptr<NFmiQueryData> bsidata = NFmiSoundingIndexCalculator::CreateNewSoundingIndexData(bqdata, "ASDF", false, 0);

		// querydata: boost::shared_ptr to std::shared_ptr
		//shared_ptr<NFmiQueryData> sidata = make_shared_ptr(bsidata);
		shared_ptr<NFmiQueryData> sidata (bsidata.get(), nullDeleter());

		// querydata: convert to info
		myTargetInfo = q->CreateInfo(sidata);

		// Set correct target level to output data

		myTargetInfo->FirstLevel();
		myTargetInfo->Level().Type(kHeight);

		string deviceType = "CPU";

		/*
		 * Newbase normalizes scanning mode to bottom left -- if that's not what
		 * the target scanning mode is, we have to swap the data back.
		 */

		for (myTargetInfo->ResetParam(); myTargetInfo->NextParam(); )
		{
			SwapTo(myTargetInfo, kBottomLeft);

			param& p = myTargetInfo->Param();

			switch (p.UnivId())
			{
				case 4720:
					p.Name("LCL-HPA");
					break;

				case 4721:
					p.Name("LFC-HPA");
					break;

				case 4722:
					p.Name("EL-HPA");
					break;

				case 4723:
					p.Name("CAPE-JKG");
					break;

				case 4724:
					p.Name("CAPE-0-3");
					break;

				case 4725:
					p.Name("CIN-N");
					break;

				case 4726:
					p.Name("LCL-M");
					break;

				case 4727:
					p.Name("LFC-M");
					break;

				case 4728:
					p.Name("EL-M");
					break;

				case 4729:
					p.Name("CAPE1040");
					break;

				case 4730:
					p.Name("LCL-500-HPA");
					break;

				case 4731:
					p.Name("LFC-500-HPA");
					break;

				case 4732:
					p.Name("EL-500-HPA");
					break;

				case 4733:
					p.Name("CAPE-500");
					break;

				case 4734:
					p.Name("CAPE-0-3-500");
					break;

				case 4735:
					p.Name("CIN-500-N");
					break;

				case 4736:
					p.Name("LCL-500-M");
					break;

				case 4737:
					p.Name("LFC-500-M");
					break;

				case 4738:
					p.Name("LFC-500-M");
					break;

				case 4739:
					p.Name("CAPE1040-500");
					break;

				case 4740:
					p.Name("LCL-MU-HPA");
					break;

				case 4741:
					p.Name("LFC-MU-HPA");
					break;

				case 4742:
					p.Name("EL-MU-HPA");
					break;

				case 4743:
					p.Name("CAPE-MU-JKG");
					break;

				case 4744:
					p.Name("CAPE-0-3-MU");
					break;

				case 4745:
					p.Name("CIN-MU-N");
					break;

				case 4746:
					p.Name("LCL-MU-M");
					break;

				case 4747:
					p.Name("LFC-MU-M");
					break;

				case 4748:
					p.Name("EL-MU-M");
					break;

				case 4749:
					p.Name("CAPE1040-MU");
					break;

				case 4750:
					p.Name("SI-N");
					break;

				case 4751:
					p.Name("LI-N");
					break;

				case 4752:
					p.Name("KINDEX-N");
					break;

				case 4753:
					p.Name("CTI-N");
					break;

				case 4754:
					p.Name("VTI-N");
					break;

				case 4755:
					p.Name("TTI-N");
					break;

				case 4770:
					p.Name("WSH-KT");
					break;

				case 4771:
					p.Name("WSH-1-KT");
					break;

				case 4772:
					p.Name("HLCY-M2S2");
					break;

				case 4773:
					p.Name("HLCY-1-M2S2");
					break;

				case 4774:
					p.Name("FF1500-MS");
					break;

				case 4775:
					p.Name("TPE3-C");
					break;

				default:
					throw runtime_error("Unkown sounding parameter calculated: " + myTargetInfo->Param().Name());
					break;
			}

			for (myTargetInfo->ResetLocation(); myTargetInfo->NextLocation();)
			{
				count++;

				if (myTargetInfo->Value() == kFloatMissing)
				{
					missingCount++;
				}
			}
		}

		if (itsConfiguration->StatisticsEnabled())
		{
			itsConfiguration->Statistics()->AddToMissingCount(missingCount);
			itsConfiguration->Statistics()->AddToValueCount(count);
		}

		/*
		 * Now we are done for this level
		 *
		 * Clone info-instance to writer since it might change our descriptor places
		 */

		myThreadedLogger->Info("[" + deviceType + "] Missing values: " + boost::lexical_cast<string> (missingCount) + "/" + boost::lexical_cast<string> (count));

		if (itsConfiguration->FileWriteOption() != kSingleFile)
		{
			WriteToFile(myTargetInfo);
		}

	}
}

void si::ScaleBase(shared_ptr<info> anInfo, double scale, double base)
{
	if (scale == 1 && base == 0)
	{
		return;
	}

	for (anInfo->ResetLocation(); anInfo->NextLocation(); )
	{
		double v = anInfo->Value();
		anInfo->Value(v * scale + base);
	}
}
<|endoftext|>


<commit_before><commit_msg>Display the warning if the device too tilted.<commit_after><|endoftext|>