text:
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2021 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the author nor the names of other contributors may
//      be used to endorse or promote products derived from this software
//      without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////

#include "pch.hpp"

#include <musikcore/debug.h>
#include <musikcore/library/Indexer.h>

#include <musikcore/config.h>
#include <musikcore/debug.h>
#include <musikcore/library/track/IndexerTrack.h>
#include <musikcore/library/track/LibraryTrack.h>
#include <musikcore/library/query/TrackMetadataQuery.h>
#include <musikcore/library/LocalLibraryConstants.h>
#include <musikcore/library/LibraryFactory.h>
#include <musikcore/db/Connection.h>
#include <musikcore/db/Statement.h>
#include <musikcore/plugin/PluginFactory.h>
#include <musikcore/support/Common.h>
#include <musikcore/support/Preferences.h>
#include <musikcore/support/PreferenceKeys.h>
#include <musikcore/sdk/IAnalyzer.h>
#include <musikcore/sdk/IIndexerSource.h>
#include <musikcore/audio/Stream.h>

#include <algorithm>
#include <atomic>

#pragma warning(push, 0)
#include <boost/bind.hpp>
#pragma warning(pop)

#define STRESS_TEST_DB 0

constexpr const char* TAG = "Indexer";
constexpr size_t TRANSACTION_INTERVAL = 300;
static FILE* logFile = nullptr;

#ifdef __arm__
constexpr int DEFAULT_MAX_THREADS = 2;
#else
constexpr int DEFAULT_MAX_THREADS = 4;
#endif

using namespace musik::core;
using namespace musik::core::sdk;
using namespace musik::core::audio;
using namespace musik::core::library;
using namespace musik::core::db;
using namespace musik::core::library::query;

using Thread = std::unique_ptr<boost::thread>;

using TagReaderDestroyer = PluginFactory::ReleaseDeleter<ITagReader>;
using DecoderDeleter = PluginFactory::ReleaseDeleter<IDecoderFactory>;
using SourceDeleter = PluginFactory::ReleaseDeleter<IIndexerSource>;

static void openLogFile() {
    if (!logFile) {
        std::string path = GetDataDirectory() + "/indexer_log.txt";
#ifdef WIN32
        logFile = _wfopen(u8to16(path).c_str(), L"w");
#else
        logFile = fopen(path.c_str(), "w");
#endif
    }
}

static void closeLogFile() noexcept {
    if (logFile) {
        fclose(logFile);
        logFile = nullptr;
    }
}

static std::string normalizePath(const std::string& path) {
    return boost::filesystem::path(path).make_preferred().string();
}

Indexer::Indexer(const std::string& libraryPath, const std::string& dbFilename)
: thread(nullptr)
, incrementalUrisScanned(0)
, totalUrisScanned(0)
, state(StateStopped)
, prefs(Preferences::ForComponent(prefs::components::Settings)) {
    if (prefs->GetBool(prefs::keys::IndexerLogEnabled, false) && !logFile) {
        openLogFile();
    }

    this->tagReaders = PluginFactory::Instance()
        .QueryInterface<ITagReader, TagReaderDestroyer>("GetTagReader");

    this->audioDecoders = PluginFactory::Instance()
        .QueryInterface<IDecoderFactory, DecoderDeleter>("GetDecoderFactory");

    this->sources = PluginFactory::Instance()
        .QueryInterface<IIndexerSource, SourceDeleter>("GetIndexerSource");

    this->dbFilename = dbFilename;
    this->libraryPath = libraryPath;

    db::Connection connection;
    connection.Open(this->dbFilename.c_str());
    db::Statement stmt("SELECT path FROM paths ORDER BY id", connection);
    while (stmt.Step() == db::Row) {
        this->paths.push_back(stmt.ColumnText(0));
    }
}

Indexer::~Indexer() {
    closeLogFile();
    this->Shutdown();
}

void Indexer::Shutdown() {
    if (this->thread) {
        {
            boost::mutex::scoped_lock lock(this->stateMutex);

            this->syncQueue.clear();
            this->state = StateStopping;

            if (this->currentSource) {
                this->currentSource->Interrupt();
            }
        }

        this->waitCondition.notify_all();
        this->thread->join();
        this->thread.reset();
    }
}

void Indexer::Schedule(SyncType type) {
    this->Schedule(type, nullptr);
}

void Indexer::Schedule(SyncType type, IIndexerSource* source) {
    boost::mutex::scoped_lock lock(this->stateMutex);

    if (!this->thread) {
        this->state = StateIdle;
        this->thread = std::make_unique<std::thread>(std::bind(&Indexer::ThreadLoop, this));
    }

    const int sourceId = source ? source->SourceId() : 0;
    for (const SyncContext& context : this->syncQueue) {
        if (context.type == type && context.sourceId == sourceId) {
            return;
        }
    }

    SyncContext context;
    context.type = type;
    context.sourceId = sourceId;
    syncQueue.push_back(context);

    this->waitCondition.notify_all();
}

void Indexer::AddPath(const std::string& path) {
    Indexer::AddRemoveContext context;
    context.add = true;
    context.path = NormalizeDir(path);

    {
        boost::mutex::scoped_lock lock(this->stateMutex);

        if (std::find(this->paths.begin(), this->paths.end(), path) == this->paths.end()) {
            this->paths.push_back(path);
        }

        this->addRemoveQueue.push_back(context);
    }
}

void Indexer::RemovePath(const std::string& path) {
    Indexer::AddRemoveContext context;
    context.add = false;
    context.path = NormalizeDir(path);

    {
        boost::mutex::scoped_lock lock(this->stateMutex);

        auto it = std::find(this->paths.begin(), this->paths.end(), path);
        if (it != this->paths.end()) {
            this->paths.erase(it);
        }

        this->addRemoveQueue.push_back(context);
    }
}

void Indexer::Synchronize(const SyncContext& context, boost::asio::io_service* io) {
    LocalLibrary::CreateIndexes(this->dbConnection);

    IndexerTrack::OnIndexerStarted(this->dbConnection);

    this->ProcessAddRemoveQueue();

    this->incrementalUrisScanned = 0;
    this->totalUrisScanned = 0;

    /* always remove tracks that no longer have a corresponding source */
    for (const auto id : this->GetOrphanedSourceIds()) {
        this->RemoveAllForSourceId(id);
    }

    auto type = context.type;
    const auto sourceId = context.sourceId;
    if (type == SyncType::Rebuild) {
        LocalLibrary::InvalidateTrackMetadata(this->dbConnection);

        /* for sources with stable ids: just nuke all of the records and allow
        a rebuild from scratch; things like playlists will remain intact.
        this ensures tracks that should be removed, are */
        for (auto source: sources) {
            if (source->HasStableIds()) {
                this->RemoveAll(source.get());
            }
        }

        type = SyncType::All;
    }

    std::vector<std::string> paths;
    std::vector<int64_t> pathIds;

    /* resolve all the path and path ids (required for local files */
    db::Statement stmt("SELECT id, path FROM paths", this->dbConnection);

    while (stmt.Step() == db::Row) {
        try {
            const int64_t id = stmt.ColumnInt64(0);
            std::string path = stmt.ColumnText(1);
            boost::filesystem::path dir(path);

            if (boost::filesystem::exists(dir)) {
                paths.push_back(path);
                pathIds.push_back(id);
            }
        }
        catch(...) {
        }
    }

    /* refresh sources */
    for (auto it : this->sources) {
        if (this->Bail()) {
            break;
        }

        if (sourceId != 0 && sourceId != it->SourceId()) {
            continue; /* asked to scan a specific source, and this isn't it. */
        }

        this->currentSource = it;
        if (this->SyncSource(it.get(), paths) == ScanRollback) {
            this->trackTransaction->Cancel();
        }
        this->trackTransaction->CommitAndRestart();

        if (sourceId != 0) {
            break; /* done with the one we were asked to scan */
        }
    }

    this->currentSource.reset();

    /* process local files */
    if (type != SyncType::Sources) {
        if (logFile) {
            fprintf(logFile, "\n\nSYNCING LOCAL FILES:\n");
        }

        /* read metadata from the files  */
        for (std::size_t i = 0; i < paths.size(); ++i) {
            musik::debug::info(TAG, "scanning " + paths[i]);
            this->SyncDirectory(io, paths[i], paths[i], pathIds[i]);
        }

        /* close any pending transaction */
        this->trackTransaction->CommitAndRestart();

        /* re-index */
        LocalLibrary::CreateIndexes(this->dbConnection);
    }
}

void Indexer::FinalizeSync(const SyncContext& context) {
    /* remove undesired entries from db (files themselves will remain) */
    musik::debug::info(TAG, "cleanup 1/2");

    const auto type = context.type;

    if (type != SyncType::Sources) {
        if (!this->Bail()) {
            this->SyncDelete();
        }
    }

    /* cleanup -- remove stale artists, albums, genres, etc */
    musik::debug::info(TAG, "cleanup 2/2");

    if (!this->Bail()) {
        this->SyncCleanup();
    }

    /* optimize and sort */
    musik::debug::info(TAG, "optimizing");

    if (!this->Bail()) {
        this->SyncOptimize();
    }

    /* run analyzers. */
    this->RunAnalyzers();

    IndexerTrack::OnIndexerFinished(this->dbConnection);
}

void Indexer::ReadMetadataFromFile(
    boost::asio::io_service* io,
    const boost::filesystem::path& file,
    const std::string& pathId)
{
    /* we do this here because work may have already been queued before the abort
    flag was raised */
    if (io && this->Bail()) {
        if (!io->stopped()) {
            musik::debug::info(TAG, "run aborted");
            io->stop();
        }
        return;
    }

    #define APPEND_LOG(x) if (logFile) { fprintf(logFile, "    - [%s] %s\n", x, file.string().c_str()); }

    musik::core::IndexerTrack track(0);

    const bool needsToBeIndexed = track.NeedsToBeIndexed(file, this->dbConnection);

    /* get cached filesize, parts, size, etc */
    if (needsToBeIndexed) {
        APPEND_LOG("needs to be indexed")

        bool saveToDb = false;

        /* read the tag from the plugin */
        TagStore store(track);
        typedef TagReaderList::iterator Iterator;
        Iterator it = this->tagReaders.begin();
        while (it != this->tagReaders.end()) {
            try {
                if ((*it)->CanRead(track.GetString("extension").c_str())) {
                    APPEND_LOG("can read")
                    if ((*it)->Read(file.string().c_str(), &store)) {
                        APPEND_LOG("did read")
                        saveToDb = true;
                        break;
                    }
                }
            }
            catch (...) {
                /* sometimes people have files with crazy tags that cause the
                tag reader to throw fits. not a lot we can do. just move on. */
            }

            it++;
        }

        /* write it to the db, if read successfully */
        if (saveToDb) {
            track.SetValue("path_id", pathId.c_str());
            track.Save(this->dbConnection, this->libraryPath);

#if STRESS_TEST_DB != 0
            #define INC(track, key, x) \
                { \
                    std::string val = track.GetValue(key); \
                    val += (char) ('a' + x); \
                    track.ClearValue(key); \
                    track.SetValue(key, val.c_str()); \
                }

            for (int i = 0; i < 20; i++) {
                track.SetId(0);
                INC(track, "title", i);
                INC(track, "artist", i);
                INC(track, "album_artist", i);
                INC(track, "album", i);
                track.Save(this->dbConnection, this->libraryPath);
            }
#endif
        }
        else {
            APPEND_LOG("read failed")
        }
    }
    else {
        APPEND_LOG("does not need to be indexed")
    }

    #undef APPEND_LOG

    this->IncrementTracksScanned();
}

inline void Indexer::IncrementTracksScanned(int delta) {
    std::unique_lock<std::mutex> lock(IndexerTrack::sharedWriteMutex);

    this->incrementalUrisScanned.fetch_add(delta);
    this->totalUrisScanned.fetch_add(delta);

    const int interval = prefs->GetInt(
        prefs::keys::IndexerTransactionInterval, TRANSACTION_INTERVAL);

    if (this->incrementalUrisScanned > TRANSACTION_INTERVAL) {
        this->trackTransaction->CommitAndRestart();
        this->Progress(this->totalUrisScanned);
        this->incrementalUrisScanned = 0;
    }
}

void Indexer::SyncDirectory(
    boost::asio::io_service* io,
    const std::string &syncRoot,
    const std::string &currentPath,
    int64_t pathId)
{
    std::string normalizedSyncRoot = NormalizeDir(syncRoot);
    std::string normalizedCurrentPath = NormalizeDir(currentPath);
    std::string leaf = boost::filesystem::path(currentPath).leaf().string(); /* trailing subdir in currentPath */

    /* start recursive filesystem scan */

    try { /* boost::filesystem may throw */

        /* for each file in the current path... */

        boost::filesystem::path path(currentPath);
        boost::filesystem::directory_iterator end;
        boost::filesystem::directory_iterator file(path);

        std::string pathIdStr = std::to_string(pathId);
        std::vector<Thread> threads;

        for( ; file != end && !this->Bail(); file++) {
            if (this->Bail()) {
                break;
            }
            if (is_directory(file->status())) {
                /* recursion here */
                /* musik::debug::info(TAG, "scanning subdirectory " + file->path().string()); */
                this->SyncDirectory(io, syncRoot, file->path().string(), pathId);
            }
            else {
                std::string extension = file->path().extension().string();
                for (auto it : this->tagReaders) {
                    if (it->CanRead(extension.c_str())) {
                        if (io) {
                            io->post(boost::bind(
                                &Indexer::ReadMetadataFromFile,
                                this,
                                io,
                                file->path(),
                                pathIdStr));
                        }
                        else {
                            this->ReadMetadataFromFile(nullptr, file->path(), pathIdStr);
                        }
                        break;
                    }
                }

            }
        }
    }
    catch(...) {
    }
}

ScanResult Indexer::SyncSource(
    IIndexerSource* source,
    const std::vector<std::string>& paths)
{
    debug::info(TAG, u8fmt("indexer source %d running...", source->SourceId()));

    if (source->SourceId() == 0) {
        return ScanRollback;
    }

    /* only commit if explicitly succeeded */
    ScanResult result = ScanRollback;

    source->OnBeforeScan();

    try {
        /* alloc/init fun interop; we pass all paths to the indexer source */
        const char** pathsList = new const char*[paths.size()];
        for (size_t i = 0; i < paths.size(); i++) {
            auto& p = paths[i];
            auto sz = p.size();
            auto dst = new char[sz + 1];
            strncpy(dst, p.c_str(), sz);
            dst[sz] = '\0';
            pathsList[i] = dst;
        }

        /* now tell it to do a wide-open scan. it can use this opportunity to
        remove old tracks, or add new ones. */
        try {
            result = source->Scan(this, pathsList, (unsigned int) paths.size());
        }
        catch (...) {
            debug::error("Indexer", "failed to index " + std::to_string(source->SourceId()));
        }

        /* free fun interop -- it's done with the paths now. */
        for (size_t i = 0; i < paths.size(); i++) {
            delete[] pathsList[i];
        }
        delete[] pathsList;

        /* finally, allow the source to update metadata for any tracks that it
        previously indexed, if it needs to. */
        {
            if (!this->Bail() && source->NeedsTrackScan()) {
                db::Statement tracks(
                    "SELECT id, filename, external_id FROM tracks WHERE source_id=? ORDER BY id",
                    this->dbConnection);

                tracks.BindInt32(0, source->SourceId());
                while (tracks.Step() == db::Row) {
                    TrackPtr track = std::make_shared<IndexerTrack>(tracks.ColumnInt64(0));
                    track->SetValue(constants::Track::FILENAME, tracks.ColumnText(1));

                    if (logFile) {
                        fprintf(logFile, "    - %s\n", track->GetString(constants::Track::FILENAME).c_str());
                    }

                    TagStore* store = new TagStore(track);
                    source->ScanTrack(this, store, tracks.ColumnText(2));
                    store->Release();
                }
            }
        }

        debug::info(TAG, u8fmt("indexer source %d finished", source->SourceId()));
    }
    catch (...) {
        debug::error(TAG, u8fmt("indexer source %d crashed", source->SourceId()));
    }

    source->OnAfterScan();

    return result;
}

void Indexer::ThreadLoop() {
    boost::filesystem::path thumbPath(this->libraryPath + "thumbs/");

    if (!boost::filesystem::exists(thumbPath)) {
        boost::filesystem::create_directories(thumbPath);
    }

    while (true) {
        /* wait for some work. */
        {
            boost::mutex::scoped_lock lock(this->stateMutex);
            while (!this->Bail() && this->syncQueue.size() == 0) {
                this->state = StateIdle;
                this->waitCondition.wait(lock);
            }
        }

        if (this->Bail()) {
            return;
        }

        const SyncContext context = this->syncQueue.front();
        this->syncQueue.pop_front();

        this->state = StateIndexing;
        this->Started();

        this->dbConnection.Open(this->dbFilename.c_str(), 0);
        this->trackTransaction = std::make_shared<db::ScopedTransaction>(this->dbConnection);

        const int threadCount = prefs->GetInt(
            prefs::keys::IndexerThreadCount, DEFAULT_MAX_THREADS);

        if (threadCount > 1) {
            boost::asio::io_service io;
            boost::thread_group threadPool;
            boost::asio::io_service::work work(io);

            /* initialize the thread pool -- we'll use this to index tracks in parallel. */
            for (int i = 0; i < threadCount; i++) {
                threadPool.create_thread(boost::bind(&boost::asio::io_service::run, &io));
            }

            this->Synchronize(context, &io);

            /* done with sync, remove all the threads in the pool to free resources. they'll
            be re-created later if we index again. */
            io.post([&io]() {
                if (!io.stopped()) {
                    musik::debug::info(TAG, "scan completed successfully");
                    io.stop();
                }
            });
            threadPool.join_all();
        }
        else {
            this->Synchronize(context, nullptr);
        }

        this->FinalizeSync(context);

        this->trackTransaction.reset();

        this->dbConnection.Close();

        if (!this->Bail()) {
            this->Progress(this->totalUrisScanned);
            this->Finished(this->totalUrisScanned);
        }

        musik::debug::info(TAG, "done!");
    }
}

void Indexer::SyncDelete() {
    /* remove all tracks that no longer reference a valid path entry */

    this->dbConnection.Execute("DELETE FROM tracks WHERE source_id == 0 AND path_id NOT IN (SELECT id FROM paths)");

    /* remove files that are no longer on the filesystem. */

    if (prefs->GetBool(prefs::keys::RemoveMissingFiles, true)) {
        db::Statement stmtRemove("DELETE FROM tracks WHERE id=?", this->dbConnection);

        db::Statement allTracks(
            "SELECT t.id, t.filename "
            "FROM tracks t "
            "WHERE source_id == 0", /* IIndexerSources delete their own tracks */
            this->dbConnection);

        while (allTracks.Step() == db::Row && !this->Bail()) {
            bool remove = false;
            std::string fn = allTracks.ColumnText(1);

            try {
                boost::filesystem::path file(fn);
                if (!boost::filesystem::exists(file)) {
                    remove = true;
                }
            }
            catch (...) {
            }

            if (remove) {
                stmtRemove.BindInt32(0, allTracks.ColumnInt32(0));
                stmtRemove.Step();
                stmtRemove.Reset();
            }
        }
    }
}

void Indexer::SyncCleanup() {
    /* remove old artists */
    this->dbConnection.Execute("DELETE FROM track_artists WHERE track_id NOT IN (SELECT id FROM tracks)");
    this->dbConnection.Execute("DELETE FROM artists WHERE id NOT IN (SELECT DISTINCT(visual_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(album_artist_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(artist_id) FROM track_artists)");

    /* remove old genres */
    this->dbConnection.Execute("DELETE FROM track_genres WHERE track_id NOT IN (SELECT id FROM tracks)");
    this->dbConnection.Execute("DELETE FROM genres WHERE id NOT IN (SELECT DISTINCT(visual_genre_id) FROM tracks) AND id NOT IN (SELECT DISTINCT(genre_id) FROM track_genres)");

    /* remove old albums */
    this->dbConnection.Execute("DELETE FROM albums WHERE id NOT IN (SELECT DISTINCT(album_id) FROM tracks)");

    /* orphaned metadata */
    this->dbConnection.Execute("DELETE FROM track_meta WHERE track_id NOT IN (SELECT id FROM tracks)");
    this->dbConnection.Execute("DELETE FROM meta_values WHERE id NOT IN (SELECT DISTINCT(meta_value_id) FROM track_meta)");
    this->dbConnection.Execute("DELETE FROM meta_keys WHERE id NOT IN (SELECT DISTINCT(meta_key_id) FROM meta_values)");

    /* orphaned replay gain and directories */
    this->dbConnection.Execute("DELETE FROM replay_gain WHERE track_id NOT IN (SELECT id FROM tracks)");
    this->dbConnection.Execute("DELETE FROM directories WHERE id NOT IN (SELECT DISTINCT directory_id FROM tracks)");

    /* NOTE: we used to remove orphaned local library tracks here, but we don't anymore because
    the indexer generates stable external ids by hashing various file and metadata fields */

    /* orphaned playlist tracks from source plugins that do not have stable
    ids need to be cleaned up. */
    for (auto source : this->sources) {
        if (!source->HasStableIds()) {
            std::string query =
                "DELETE FROM playlist_tracks "
                "WHERE source_id=? AND track_external_id NOT IN ( "
                "  SELECT DISTINCT external_id "
                "  FROM tracks "
                "  WHERE source_id == ?)";

            db::Statement stmt(query.c_str(), this->dbConnection);
            stmt.BindInt32(0, source->SourceId());
            stmt.BindInt32(1, source->SourceId());
            stmt.Step();
        }
    }

    this->SyncPlaylistTracksOrder();

    /* optimize and shrink */
    this->dbConnection.Execute("VACUUM");
}

void Indexer::SyncPlaylistTracksOrder() {
    /* make sure playlist sort orders are always sequential without holes. we
    do this anyway, as playlists are updated, but there's no way to guarantee
    it stays this way -- plugins, external processes, etc can cause problems */

    db::Statement playlists(
        "SELECT DISTINCT id FROM playlists",
        this->dbConnection);

    db::Statement tracks(
        "SELECT track_external_id, sort_order "
        "FROM playlist_tracks WHERE playlist_id=? "
        "ORDER BY sort_order",
        this->dbConnection);

    db::Statement update(
        "UPDATE playlist_tracks "
        "SET sort_order=? "
        "WHERE track_external_id=? AND sort_order=?",
        this->dbConnection);

    struct Record { std::string id; int order; };

    while (playlists.Step() == db::Row) {
        tracks.ResetAndUnbind();
        tracks.BindInt64(0, playlists.ColumnInt64(0));

        /* gotta cache these in memory because we can't update the
        table at the same time we're iterating */
        std::vector<Record> records;
        while (tracks.Step() == db::Row) {
            records.push_back({ tracks.ColumnText(0), tracks.ColumnInt32(1) });
        }

        int order = 0;
        for (auto& r : records) {
            update.ResetAndUnbind();
            update.BindInt32(0, order++);
            update.BindText(1, r.id);
            update.BindInt32(2, r.order);
            update.Step();
        }
    }
}

void Indexer::GetPaths(std::vector<std::string>& paths) {
    boost::mutex::scoped_lock lock(this->stateMutex);
    std::copy(this->paths.begin(), this->paths.end(), std::back_inserter(paths));
}

std::set<int> Indexer::GetOrphanedSourceIds() {
    /* build a list of source ids: `(x, y, z)` */
    std::string group = "(0"; /* 0 is the built-in source, it's always valid */
    for (size_t i = 0; i < this->sources.size(); i++) {
        group += "," + std::to_string(this->sources.at(i)->SourceId());
    }
    group += ")";

    std::string query =
        "SELECT DISTINCT source_id "
        "FROM tracks "
        "WHERE source_id NOT IN " + group;

    std::set<int> result;
    db::Statement stmt(query.c_str(), this->dbConnection);
    while (stmt.Step() == db::Row) {
        result.insert(stmt.ColumnInt32(0));
    }
    return result;
}

static int optimize(
    musik::core::db::Connection &connection,
    std::string singular,
    std::string plural)
{

    std::string outer = u8fmt(
        "SELECT id, lower(trim(name)) AS %s FROM %s ORDER BY %s",
        singular.c_str(), plural.c_str(), singular.c_str());

    db::Statement outerStmt(outer.c_str(), connection);

    std::string inner = u8fmt("UPDATE %s SET sort_order=? WHERE id=?", plural.c_str());
    db::Statement innerStmt(inner.c_str(), connection);

    int count = 0;
    while (outerStmt.Step() == db::Row) {
        innerStmt.BindInt32(0, count);
        innerStmt.BindInt64(1, outerStmt.ColumnInt64(0));
        innerStmt.Step();
        innerStmt.Reset();
        ++count;
    }

    boost::thread::yield();

    return count;
}

void Indexer::SyncOptimize() {
    db::ScopedTransaction transaction(this->dbConnection);
    optimize(this->dbConnection, "genre", "genres");
    optimize(this->dbConnection, "artist", "artists");
    optimize(this->dbConnection, "album", "albums");
    optimize(this->dbConnection, "content", "meta_values");
}

void Indexer::ProcessAddRemoveQueue() {
    boost::mutex::scoped_lock lock(this->stateMutex);

    while (!this->addRemoveQueue.empty()) {
        AddRemoveContext context = this->addRemoveQueue.front();

        if (context.add) { /* insert new paths */
            db::Statement stmt("SELECT id FROM paths WHERE path=?", this->dbConnection);
            stmt.BindText(0, context.path);

            if (stmt.Step() != db::Row) {
                db::Statement insertPath("INSERT INTO paths (path) VALUES (?)", this->dbConnection);
                insertPath.BindText(0, context.path);
                insertPath.Step();
            }
        }
        else { /* remove old ones */
            db::Statement stmt("DELETE FROM paths WHERE path=?", this->dbConnection);
            stmt.BindText(0, context.path);
            stmt.Step();
        }

        this->addRemoveQueue.pop_front();
    }
}

void Indexer::RunAnalyzers() {
    typedef sdk::IAnalyzer PluginType;
    typedef PluginFactory::ReleaseDeleter<PluginType> Deleter;
    typedef std::shared_ptr<PluginType> PluginPtr;
    typedef std::vector<PluginPtr> PluginVector;

    /* short circuit if there aren't any analyzers */

    PluginVector analyzers = PluginFactory::Instance()
        .QueryInterface<PluginType, Deleter>("GetAudioAnalyzer");

    if (analyzers.empty()) {
        return;
    }

    /* for each track... */

    int64_t trackId = 0;

    db::Statement getNextTrack(
        "SELECT id FROM tracks WHERE id>? ORDER BY id LIMIT 1",
        this->dbConnection);

    getNextTrack.BindInt64(0, trackId);

    while(getNextTrack.Step() == db::Row ) {
        trackId = getNextTrack.ColumnInt64(0);

        getNextTrack.ResetAndUnbind();

        auto track = std::make_shared<IndexerTrack>(trackId);
        TrackMetadataQuery query(track, LibraryFactory::Instance().DefaultLocalLibrary());
        query.Run(this->dbConnection);

        if (query.GetStatus() == IQuery::Finished) {
            PluginVector runningAnalyzers;

            TagStore store(track);
            for (auto plugin : analyzers) {
                if (plugin->Start(&store)) {
                    runningAnalyzers.push_back(plugin);
                }
            }

            if (!runningAnalyzers.empty()) {
                audio::IStreamPtr stream = audio::Stream::Create(2048, 2.0, StreamFlags::NoDSP);

                if (stream) {
                    if (stream->OpenStream(track->Uri(), nullptr)) {

                        /* decode the stream quickly, passing to all analyzers */

                        IBuffer* buffer;

                        while ((buffer = stream->GetNextProcessedOutputBuffer()) && !runningAnalyzers.empty()) {
                            PluginVector::iterator plugin = runningAnalyzers.begin();
                            while(plugin != runningAnalyzers.end()) {
                                if ((*plugin)->Analyze(&store, buffer)) {
                                    ++plugin;
                                }
                                else {
                                    plugin = runningAnalyzers.erase(plugin);
                                }
                            }
                        }

                        /* done with track decoding and analysis, let the plugins know */

                        int successPlugins = 0;
                        PluginVector::iterator plugin = analyzers.begin();

                        for ( ; plugin != analyzers.end(); ++plugin) {
                            if ((*plugin)->End(&store)) {
                                successPlugins++;
                            }
                        }

                        /* the analyzers can write metadata back to the DB, so if any of them
                        completed successfully, then save the track. */

                        if (successPlugins>0) {
                            track->Save(this->dbConnection, this->libraryPath);
                        }
                    }
                }
            }
        }

        if (this->Bail()) {
            return;
        }

        getNextTrack.BindInt64(0, trackId);
    }
}

ITagStore* Indexer::CreateWriter() {
    return new TagStore(std::make_shared<IndexerTrack>(0));
}

bool Indexer::Save(IIndexerSource* source, ITagStore* store, const char* externalId) {
    if (!source || source->SourceId() == 0 || !store) {
        return false;
    }

    if (!externalId || strlen(externalId) == 0) {
        return false;
    }

    /* two levels of unpacking with dynamic_casts. don't tell anyone,
    it'll be our little secret. */
    TagStore* ts = dynamic_cast<TagStore*>(store);
    if (ts) {
        IndexerTrack* it = ts->As<IndexerTrack*>();
        if (it) {
            it->SetValue(constants::Track::EXTERNAL_ID, externalId);
            it->SetValue(constants::Track::SOURCE_ID, std::to_string(source->SourceId()).c_str());
            return it->Save(this->dbConnection, this->libraryPath);
        }
    }
    return false;
}

bool Indexer::RemoveByUri(IIndexerSource* source, const char* uri) {
    if (!source || source->SourceId() == 0) {
        return false;
    }

    if (!uri || strlen(uri) == 0) {
        return false;
    }

    db::Statement stmt(
        "DELETE FROM tracks WHERE source_id=? AND filename=?",
        this->dbConnection);

    stmt.BindInt32(0, source->SourceId());
    stmt.BindText(1, uri);

    return (stmt.Step() == db::Okay);
}

bool Indexer::RemoveByExternalId(IIndexerSource* source, const char* id) {
    if (!source || source->SourceId() == 0) {
        return false;
    }

    if (!id || strlen(id) == 0) {
        return false;
    }

    db::Statement stmt(
        "DELETE FROM tracks WHERE source_id=? AND external_id=?",
        this->dbConnection);

    stmt.BindInt32(0, source->SourceId());
    stmt.BindText(1, id);

    return (stmt.Step() == db::Okay);
}

int Indexer::RemoveAll(IIndexerSource* source) {
    if (!source) {
        return 0;
    }

    const auto id = source->SourceId();
    return (id != 0) ? this->RemoveAllForSourceId(id) : 0;
}

int Indexer::RemoveAllForSourceId(int sourceId) {
    db::Statement stmt("DELETE FROM tracks WHERE source_id=?", this->dbConnection);
    stmt.BindInt32(0, sourceId);
    return (stmt.Step() == db::Okay) ? dbConnection.LastModifiedRowCount() : 0;
}

void Indexer::CommitProgress(IIndexerSource* source, unsigned updatedTracks) {
    if (source &&
        this->currentSource &&
        this->currentSource->SourceId() == source->SourceId() &&
        trackTransaction)
    {
        trackTransaction->CommitAndRestart();
    }

    if (updatedTracks) {
        this->IncrementTracksScanned(updatedTracks);
    }
}

int Indexer::GetLastModifiedTime(IIndexerSource* source, const char* externalId) {
    if (source && externalId && strlen(externalId)) {
        db::Statement stmt("SELECT filetime FROM tracks t where source_id=? AND external_id=?", dbConnection);
        stmt.BindInt32(0, source->SourceId());
        stmt.BindText(1, externalId);
        if (stmt.Step() == db::Row) {
            return stmt.ColumnInt32(0);
        }
    }

    return -1;
}

void Indexer::ScheduleRescan(IIndexerSource* source) {
    if (source && source->SourceId() != 0) {
        this->Schedule(SyncType::Sources, source);
    }
}

bool Indexer::Bail() noexcept {
    return
        this->state == StateStopping ||
        this->state == StateStopped;
}

max_stars_repo_name: heholek/musikcube

max_issues_repo_name: heholek/musikcube

max_forks_repo_name: heholek/musikcube


text:
/*

Copyright (c) 2015, Arvid Norberg
All rights reserved.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "simulator/simulator.hpp"

#include <boost/make_shared.hpp>
#include <boost/system/error_code.hpp>

namespace sim { namespace asio {

	io_context::io_context(sim::simulation& sim)
		: io_context(sim, std::vector<asio::ip::address>())
	{}

	io_context::io_context(sim::simulation& sim, asio::ip::address const& ip)
		: io_context(sim, std::vector<asio::ip::address>{ip})
	{}

	io_context::io_context(sim::simulation& sim, std::vector<asio::ip::address> const& ips)
		: m_sim(sim)
		, m_ips(ips)
		, m_stopped(false)
	{
		for (auto const& ip : m_ips)
		{
			m_outgoing_route[ip] = m_sim.config().outgoing_route(ip);
			m_incoming_route[ip] = m_sim.config().incoming_route(ip);
		}
		m_sim.add_io_service(this);
	}

	io_context::~io_context()
	{
		m_sim.remove_io_service(this);
	}

	io_context::io_context(std::size_t)
		: m_sim(*reinterpret_cast<sim::simulation*>(0))
	{
		assert(false);
	}

	int io_context::get_path_mtu(const asio::ip::address& source, const asio::ip::address& dest) const
	{
		// TODO: it would be nice to actually traverse the virtual network nodes
		// and ask for their MTU instead
		assert(std::count(m_ips.begin(), m_ips.end(), source) > 0 && "source address must be a local address to this node/io_context");
		return m_sim.config().path_mtu(source, dest);
	}

	void io_context::stop()
	{
		// TODO: cancel all outstanding handler associated with this io_context
		m_stopped = true;
	}

	bool io_context::stopped() const
	{
		return m_stopped;
	}

	void io_context::restart()
	{
		m_stopped = false;
	}

	std::size_t io_context::run()
	{
		assert(false);
		return 0;
	}

	std::size_t io_context::run(boost::system::error_code&)
	{
		assert(false);
		return 0;
	}

	std::size_t io_context::poll()
	{
		assert(false);
		return 0;
	}

	std::size_t io_context::poll(boost::system::error_code&)
	{
		assert(false);
		return 0;
	}

	std::size_t io_context::poll_one()
	{
		assert(false);
		return 0;
	}

	std::size_t io_context::poll_one(boost::system::error_code&)
	{
		assert(false);
		return 0;
	}

	// private interface

	void io_context::add_timer(high_resolution_timer* t)
	{
		m_sim.add_timer(t);
	}

	void io_context::remove_timer(high_resolution_timer* t)
	{
		m_sim.remove_timer(t);
	}

	boost::asio::io_context& io_context::get_internal_service()
	{ return m_sim.get_internal_service(); }

	ip::tcp::endpoint io_context::bind_socket(ip::tcp::socket* socket
		, ip::tcp::endpoint ep, boost::system::error_code& ec)
	{
		assert(!m_ips.empty() && "you cannot use an internal io_context (one without an IP address) for creating and binding sockets");
		if (ep.address() == ip::address_v4::any())
		{
			auto it = std::find_if(m_ips.begin(), m_ips.end()
				, [](ip::address const& ip) { return ip.is_v4(); } );
			if (it == m_ips.end())
			{
				ec.assign(boost::system::errc::address_not_available
					, boost::system::generic_category());
				return ip::tcp::endpoint();
			}
			// TODO: pick the first local endpoint for now. In the future we may
			// want have a bias toward
			ep.address(*it);
		}
		else if (ep.address() == ip::address_v6::any())
		{
			auto it = std::find_if(m_ips.begin(), m_ips.end()
				, [](ip::address const& ip) { return ip.is_v6(); } );
			if (it == m_ips.end())
			{
				ec.assign(boost::system::errc::address_not_available
					, boost::system::generic_category());
				return ip::tcp::endpoint();
			}
			// TODO: pick the first local endpoint for now. In the future we may
			// want have a bias toward
			ep.address(*it);
		}
		else if (std::count(m_ips.begin(), m_ips.end(), ep.address()) == 0)
		{
			// you can only bind to the IP assigned to this node.
			// TODO: support loopback
			ec.assign(boost::system::errc::address_not_available
				, boost::system::generic_category());
			return ip::tcp::endpoint();
		}

		return m_sim.bind_socket(socket, ep, ec);
	}

	void io_context::unbind_socket(ip::tcp::socket* socket
		, const ip::tcp::endpoint& ep)
	{
		m_sim.unbind_socket(socket, ep);
	}

	void io_context::rebind_socket(asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep)
	{
		m_sim.rebind_socket(s, ep);
	}

	ip::udp::endpoint io_context::bind_udp_socket(ip::udp::socket* socket
		, ip::udp::endpoint ep, boost::system::error_code& ec)
	{
		assert(!m_ips.empty() && "you cannot use an internal io_context (one without an IP address) for creating and binding sockets");
		if (ep.address() == ip::address_v4::any())
		{
			auto it = std::find_if(m_ips.begin(), m_ips.end()
				, [](ip::address const& ip) { return ip.is_v4(); });
			if (it == m_ips.end())
			{
				ec.assign(boost::system::errc::address_not_available
					, boost::system::generic_category());
				return ip::udp::endpoint();
			}
			// TODO: pick the first local endpoint for now. In the future we may
			// want have a bias toward
			ep.address(*it);
		}
		else if (ep.address() == ip::address_v6::any())
		{
			auto it = std::find_if(m_ips.begin(), m_ips.end()
				, [](ip::address const& ip) { return ip.is_v6(); });
			if (it == m_ips.end())
			{
				ec.assign(boost::system::errc::address_not_available
						  , boost::system::generic_category());
				return ip::udp::endpoint();
			}
			// TODO: pick the first local endpoint for now. In the future we may
			// want have a bias toward
			ep.address(*it);
		}
		else if (std::count(m_ips.begin(), m_ips.end(), ep.address()) == 0)
		{
			// you can only bind to the IP assigned to this node.
			// TODO: support loopback
			ec.assign(boost::system::errc::address_not_available
				, boost::system::generic_category());
			return ip::udp::endpoint();
		}

		return m_sim.bind_udp_socket(socket, ep, ec);
	}

	void io_context::unbind_udp_socket(ip::udp::socket* socket
		, const ip::udp::endpoint& ep)
	{
		m_sim.unbind_udp_socket(socket, ep);
	}

	void io_context::rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep)
	{
		m_sim.rebind_udp_socket(socket, ep);
	}

	std::shared_ptr<aux::channel> io_context::internal_connect(ip::tcp::socket* s
		, ip::tcp::endpoint const& target, boost::system::error_code& ec)
	{
		return m_sim.internal_connect(s, target, ec);
	}

	route io_context::find_udp_socket(asio::ip::udp::socket const& socket
		, ip::udp::endpoint const& ep)
	{
		return m_sim.find_udp_socket(socket, ep);
	}

} // asio
} // sim

ext: cpp

max_stars_repo_name: SylemST-UtilCollection/libtorrent-build

max_issues_repo_name: SylemST-UtilCollection/libtorrent-build

max_forks_repo_name: SylemST-UtilCollection/libtorrent-build


text:
//
// Created by kellerberrin on 15/2/20.
//

#include "kel_property_tree.h"
#include "kel_utility.h"

// To stop boost complaining about global placeholders on boost/bind.
#define BOOST_BIND_GLOBAL_PLACEHOLDERS 1
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>

#include <sstream>
#include <stack>

namespace kel = kellerberrin;
namespace pt = boost::property_tree;
namespace lex = boost::spirit::lex;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PropertyTree::PropertyImpl does all the heavy lifting using boost::property_tree.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

using ImplSubTree = std::pair<std::string, kel::PropertyTree::PropertyImpl>;

class kel::PropertyTree::PropertyImpl {

public:

  PropertyImpl() = default;
  PropertyImpl(const pt::ptree& property_tree) : property_tree_(property_tree) {}
  PropertyImpl(const PropertyImpl&) =default;
  ~PropertyImpl() = default;

  bool readPropertiesFile(const std::string& properties_file);

//  bool getProperty(const std::string& property_name, std::string& property) const { return getProperty(property_tree_, property_name, property); }

  bool getProperty(const std::string& property_name, std::string& property) const;

  bool getPropertyVector(const std::string& property_name, std::vector<std::string>& property_vector) const;

  bool getNodeVector(const std::string& node_name, std::vector<std::string>& node_data_vector) const;

  bool getProperty(const std::string& property_name, size_t& property) const;

  void treeTraversal() const;

  bool checkProperty(const std::string& property_name) const;

  bool getTreeVector(const std::string& property_name, std::vector<ImplSubTree>& tree_vector) const;

  bool getTreeVector(std::vector<std::pair<std::string, PropertyImpl>>& tree_vector) const;

    template<class T> T getData() const { return property_tree_.get_value<T>(); }

private:

  constexpr static const char* JSON_FILE_EXT_{"JSON"};
  constexpr static const char* INCLUDE_TOKEN_{"#include"};
  constexpr static const char* LINE_IGNORE_{"//"};
  constexpr static const char INCLUDE_FILE_QUOTE_{'\"'};

  // boost property tree object
  pt::ptree property_tree_;

  void printTree(const std::string& parent, const pt::ptree& property_tree) const;
  bool getPropertyTree(const pt::ptree& property_tree, const std::string& property_name, std::string& property) const;
  // These functions are recursive and throw file exceptions which are caught in getPropertyTree().
  std::stringstream preProcessPropertiesFile(const std::string& properties_file);
  void readRecursive(std::stringstream& ss, const std::string& properties_file, size_t& file_count);

};

// These functions throw file exceptions.
std::stringstream kel::PropertyTree::PropertyImpl::preProcessPropertiesFile(const std::string& properties_file) {

  std::stringstream ss;
  size_t file_count{0};

  readRecursive(ss, properties_file, file_count);

  ExecEnv::log().info("Runtime definition XML files parsed: {}", file_count);

  return ss;

}

// This function preprocesses the runtime XML file by allowing a c++ style include directive, e.g. #include "subdir/include.xml".
// This allows the runtime XML file to be broken up and simplified.
// Redundant include statements can be disabled by prefixing with '//' in the first two characters of the line.
// For example '//#include "subdir/include.xml' is a disabled include statement.
void kel::PropertyTree::PropertyImpl::readRecursive(std::stringstream& ss, const std::string& properties_file, size_t& file_count) {

  static const size_t include_token_size = std::string(INCLUDE_TOKEN_).size();
  static const size_t ignore_size = std::string(LINE_IGNORE_).size();

  ++file_count;
  std::ifstream xml_file(properties_file);

  if (not xml_file.good()) {

    ExecEnv::log().error("PropertyImpl::preProcessPropertiesFile; Unable to open runtime XML file: {}", properties_file);
    throw std::runtime_error(properties_file);

  }

  std::string line;
  while(std::getline(xml_file, line))
  {
    // If the first two characters are "//" then the line is ignored.
    std::string ignore_text = line.substr(0, ignore_size);
    if (ignore_text == LINE_IGNORE_) {

      continue;

    }

    // Recursively include XML files.
    if (line.find(INCLUDE_TOKEN_) != std::string::npos) {

      // The include XML file spec should be in quotes, e.g #include "subdir/include.xml".
      std::string file_spec = Utility::trimAllWhiteSpace(line.substr(include_token_size));
      file_spec = Utility::trimAllChar(file_spec, INCLUDE_FILE_QUOTE_);
      file_spec = Utility::filePath(file_spec, Utility::filePath(properties_file));
      readRecursive(ss, file_spec, file_count);

    } else {

      ss << line << '\n';

    }

  }

}

bool kel::PropertyTree::PropertyImpl::readPropertiesFile(const std::string& properties_file) {

  std::string uc_file_extension = Utility::toupper(Utility::fileExtension(properties_file));

  try {

    if (uc_file_extension == JSON_FILE_EXT_) {

      pt::read_json(properties_file, property_tree_);

    } else {

      std::stringstream ss = preProcessPropertiesFile(properties_file);
      pt::read_xml(ss, property_tree_);

    }

  }
  catch(std::runtime_error& e) {

    ExecEnv::log().error("PropertyTree; Missing or Malformed property tree in file: {}, error: {}", properties_file, e.what());
    return false;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::checkProperty(const std::string& property_name) const {

  try {

    property_tree_.get<std::string>(property_name);

  }
  catch (...) {

    return false;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getProperty(const std::string& property_name,  std::string& property) const {

  try {

    property = property_tree_.get<std::string>(property_name);
    property = Utility::trimEndWhiteSpace(property);
    if (property.empty()) {

      ExecEnv::log().error("PropertyTree; Well-formed Property Tree but Property: {} not found or NULL", property_name);
      return false;

    }

  }
  catch (const std::runtime_error& e) {

    ExecEnv::log().error("Exception: PropertyTree GetProperty; Property: {} not found, error: {}", property_name, e.what());
    ExecEnv::log().error("*********** Property Tree Contents *************");
    treeTraversal();
    ExecEnv::log().error("**********************************************");
    return false;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getPropertyTree(const pt::ptree& property_tree, const std::string& property_name,  std::string& property) const {

  try {

    property = property_tree.get<std::string>(property_name);
    property = Utility::trimEndWhiteSpace(property);

  }
  catch (const std::runtime_error& e) {

    ExecEnv::log().error("PropertyTree; Property: {} not found, error: {}", property_name, e.what());
    ExecEnv::log().error("***********Property Tree Contents*************");
    treeTraversal();
    ExecEnv::log().error("**********************************************");
    return false;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getPropertyVector(const std::string& property_name, std::vector<std::string>& property_vector) const {

  try {

    for (auto child : property_tree_.get_child(property_name)) {

      // The data function is used to access the data stored in a node.
      property_vector.push_back(Utility::trimEndWhiteSpace(child.second.data()));

    }

  }
  catch (const std::runtime_error& e) {

    ExecEnv::log().error("PropertyTree::getPropertyVector; Property Vector: {} not found, error: {}", property_name, e.what());
    ExecEnv::log().error("***********Property Tree Contents*************");
    treeTraversal();
    ExecEnv::log().error("**********************************************");
    return false;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getNodeVector(const std::string& node_name, std::vector<std::string>& node_vector) const {

  try {

    for (auto const& tree : property_tree_) {

      if (tree.first == node_name) {

        node_vector.push_back(tree.second.get_value<std::string>());

      }

    }

  }
  catch (const std::runtime_error& e) {

    ExecEnv::log().error("PropertyTree::getPropertyVector; Property Vector: {} not found, error: {}", node_name, e.what());
    ExecEnv::log().error("***********Property Tree Contents*************");
    treeTraversal();
    ExecEnv::log().error("**********************************************");
    return false;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getTreeVector(std::vector<std::pair<std::string, PropertyImpl>>& tree_vector) const {

  tree_vector.clear();

  try {

    for (auto const& sub_tree : property_tree_) {

      tree_vector.emplace_back(ImplSubTree(sub_tree.first, PropertyImpl(sub_tree.second)));

    }

  }
  catch (...) {

    // No sub-tree is not an error.
    return true;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getTreeVector(const std::string& property_name, std::vector<std::pair<std::string, PropertyImpl>>& tree_vector) const {

  tree_vector.clear();

  try {

    for (auto sub_tree : property_tree_.get_child(property_name)) {

      tree_vector.emplace_back(ImplSubTree(sub_tree.first, PropertyImpl(sub_tree.second)));

    }

  }
  catch (...) {

    // No sub-tree is not an error.
    return true;

  }

  return true;

}

bool kel::PropertyTree::PropertyImpl::getProperty(const std::string& property_name, size_t& property) const {

  std::string size_string;
  if (not getProperty(property_name, size_string)) {

    return false;

  }

  try {

    property= std::stoull(size_string);

  }
  catch (const std::runtime_error& e) {

    ExecEnv::log().error("PropertyTree; Property: {}, Value: {} is not an unsigned integer, error: {}", property_name, size_string, e.what());
    return false;

  }

  return true;

}

void kel::PropertyTree::PropertyImpl::treeTraversal() const {

  printTree("", property_tree_);

}

void kel::PropertyTree::PropertyImpl::printTree(const std::string& parent, const pt::ptree& property_tree) const {

  for (auto item : property_tree) {

    std::string key = item.first;
    std::string parent_key;
    if (parent.empty()) {

      parent_key = key;

    } else {

      parent_key = parent + "." + key;

    }
    std::string value = item.second.data();
    value = Utility::trimEndWhiteSpace(value);
    if (not value.empty()) {

      ExecEnv::log().info("PropertyTree; key: {}, value: {}", parent_key, value);

    } else {

      ExecEnv::log().info("PropertyTree; key: {}", parent_key);

    }
    printTree(parent_key, item.second);

  }

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PropertyTree is a public facade class that passes the functionality onto PropertyTree::PropertyImpl.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

kel::PropertyTree::PropertyTree() : properties_impl_ptr_(std::make_unique<kel::PropertyTree::PropertyImpl>()) {}

kel::PropertyTree::PropertyTree(const PropertyTree& property_tree) : properties_impl_ptr_(std::make_unique<kel::PropertyTree::PropertyImpl>(*(property_tree.properties_impl_ptr_))) {}

kel::PropertyTree::PropertyTree(const PropertyImpl& properties_impl) : properties_impl_ptr_(std::make_unique<kel::PropertyTree::PropertyImpl>(properties_impl)) {}

kel::PropertyTree::~PropertyTree() {}  // DO NOT DELETE or USE DEFAULT. Required because of incomplete pimpl type.

// Functionality passed to the implmentation.

bool kel::PropertyTree::readProperties(const std::string& properties_file) {

  return properties_impl_ptr_->readPropertiesFile(properties_file);

}

bool kel::PropertyTree::getProperty(const std::string& property_name, std::string& property) const {

  return properties_impl_ptr_->getProperty(property_name, property);

}

bool kel::PropertyTree::getProperty(const std::string& property_name, size_t& property) const {

  return properties_impl_ptr_->getProperty(property_name, property);

}

bool kel::PropertyTree::getOptionalProperty(const std::string& property_name, std::string& property) const {

  if (checkProperty(property_name)) {

    return properties_impl_ptr_->getProperty(property_name, property);

  } else {

    return false;

  }

}

void kel::PropertyTree::treeTraversal() const {

  return properties_impl_ptr_->treeTraversal();

}

bool kel::PropertyTree::checkProperty(const std::string& property_name) const {

  return properties_impl_ptr_->checkProperty(property_name);

}

bool kel::PropertyTree::getFileProperty(const std::string& property_name, const std::string& work_directory, std::string& file_path) const {

  std::string file_name;

  if (not getProperty(property_name, file_name)) {

    ExecEnv::log().warn("PropertyTree::getFileProperty; Requested file property: {} not found. A list of all valid properties follows:",
                        property_name);

    treeTraversal();
    return false;

  }

  file_path = Utility::filePath(file_name, work_directory);

  if (not Utility::fileExists(file_path)) {

    ExecEnv::log().warn("PropertyTree::getFileProperty; File: {} does not exist", file_path);
    return false;

  }

  return true;

}

bool kel::PropertyTree::getFileCreateProperty(const std::string& property_name, const std::string& work_directory, std::string& file_path) const {

  std::string file_name;

  if (not getProperty(property_name, file_name)) {

    ExecEnv::log().warn("PropertyTree::getFileCreateProperty; Requested file property: {} not found. A list of all valid properties follows:",
                        property_name);

    treeTraversal();
    return false;

  }

  file_path = Utility::filePath(file_name, work_directory);

  if (Utility::fileExists(file_path)) {

    return true;

  }

  if (Utility::fileExistsCreate(file_path)) {

    ExecEnv::log().info("Requested Property; Created file: {}", file_path);
    return true;

  } else {

    ExecEnv::log().warn("PropertyTree::getFileCreateProperty; File: {} does not exist and could not be created", file_path);
    return false;

  }

}

bool kel::PropertyTree::getOptionalFileProperty(const std::string& property_name, const std::string& work_directory, std::string& file_path) const {

  std::string file_name;

  if (not getOptionalProperty(property_name, file_name)) {

    return false;

  }

  file_path = Utility::filePath(file_name, work_directory);

  if (not Utility::fileExists(file_path)) {

    ExecEnv::log().critical("PropertyTree::getFileProperty; Specified optional File: {} does not exist, remove from XML specification.", file_path);
    return false;

  }

  return true;

}

bool kel::PropertyTree::getPropertyVector(const std::string& property_name, std::vector<std::string>& property_vector) const {

  return properties_impl_ptr_->getPropertyVector(property_name, property_vector);

}

bool kel::PropertyTree::getNodeVector(const std::string& node_name, std::vector<std::string>& node_vector) const {

  return properties_impl_ptr_->getNodeVector(node_name, node_vector);

}

bool kel::PropertyTree::getPropertyTreeVector(const std::string& property_name, std::vector<SubPropertyTree>& property_tree_vector) const {

  property_tree_vector.clear();

  std::vector<ImplSubTree> tree_vector;
  if (not properties_impl_ptr_->getTreeVector(property_name, tree_vector)) {

    return false;

  }

  for (auto impl_tree : tree_vector) {

    property_tree_vector.emplace_back(SubPropertyTree(impl_tree.first, PropertyTree(impl_tree.second)));

  }

  return true;

}

bool kel::PropertyTree::getPropertySubTreeVector(std::vector<SubPropertyTree>& property_tree_vector) const {

  property_tree_vector.clear();

  std::vector<ImplSubTree> tree_vector;
  if (not properties_impl_ptr_->getTreeVector(tree_vector)) {

    return false;

  }

  for (auto impl_tree : tree_vector) {

    property_tree_vector.emplace_back(SubPropertyTree(impl_tree.first, PropertyTree(impl_tree.second)));

  }

  return true;

}

std::string kel::PropertyTree::getValue() const { return properties_impl_ptr_->getData<std::string>(); }


text:
//==================================================================================================
/*
  Copyright 2017 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
//! [toint]
#include <boost/simd/arithmetic.hpp>
#include <boost/simd/pack.hpp>
#include <boost/simd/constant/inf.hpp>
#include <iostream>

namespace bs =  boost::simd;
using pack_ft = bs::pack <float, 4>;
using pack_it = bs::pack <std::uint16_t, 4>;

int main()
{
  pack_ft pf = {-1.0f, 2.6f, -3.4f, bs::Inf<float>()};
  pack_it pi = { 1, 2, 3, bs::Inf<std::uint16_t>()  };

  std::cout
    << "---- simd" << '\n'
    << "<- pf =                        " << pf << '\n'
    << "-> bs::toint(pf) =             " << bs::toint(pf) << '\n'
    << "-> saturated_(bs::toint)(pf) = " << bs::saturated_(bs::toint)(pf) << '\n'
    << "<- pi =                        " << pi << '\n'
    << "-> bs::toint(pi) =             " << bs::toint(pi) << '\n'
    << "-> saturated_(bs::toint)(pi) = " << bs::saturated_(bs::toint)(pi) << '\n';

  float xf = -2.5f;
  std::uint16_t xi = 65535;
  std::cout
    << "---- scalar"  << '\n'
    << "<- xf =                        " << xf << '\n'
    << "-> bs::toint(xf) =             " << bs::toint(xf) << '\n'
    << "-> saturated_(bs::toint)(xf) = " << bs::saturated_(bs::toint)(xf) << '\n'
    << "<- xi =                        " << xi << '\n'
    << "-> bs::toint(xi) =             " << bs::toint(xi) << '\n'
    << "-> saturated_(bs::toint)(xi) = " << bs::saturated_(bs::toint)(xi) << '\n';
     return 0;
}
//! [toint]


text:
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

#if defined(_WIN32)
#include <string>
#include <vector>
#include <cctype>
#include <iostream>
#include <stdlib.h>
#include <boost/program_options/parsers.hpp>

using namespace std;
using namespace boost::program_options;

void check_equal(const std::vector<string>& actual, const char **expected, int n)
{
    if (actual.size() != n)
    {
        std::cerr << "Size mismatch between expected and actual data\n";
        abort();
    }
    for (int i = 0; i < n; ++i)
    {
        if (actual[i] != expected[i])
        {
            std::cerr << "Unexpected content\n";
            abort();
        }
    }
}

#include <boost/preprocessor/cat.hpp>

void test_winmain()
{

#define C ,
#define TEST(input, expected) \
    const char* BOOST_PP_CAT(e, __LINE__)[] = expected;\
    vector<string> BOOST_PP_CAT(v, __LINE__) = split_winmain(input);\
    check_equal(BOOST_PP_CAT(v, __LINE__), BOOST_PP_CAT(e, __LINE__),\
                sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));

// The following expectations were obtained in Win2000 shell:
    TEST("1 ", {"1"});
    TEST("1\"2\" ", {"12"});
    TEST("1\"2  ", {"12  "});
    TEST("1\"\\\"2\" ", {"1\"2"});
    TEST("\"1\" \"2\" ", {"1" C "2"});
    TEST("1\\\" ", {"1\""});
    TEST("1\\\\\" ", {"1\\ "});
    TEST("1\\\\\\\" ", {"1\\\""});
    TEST("1\\\\\\\\\" ", {"1\\\\ "});

    TEST("1\" 1 ", {"1 1 "});
    TEST("1\\\" 1 ", {"1\"" C "1"});
    TEST("1\\1 ", {"1\\1"});
    TEST("1\\\\1 ", {"1\\\\1"});
}

int main(int, char*[])
{
    test_winmain();
    return 0;
}
#else
int main(int, char*[])
{
    return 0;
}
#endif


text:
#include <cassert>

#include "sdch_fastdict_factory.h"

#include <boost/make_shared.hpp>

namespace sdch {

FastdictFactory::Value::~Value() {}

FastdictFactory::FastdictFactory() : max_size_(10000000) {}

Dictionary* FastdictFactory::create_dictionary(const char* buf, size_t len) {
  ValuePtr v = boost::make_shared<Value>(time(NULL));
  if (!v->dict.init(buf, buf, buf + len)) {
    return NULL;
  }

  if (!store(v->dict.client_id(), v)) {
    return NULL;
  }

  return &v->dict;
}

bool FastdictFactory::store(Dictionary::id_t key, ValuePtr value) {
  std::pair<StoreType::iterator, bool> r =
      values_.insert(std::make_pair(key, value));
  if (!r.second) {
#if 0
        if (r.first->second.ts < ts)
            r.first->second.ts = ts;
#endif
    return false;
  }

  lru_.insert(LRUType::value_type(r.first->second->ts, key));
  total_size_ += r.first->second->dict.size();

  // Remove oldest entries if we exceeded max_size_
  for (LRUType::iterator i = lru_.begin();
       total_size_ > max_size_ && i != lru_.end();) {
    StoreType::iterator si = values_.find(i->second);
    if (si == values_.end()) {
      assert(0);
      continue;
    }

    if (!si->second.unique()) {  // XXX
      ++i;
      continue;
    }

    total_size_ -= si->second->dict.size();
    values_.erase(si);
    lru_.erase(i++);
  }

  return true;
}

FastdictFactory::ValuePtr FastdictFactory::find(const Dictionary::id_t& key) {
  StoreType::iterator i = values_.find(key);
  if (i == values_.end())
    return ValuePtr();

  // TODO Update LRU?
  return i->second;
}

}  // namespace sdch

ext: cc


text:
#include "DogConfig.h"

#include <stdio.h>
#include <fstream>
#include <string>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>

// namespace Diagnosis-of-GPS
namespace dog
{

//-----------------------------------------------------------------------------
CDogConfig::CDogConfig() : m_nSizeOfMMF(64*1024), m_MmfStamp(0)
{
	InitConfigStringMapTable();
	Reset();

	const LPCTSTR szMmfName = L"DogConfig-{D9E5ADCD-1914-4e2e-97A5-BC076B2FC5CF}";
	if( !m_MMF.Open(szMmfName))
	{
		m_MMF.Create(szMmfName, static_cast<DWORD>(m_nSizeOfMMF));
		TMmfHeader mmfHeader;
		memcpy(m_MMF.GetData(), &mmfHeader, sizeof(mmfHeader));
	}
}

CDogConfig::~CDogConfig()
{
	m_MMF.Close();
}

void CDogConfig::Reset()
{
	m_PropertyTrees.clear();
	m_PropertyTrees.resize(NUM_OF_DOG_FUNC);

	m_ErrorMessage.clear();
	m_ReadLineErrMsg.clear();
}

//-----------------------------------------------------------------------------
bool CDogConfig::ReadFromFile(const char* filename)
{
	std::ifstream fin(filename, std::ios_base::in);
	if(fin.fail())
	{
		return false;
	}

	bool bResult = true;
	int nLine = 0;
	std::string strline;
	while(!fin.fail() && !fin.eof())
	{
		char linebuf[256];
		fin.getline(linebuf, sizeof(linebuf));
		if(fin.rdstate() & std::ifstream::eofbit)
		{
			break;
		}

		strline = linebuf;

		while ((fin.rdstate() & std::ifstream::failbit) && !(fin.rdstate() & std::ifstream::eofbit))
		{
			fin.getline(linebuf, sizeof(linebuf));
			if(fin.rdstate() & std::ifstream::eofbit)
			{
				break;
			}

			strline += std::string(linebuf);
		}

		try
		{
			++nLine;
			bool ret = ReadLine(strline);
			if(!ret)
			{
				bResult = false;
				m_ErrorMessage.push_back( (boost::format("Error: line %d: \"%s\". ") %nLine %strline).str() );
				m_ErrorMessage.push_back(m_ReadLineErrMsg);
			}
		}
		catch (...) {
			// syntax error
			bResult = false;
		}
	}

	fin.close();

	UpdateToMMF();
	return bResult;
}

bool CDogConfig::ReadFromString(const char* szConfigString)
{
	bool bResult = true;
	int nLine = 0;
	split_vector_type lines;
	boost::split( lines, szConfigString, boost::is_any_of("\n"));
	for(size_t i=0; i<lines.size(); ++i)
	{
		try
		{
			++nLine;
			bool ret = ReadLine(lines[i]);
			if(!ret)
			{
				bResult = false;
				m_ErrorMessage.push_back( (boost::format("Error: line %d: \"%s\". ") %nLine %lines[i]).str() );
				m_ErrorMessage.push_back(m_ReadLineErrMsg);
			}
		}
		catch (...) {
			// syntax error
			bResult = false;
		}
	}

	UpdateToMMF();
	return bResult;
}

//-----------------------------------------------------------------------------
namespace dog_internal
{
	class string_map_id_error : public std::exception
	{
	public:
		string_map_id_error(std::string _what) : std::exception(_what.c_str()) {}
	};
} // namespace dog_internal

static inline unsigned int StringMapToId(std::string str, const std::map<std::string, unsigned int>& mapTable)
{
	std::map<std::string, unsigned int>::const_iterator iter = mapTable.find(str);
	if(iter == mapTable.end())
	{
		using boost::lexical_cast;
		using boost::bad_lexical_cast;

		try {
			return lexical_cast<unsigned int>(str);
		} catch(bad_lexical_cast &) {
			throw dog_internal::string_map_id_error(std::string("'") + str + "' doesn't be defined");
		}
	}
	else
	{
		return iter->second;
	}

	return (std::numeric_limits<unsigned int>::max)();
}

static inline unsigned int StringMapToId(std::string str)
{
	using boost::lexical_cast;
	using boost::bad_lexical_cast;

	try {
		return lexical_cast<unsigned int>(str);
	} catch(bad_lexical_cast &) {
		throw dog_internal::string_map_id_error(std::string("'") + str + "' doesn't be defined");
	}

	return (std::numeric_limits<unsigned int>::max)();
}

//-----------------------------------------------------------------------------
bool CDogConfig::ReadLine(const std::string& str)
{
	m_ReadLineErrMsg.clear();
	std::string line = boost::trim_copy(str);
	if(line.empty()) {
		return true;
	}

	split_vector_type PropertyAndValue;
	boost::split( PropertyAndValue, line, boost::is_any_of("="));
	if(PropertyAndValue.size() != 2)
	{
		// syntax error : no '=' is found
		return false;
	}

	split_vector_type PropertyNode;
	boost::split( PropertyNode, PropertyAndValue[0], boost::is_any_of("."));
	if(PropertyNode.empty() || PropertyNode.size() > 4) {
		return false; // syntax error
	}

	for(size_t i=0; i<PropertyNode.size(); ++i)
	{
		boost::trim(PropertyNode[i]);
		boost::to_lower(PropertyNode[i]);
	}

	split_vector_type Values;
	boost::split( Values, PropertyAndValue[1], boost::is_any_of(","));
	if(Values.empty()) {
		return false; // syntax error: no value specified
	}

	for(size_t i=0; i<Values.size(); ++i)
	{
		boost::trim(Values[i]);
		boost::to_lower(Values[i]);
	}

	try
	{
		if( std::string("output") == PropertyNode[PropertyNode.size()-1]) {
			UpdateOutputProperty(PropertyNode, Values);
		} else {
			UpdateEnableProperty(PropertyNode, Values);
		}
	}
	catch(const dog_internal::string_map_id_error& e)
	{
		m_ReadLineErrMsg = e.what();
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
void CDogConfig::UpdateOutputProperty(const CDogConfig::split_vector_type& PropertyNode, const CDogConfig::split_vector_type& Values)
{
	std::vector<unsigned int> outputList;
	for(size_t i=0; i<Values.size(); ++i)
	{
		const unsigned int outputId = StringMapToId(Values[i], m_OutputNameMap);
		outputList.push_back(outputId);
	}

	// remove duplicated elements
	std::vector<unsigned int>::iterator new_last;
	std::sort(outputList.begin(), outputList.end());
	new_last = std::unique(outputList.begin(), outputList.end());
	outputList.erase(new_last, outputList.end());

	if(2 == PropertyNode.size()) // function.output = ...
	{
		const unsigned int functionId = StringMapToId(PropertyNode[0], m_DogFunctionNameMap);

		TProperty property = GetProperty(functionId);
		property.OutputList = outputList;
		SetProperty(functionId, property);
	}
	else if (3 == PropertyNode.size()) // function.category.output = ...
	{
		const unsigned int functionId = StringMapToId(PropertyNode[0], m_DogFunctionNameMap);
		const unsigned int categoryId = StringMapToId(PropertyNode[1], m_CategoryNameMap);

		TProperty property = GetProperty(functionId, categoryId);
		property.OutputList = outputList;
		SetProperty(functionId, categoryId, property);
	}
	else if (4 == PropertyNode.size()) // function.category.subcategory.output = ...
	{
		const unsigned int functionId = StringMapToId(PropertyNode[0], m_DogFunctionNameMap);
		const unsigned int categoryId = StringMapToId(PropertyNode[1], m_CategoryNameMap);
		unsigned int subcategoryId = 0;

		std::map<unsigned int, TStringIdMap >::iterator subiter;
		subiter = m_SubCategoryNameMap.find(categoryId);
		if(subiter != m_SubCategoryNameMap.end()) {
			subcategoryId = StringMapToId(PropertyNode[2], subiter->second);
		} else {
			subcategoryId = StringMapToId(PropertyNode[2]);
		}

		TProperty property = GetProperty(functionId, categoryId, subcategoryId);
		property.OutputList = outputList;
		SetProperty(functionId, categoryId, subcategoryId, property);
	}
}

void CDogConfig::UpdateEnableProperty(const CDogConfig::split_vector_type& PropertyNode, const CDogConfig::split_vector_type& Values)
{
	if(1 == PropertyNode.size()) // function = ...
	{
		const unsigned int functionId = StringMapToId(PropertyNode[0], m_DogFunctionNameMap);

		for(size_t i=0; i<Values.size(); ++i)
		{
			if(std::string("all") == Values[i])
			{
				TProperty property = GetProperty(functionId);
				property.bEnable = true;
				SetProperty(functionId, property);
			}
			else if (std::string("none") == Values[i])
			{
				TProperty property = GetProperty(functionId);
				property.bEnable = false;
				SetProperty(functionId, property);
			}
			else
			{
				std::string strCategory;
				bool bEnableSetting = true;
				if('+' == Values[i][0])
				{
					bEnableSetting =  true;
					strCategory = Values[i].substr(1);
				}
				else if('-' == Values[i][0])
				{
					bEnableSetting =  false;
					strCategory = Values[i].substr(1);
				}
				else
				{
					bEnableSetting =  true;
					strCategory = Values[i];
				}

				const unsigned int categoryId = StringMapToId(strCategory, m_CategoryNameMap);
				TProperty property = GetProperty(functionId, categoryId);
				property.bEnable = bEnableSetting;
				SetProperty(functionId, categoryId, property);
			}
		}
	}
	else if (2 == PropertyNode.size()) // function.category = ...
	{
		const unsigned int functionId = StringMapToId(PropertyNode[0], m_DogFunctionNameMap);
		const unsigned int categoryId = StringMapToId(PropertyNode[1], m_CategoryNameMap);

		for(size_t i=0; i<Values.size(); ++i)
		{
			if(std::string("all") == Values[i])
			{
				TProperty property = GetProperty(functionId, categoryId);
				property.bEnable = true;
				SetProperty(functionId, categoryId, property);
			}
			else if (std::string("none") == Values[i])
			{
				TProperty property = GetProperty(functionId, categoryId);
				property.bEnable = false;
				SetProperty(functionId, categoryId, property);
			}
			else
			{
				std::string strSubCategory;
				bool bEnableSetting = true;
				if('+' == Values[i][0])
				{
					bEnableSetting =  true;
					strSubCategory = Values[i].substr(1);
				}
				else if('-' == Values[i][0])
				{
					bEnableSetting =  false;
					strSubCategory = Values[i].substr(1);
				}
				else
				{
					bEnableSetting =  true;
					strSubCategory = Values[i];
				}

				unsigned int subcategoryId = 0;
				std::map<unsigned int, TStringIdMap >::iterator subiter;
				subiter = m_SubCategoryNameMap.find(categoryId);
				if(subiter != m_SubCategoryNameMap.end()) {
					subcategoryId = StringMapToId(strSubCategory, subiter->second);
				} else {
					subcategoryId = StringMapToId(strSubCategory);
				}

				TProperty property = GetProperty(functionId, categoryId, subcategoryId);
				property.bEnable = bEnableSetting;
				SetProperty(functionId, categoryId, subcategoryId, property);
			}
		}
	}
}

//-----------------------------------------------------------------------------
const CDogConfig::TProperty& CDogConfig::GetProperty(unsigned int function)
{
	if(function > m_PropertyTrees.size()) {
		return m_EmptyProperty;
	}

	if(GetMmfUpdateStamp() != m_MmfStamp) {
		UpdateFromMMF();
	}

	return m_PropertyTrees[function].m_Property;
}

const CDogConfig::TProperty& CDogConfig::GetProperty(unsigned int function, unsigned int category)
{
	if(function > m_PropertyTrees.size()) {
		return m_EmptyProperty;
	}

	if(GetMmfUpdateStamp() != m_MmfStamp) {
		UpdateFromMMF();
	}

	const std::map<unsigned int, TCategoryNode> & catNodes = m_PropertyTrees[function].m_CatNodes;
	const std::map<unsigned int, TCategoryNode>::const_iterator catIter = catNodes.find(category);
	if(catIter == catNodes.end()) {
		return m_PropertyTrees[function].m_Property;
	}

	return catIter->second.m_Property;
}

const CDogConfig::TProperty& CDogConfig::GetProperty(unsigned int function, unsigned int category, unsigned int subcategory)
{
	if(function > m_PropertyTrees.size()) {
		return m_EmptyProperty;
	}

	if(GetMmfUpdateStamp() != m_MmfStamp) {
		UpdateFromMMF();
	}

	const std::map<unsigned int, TCategoryNode> & catNodes = m_PropertyTrees[function].m_CatNodes;
 	const std::map<unsigned int, TCategoryNode>::const_iterator catIter = catNodes.find(category);
 	if(catIter == catNodes.end()) {
		return m_PropertyTrees[function].m_Property;
	}

	const std::map<unsigned int, TProperty> & subNodes = catIter->second.m_SubcatNodes;
	const std::map<unsigned int, TProperty>::const_iterator subIter = subNodes.find(subcategory);
	if(subIter == subNodes.end()) {
		return catIter->second.m_Property;
	}

	return subIter->second;
}

//-----------------------------------------------------------------------------
void CDogConfig::SetProperty(unsigned int function, const CDogConfig::TProperty& property)
{
	if(function > m_PropertyTrees.size()) {
		return;
	}
	m_PropertyTrees[function].m_Property = property;

	if(!property.OutputList.empty())
	{
		// update OutputList
		std::map<unsigned int, TCategoryNode> & catNodes = m_PropertyTrees[function].m_CatNodes;
		std::map<unsigned int, TCategoryNode>::iterator iter;
		for(iter = catNodes.begin(); iter!=catNodes.end(); ++iter )
		{
			const unsigned int category = iter->first;
			const TProperty& catProp = GetProperty(function, category);
			if( catProp.OutputList.empty())
			{
				TProperty catPropSetting = catProp;
				catPropSetting.OutputList = property.OutputList;
				SetProperty(function, category, catPropSetting);
			}
		}
	}
}

void CDogConfig::SetProperty(unsigned int function, unsigned int category, const CDogConfig::TProperty& property)
{
	if(function > m_PropertyTrees.size()) {
		return;
	}
	m_PropertyTrees[function].m_CatNodes[category].m_Property = property;

	if(!property.OutputList.empty())
	{
		// update OutputList
		std::map<unsigned int, TProperty> & subNodes = m_PropertyTrees[function].m_CatNodes[category].m_SubcatNodes;
		std::map<unsigned int, TProperty>::const_iterator iter;
		for(iter = subNodes.begin(); iter!=subNodes.end(); ++iter )
		{
			const unsigned int subcategory = iter->first;
			const TProperty& subcatProp = GetProperty(function, category, subcategory);
			if( subcatProp.OutputList.empty())
			{
				TProperty subcatPropSetting = subcatProp;
				subcatPropSetting.OutputList = property.OutputList;
				SetProperty(function, category, subcategory, subcatPropSetting);
			}
		}
	}
}

void CDogConfig::SetProperty(unsigned int function, unsigned int category, unsigned int subcategory, const CDogConfig::TProperty& property)
{
	if(function > m_PropertyTrees.size()) {
		return;
	}

	// in case of set subcategory directly when category is not specified
	const std::map<unsigned int, TCategoryNode> & catNodes = m_PropertyTrees[function].m_CatNodes;
	const std::map<unsigned int, TCategoryNode>::const_iterator catIter = catNodes.find(category);
	if(catIter == catNodes.end()) {
		m_PropertyTrees[function].m_CatNodes[category].m_Property = m_PropertyTrees[function].m_Property;
	}

	m_PropertyTrees[function].m_CatNodes[category].m_SubcatNodes[subcategory] = property;
}

//-----------------------------------------------------------------------------
void CDogConfig::SetTimerType(unsigned int timerType)
{
	m_TimerSetting = timerType;

	UpdateMMFHeader();
}

unsigned int CDogConfig::GetTimerType()
{
	if(GetMmfUpdateStamp() != m_MmfStamp) {
		UpdateFromMMF();
	}

	return m_TimerSetting;
}

//-----------------------------------------------------------------------------
size_t CDogConfig::CopyPropertyToMem(BYTE* pMem, size_t sizeOfMem, unsigned int depth, unsigned int function, unsigned int category, unsigned int subcategory, const TProperty& property)
{
	TMmfPropertyEntry entry;
	entry.depth = depth;
	entry.function = function;
	entry.category = category;
	entry.subcategory = subcategory;
	entry.bEnableFlag = property.bEnable;
	entry.numOfOutput = property.OutputList.size();
	const size_t bytesOfOutputList = entry.numOfOutput*sizeof(unsigned int);
	const size_t bytesOfRecord = sizeof(entry)+bytesOfOutputList;

	if(sizeOfMem < bytesOfRecord) {
		return 0;
	}

	memcpy(pMem, &entry, sizeof(entry));
	if(!property.OutputList.empty()) {
		memcpy(pMem+sizeof(entry), &property.OutputList[0], bytesOfOutputList);
	}

	return bytesOfRecord;
}

void CDogConfig::UpdateMMFHeader()
{
	TMmfHeader mmfHeader;
	mmfHeader = *((TMmfHeader*)m_MMF.GetData());
	mmfHeader.updateStamp = ++mmfHeader.updateStamp;

	mmfHeader.timerSetting = m_TimerSetting;

	memcpy(m_MMF.GetData(), &mmfHeader, sizeof(mmfHeader));
	m_MmfStamp = mmfHeader.updateStamp;
}

void CDogConfig::UpdateToMMF()
{
	TMmfHeader mmfHeader;
	mmfHeader = *((TMmfHeader*)m_MMF.GetData());

	BYTE* pMem = (BYTE*)m_MMF.GetData();
	size_t nSizeOfMem = m_nSizeOfMMF;
	pMem += sizeof(TMmfHeader);
	nSizeOfMem -= sizeof(TMmfHeader);

	size_t numOfEntries = 0;

	for(unsigned int function=EVENT_LOG; function<NUM_OF_DOG_FUNC; ++function)
	{
		size_t bytesOfWrite = CopyPropertyToMem(pMem, nSizeOfMem, 1, function, 0,0, m_PropertyTrees[function].m_Property);
		if(bytesOfWrite)
		{
			++numOfEntries;
			pMem += bytesOfWrite;
		}

		const std::map<unsigned int, TCategoryNode> & catNodes = m_PropertyTrees[function].m_CatNodes;
		std::map<unsigned int, TCategoryNode>::const_iterator catIter;
		for(catIter = catNodes.begin(); catIter!=catNodes.end(); ++catIter )
		{
			const unsigned int category = catIter->first;
			size_t bytesOfWrite = CopyPropertyToMem(pMem, nSizeOfMem, 2, function, category, 0, GetProperty(function, category));
			if(bytesOfWrite)
			{
				++numOfEntries;
				pMem += bytesOfWrite;
				nSizeOfMem -= bytesOfWrite;
			}

			const std::map<unsigned int, TProperty> & subNodes = catIter->second.m_SubcatNodes;
			std::map<unsigned int, TProperty>::const_iterator subIter;
			for(subIter = subNodes.begin(); subIter!=subNodes.end(); ++subIter )
			{
				const unsigned int subcategory = subIter->first;
				size_t bytesOfWrite = CopyPropertyToMem(pMem, nSizeOfMem, 3, function, category, subcategory, GetProperty(function, category, subcategory));
				if(bytesOfWrite)
				{
					++numOfEntries;
					pMem += bytesOfWrite;
					nSizeOfMem -= bytesOfWrite;
				}
			}
		}
	}

	mmfHeader.updateStamp = ++mmfHeader.updateStamp;
	mmfHeader.timerSetting = m_TimerSetting;
	mmfHeader.numOfEntries = numOfEntries;
	mmfHeader.size = pMem - (BYTE*)m_MMF.GetData() - sizeof(mmfHeader);
	memcpy(m_MMF.GetData(), &mmfHeader, sizeof(mmfHeader));

	m_MmfStamp = mmfHeader.updateStamp;
}

void CDogConfig::UpdateFromMMF()
{
	//reset
	m_PropertyTrees.clear();
	m_PropertyTrees.resize(NUM_OF_DOG_FUNC);

	TMmfHeader mmfHeader;
	mmfHeader = *((TMmfHeader*)m_MMF.GetData());

	m_TimerSetting = mmfHeader.timerSetting;

	BYTE* pMem = (BYTE*)m_MMF.GetData();
	size_t nSizeOfMem = m_nSizeOfMMF;
	pMem += sizeof(TMmfHeader);

	for(size_t i=0; i<mmfHeader.numOfEntries; ++i)
	{
		TMmfPropertyEntry entry = *(TMmfPropertyEntry*)pMem;
		const size_t bytesOfOutputList = entry.numOfOutput*sizeof(unsigned int);

		TProperty property;
		property.bEnable = entry.bEnableFlag;
		if(entry.numOfOutput) {
			property.OutputList.insert(property.OutputList.begin(), pMem+sizeof(entry), pMem+bytesOfOutputList);
		}

		switch(entry.depth)
		{
		case 1:
			SetProperty(entry.function, property);
			break;
		case 2:
			SetProperty(entry.function, entry.category, property);
			break;
		case 3:
			SetProperty(entry.function, entry.category, entry.subcategory, property);
			break;
		default:
			break;
		}

		const size_t bytesOfRecord = sizeof(entry)+bytesOfOutputList;
		pMem += bytesOfRecord;
	}

	m_MmfStamp = mmfHeader.updateStamp;
}

} // namespace dog


text:
/*
 * Copyright (C) 2009 National Association of REALTORS(R)
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, and/or sell copies of the
 * Software, and to permit persons to whom the Software is furnished
 * to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that
 * both the above copyright notice(s) and this permission notice
 * appear in supporting documentation.
 */

#include <sstream>
#include <iostream> // $$DEBUG

#include "librets/forward.h"
#include "librets/str_stream.h"
#include "librets/RetsException.h"
#include "librets/UpdateRequest.h"
#include <boost/lexical_cast.hpp>

using namespace librets;

using std::string;
using std::vector;

using boost::lexical_cast;

const char * UpdateRequest::CLASS_PARAMETER = "ClassName";
const char * UpdateRequest::DELIMITER_PARAMETER = "Delimiter";
const char * UpdateRequest::RECORD_PARAMETER = "Record";
const char * UpdateRequest::RESOURCE_PARAMETER = "Resource";
const char * UpdateRequest::UPDATE_TYPE_PARAMETER = "Type";
const char * UpdateRequest::VALIDATE_PARAMETER = "Validate";
const char * UpdateRequest::WARNING_RESPONSE_PARAMETER = "WarningResponse";

UpdateRequest::UpdateRequest(string resourceName, string className)
    : mDelimiter("\t"), mFlag(UpdateRequest::UPDATE_OK), mUpdateType("")
{
    SetQueryParameter(CLASS_PARAMETER, className);
    SetQueryParameter(RESOURCE_PARAMETER, resourceName);

    SetDelimiter(mDelimiter);
    SetMethod(POST);
    SetUpdateType(mUpdateType);
    SetValidateFlag(mFlag);
}

const string UpdateRequest::GetDelimiter()
{
    return mDelimiter;
}

string UpdateRequest::ConstructRecord()
{
    string delimiter;
    str_stream record;

    /*
     * Format the "Record" portion of the request.
     */
    delimiter = "";
    for (StringMap::iterator i = mFields.begin(); i != mFields.end(); i++)
    {
        record << delimiter << i->first << "=" << i->second;
        delimiter = mDelimiter;
    }
    return record;
}

void UpdateRequest::ConstructRequest()
{
    SetQueryParameter(RECORD_PARAMETER, ConstructRecord());

    SetQueryParameter(WARNING_RESPONSE_PARAMETER, ConstructWarningResponse());
}

string UpdateRequest::ConstructWarningResponse()
{
    string delimiter;
    str_stream warnings;

    /*
     * Format the "WarningResponse portion of the request.
     */
    delimiter = "";
    for (StringMap::iterator i = mWarnings.begin(); i != mWarnings.end(); i++)
    {
        warnings << delimiter << i->first << "=" << i->second;
        delimiter = mDelimiter;
    }
    return warnings;
}

void UpdateRequest::SetDelimiter(string delimiter)
{
    if (delimiter.length() != 1)
        throw RetsException("Invalid delimiter in Update Transaction. Must be 0x00-0xff.");

    mDelimiter = delimiter;

    short delimiterChar = (short) delimiter.at(0);

    str_stream delimiterHex;

    delimiterHex << "0123456789abcdef"[(delimiterChar >> 4) & 0xf];
    delimiterHex << "0123456789abcdef"[delimiterChar & 0xf];

    SetQueryParameter(DELIMITER_PARAMETER, delimiterHex);
}

const StringVector UpdateRequest::GetAllFields()
{
    StringVector fields;

    for (StringMap::iterator i = mFields.begin(); i != mFields.end(); i++)
    {
        fields.push_back(i->first);
    }

    return fields;
}

const string UpdateRequest::GetField(string key)
{
    return mFields[key];
}

void UpdateRequest::SetField(string key, string value)
{
    mFields[key] = value;
}

const string UpdateRequest::GetUpdateType()
{
    return mUpdateType;
}

void UpdateRequest::SetUpdateType(string updateType)
{
    mUpdateType = updateType;
    SetQueryParameter(UPDATE_TYPE_PARAMETER, mUpdateType);
}

const int UpdateRequest::GetValidateFlag()
{
    return mFlag;
}

void UpdateRequest::SetValidateFlag(int flag)
{
    mFlag = flag;
    SetQueryParameter(VALIDATE_PARAMETER, mFlag);
}

const StringVector UpdateRequest::GetAllWarnings()
{
    StringVector warnings;

    for (StringMap::iterator i = mWarnings.begin(); i != mWarnings.end(); i++)
    {
        warnings.push_back(i->first);
    }

    return warnings;
}

const string UpdateRequest::GetWarningResponse(int key)
{
    return GetWarningResponse(lexical_cast<string>(key));
}

const string UpdateRequest::GetWarningResponse(string key)
{
    return mWarnings[key];
}

void UpdateRequest::SetWarningResponse(int key, string value)
{
    SetWarningResponse(lexical_cast<string>(key), value);
}

void UpdateRequest::SetWarningResponse(string key, string value)
{
    mWarnings[key] = value;
}

max_stars_repo_name: jbramleycl/libRETS

max_issues_repo_name: jbramleycl/libRETS

max_forks_repo_name: jbramleycl/libRETS


text:
#include <cmath>
#include <ctime>
#include <functional>
#include <iostream>
#include <random>
#include <vector>

#include <Eigen/Dense>

#include "house.hpp"
#include "ukf.hpp"
#include "dyn.hpp"
#include "filter_aux.hpp"
#include "pearsonator.hpp"
#include "eigen_csv.hpp"
#include "timer.hpp"

#define G_0 9.80665

#define DEG M_PI / 180
#define ARC_MIN M_PI / (180 * 60)
#define ARC_SEC M_PI / (180 * 60 * 60)

#define BB 1E-3

#define THRESHOLD 1E-20

using namespace Eigen;
using namespace std;

string filter_file(bool gauss, const string& filter, int trial) {
    string filename = "out/";
    filename += filter;
    filename += "_est_";
    filename += (gauss ? "gauss_" : "pearson_");
    filename += to_string(trial);
    filename += ".csv";
    return filename;
}

void run(bool gauss) {

    DynamicModel::stf g = [] (double t, const VectorXd& X, const VectorXd& fd)
        -> VectorXd {
            double d = -BB * X.tail(3).norm();
            VectorXd Xd(6);
            Xd.head(3) = X.tail(3);
            Xd.tail(3) = d * X.tail(3) + fd;
            Xd(5) -= G_0;
            return Xd;
        };

    DynamicModel f(g, 6, 1E-9, 1E-9);

    UKF::meas_model h = [] (double t, const VectorXd& X)
        -> VectorXd {
            double r = X.head(2).norm();
            Vector2d z;
            if (r > THRESHOLD) {
                z(0) = atan2(X(1), -X(0));
                z(1) = atan2(X(2), r);
            } else {
                z(0) = 0;
                z(1) = M_PI / 2;
            }
            return z;
        };

    HOUSE::meas_model hh = [h] (double t, const VectorXd& X, const VectorXd& n)
        -> VectorXd {
            return h(t, X) + n;
        };

    double stdw, stdn;
    stdw = 0.01;
    stdn = ARC_MIN;

    Matrix3d Pww = Matrix3d::Identity() * stdw * stdw;
    Matrix2d Pnn = Matrix2d::Identity() * stdn * stdn;

    double skeww, skewn, kurtw, kurtn, stdx0, stdv0, skew0, kurt0;

    if (gauss) {

        skeww = 0;
        kurtw = 3;

        skewn = 0;
        kurtn = 3;

        skew0 = 0;
        kurt0 = 3;

     } else {

        skeww = 1;
        kurtw = 30;

        skewn = -1;
        kurtn =  30;

        skew0 = 1;
        kurt0 = 30;

    }

    stdx0 = 250;
    stdv0 = 100;

    VectorXd X0m(6), X0std(6), X0skew(6), X0kurt(6);

    X0m  << 1000, 1000, 0, 500, 0, 500;

    X0std.head(3).setConstant(stdx0);
    X0std.tail(3).setConstant(stdv0);
    X0skew.setConstant(skew0);
    X0kurt.setConstant(kurt0);

    MatrixXd Pxx0 = X0std.array().square().matrix().asDiagonal();

    Pearsonator::TypeIV genw_p (0, stdw,  skeww, kurtw),
                        genn_p (0, stdn,  skewn, kurtn),
                        genx0_p(0, stdx0, skew0, kurt0),
                        genv0_p(0, stdv0, skew0, kurt0);

    normal_distribution<double> genw_g (0, stdw),
                                genn_g (0, stdn),
                                genx0_g(0, stdx0),
                                genv0_g(0, stdv0);

    mt19937_64 mt(0);

    typedef function<double(void)> noisemaker;

    noisemaker genw  = [&] () -> double {return gauss ? genw_g (mt) : genw_p (mt);};
    noisemaker genn  = [&] () -> double {return gauss ? genn_g (mt) : genn_p (mt);};
    noisemaker genx0 = [&] () -> double {return gauss ? genx0_g(mt) : genx0_p(mt);};
    noisemaker genv0 = [&] () -> double {return gauss ? genv0_g(mt) : genv0_p(mt);};

    UKF::cut_dir = "../CUT/";

    UKF ukf (f, h, false, 0, X0m, Pxx0, Pww, Pnn, UKF::sig_type::JU,   1);
    UKF cut4(f, h, false, 0, X0m, Pxx0, Pww, Pnn, UKF::sig_type::CUT4, 1);
    UKF cut6(f, h, false, 0, X0m, Pxx0, Pww, Pnn, UKF::sig_type::CUT6, 1);

    HOUSE::Dist distx0(Pxx0), distw(Pww), distn(Pnn);

    distx0.mean = X0m;
    distx0.skew = X0skew;
    distx0.kurt = X0kurt;

    distw.skew.setConstant(skeww);
    distw.kurt.setConstant(kurtw);

    distn.skew.setConstant(skewn);
    distn.kurt.setConstant(kurtn);

    HOUSE house(f, hh, 2, 0, distx0, distw, distn, 0);

    double dt = 0.1;
    double tmin = 1;

    int trials = 100;

    vector<string> header(7);
    header[0] = "t";
    header[1] = "x";
    header[2] = "y";
    header[3] = "z";
    header[4] = "vx";
    header[5] = "vy";
    header[6] = "vz";

    MatrixXd run_times(trials, 4);
    Timer timer;

    for (int j = 1; j <= trials; j++) {

        cout << "Running trial " << j << endl;

        house.reset(0, distx0);
        ukf.reset(0, X0m, Pxx0);
        cut4.reset(0, X0m, Pxx0);
        cut6.reset(0, X0m, Pxx0);

        vector<VectorXd> Xtru;
        double time;
        do {
            cout << "       Trying" << endl;
            time = 0;
            VectorXd X(6);
            X = X0m;
            for (int i = 0; i < 3; i++) {
                X(i)   += genx0();
                X(i+3) += genv0();
            }
            Xtru.clear();
            Xtru.push_back(X);
            do {
                Vector3d fd;
                fd << genw(), genw(), genw();
                X = f(time, time+dt, X, fd);
                time += dt;
                Xtru.push_back(X);
            } while (X(2) > 0);
         } while (time < tmin);

        int steps = Xtru.size();

        VectorXd t;
        t.setLinSpaced(steps, 0, (steps-1)*dt);

        MatrixXd table(steps, 7);
        table.col(0) = t;
        for (int k = 0; k < steps; k++)
            table.row(k).tail(6) = Xtru[k];

        string xtrufile = "out/tru_";
        xtrufile += (gauss ? "gauss_" : "pearson_");
        xtrufile += to_string(j);
        xtrufile += ".csv";

        EigenCSV::write(table, header, xtrufile);

        MatrixXd Ztru(2, steps);
        for (int k = 0; k < steps; k++) {
            Ztru.col(k) = h(t(k), Xtru[k]);
            Ztru(0,k) += genn();
            Ztru(1,k) += genn();
        }

        cout << "   HOUSE" << endl;
        timer.tick();
        house.run(t, Ztru);
        run_times(j-1, 0) = timer.tock();
        house.save(filter_file(gauss, "house", j));

        cout << "   UKF" << endl;
        timer.tick();
        ukf.run(t, Ztru);
        run_times(j-1, 1) = timer.tock();
        ukf.save(filter_file(gauss, "ukf", j));

        cout << "   CUT-4" << endl;
        timer.tick();
        cut4.run(t, Ztru);
        run_times(j-1, 2) = timer.tock();
        cut4.save(filter_file(gauss, "cut4", j));

        cout << "   CUT-6" << endl;
        timer.tick();
        cut6.run(t, Ztru);
        run_times(j-1, 3) = timer.tock();
        cut6.save(filter_file(gauss, "cut6", j));

    }

    vector<string> filters;
    filters.push_back("house");
    filters.push_back("ukf");
    filters.push_back("cut4");
    filters.push_back("cut6");

    string time_file = "out/run_times_";
    time_file += (gauss ? "gauss" : "pearson");
    time_file += ".csv";

    EigenCSV::write(run_times, filters, time_file);

}

int main() {

    cout << "-- Gaussian Distributions --" << endl;
    run(true);

    cout << "-- Pearson Type IV Distributions --" << endl;
    run(false);

    return 0;

}

max_stars_repo_name: SIOSlab/HOUSE

max_issues_repo_name: SIOSlab/HOUSE

max_forks_repo_name: SIOSlab/HOUSE


text:
// Walk and kick: Team communications ROS interface
// Author: Philipp Allgeuer <pallgeuer@ais.uni-bonn.de>

// Includes
#include <walk_and_kick/wak_tc_ros.h>
#include <boost/make_shared.hpp>
#include <XmlRpcValue.h>

// Namespaces
using namespace walk_and_kick;

//
// TCRosInterface class
//

// Reset function
void TCRosInterface::resetData()
{
	// Reset the data of all the robot listeners
	for(std::size_t i = 0; i < m_teamCommsInternal.size(); i++)
		m_teamCommsInternal[i]->resetData();
}

// Start listening to team communications
void TCRosInterface::startListening(bool listenToSelf)
{
	// Clear the current list of robot team communications listeners
	m_teamCommsInternal.clear();
	m_teamComms.clear();

	// Retrieve our robot name
	std::string robotName = m_nh.param<std::string>("/robot_name", std::string());
	if(robotName.empty())
	{
		if(listenToSelf)
			ROS_ERROR("The '/robot_name' ROS parameter is empty => I don't know who I am!");
		else
			ROS_ERROR("The '/robot_name' ROS parameter is empty => I don't know who I am, so I will subscribe to all team communications!");
	}

	// Retrieve the list of robots from the parameter server and create subscribers for their respective team packets
	XmlRpc::XmlRpcValue list;
	bool teamCommsOk = false;
	if(m_nh.getParam("/robots", list))
	{
		if(list.getType() != XmlRpc::XmlRpcValue::TypeArray)
			ROS_ERROR("The '/robots' ROS parameter is not of type XmlRpcValue::TypeArray!");
		else
		{
			try
			{
				for(int i = 0; i < list.size(); i++)
				{
					XmlRpc::XmlRpcValue& value = list[i];
					if(value.getType() == XmlRpc::XmlRpcValue::TypeString)
					{
						std::string name = static_cast<std::string>(value);
						if(name.empty() || (!listenToSelf && name == robotName)) continue;
						ROS_INFO("Subscribing to team communications from: %s", name.c_str());
						TCRobotListenerPtr listener = boost::make_shared<TCRobotListener>(boost::ref(m_nh), name, (unsigned int) m_teamCommsInternal.size());
						m_teamCommsInternal.push_back(listener);
						m_teamComms.push_back(listener);
					}
					else
						ROS_ERROR("The '/robots' ROS parameter array contains non-string values!");
				}
				teamCommsOk = true;
			}
			catch(std::exception& e) { ROS_ERROR("Failed to process the '/robots' ROS parameter: %s", e.what()); }
		}
	}
	else
		ROS_ERROR("Failed to retrieve the list of robots from the '/robots' ROS parameter!");
	if(!teamCommsOk)
		ROS_ERROR("Failed to subscribe to the appropriate team communications topics!");
}

// Stop listening to team communications
void TCRosInterface::stopListening()
{
	// Clear the current list of robot team communications listeners
	m_teamCommsInternal.clear();
	m_teamComms.clear();
}

//
// TCRobotListener class
//

// Handle a new team communications packet
void TCRobotListener::handleTeamPacket(const TeamCommsDataConstPtr& msg)
{
	// Only accept the packet if the source time stamp has increased over what we have last heard, and if the packet ID has changed
	if(msg->timestamp >= m_lastEmbeddedStamp && msg->packetID != m_data.packetID)
	{
		m_lastEmbeddedStamp = msg->timestamp;
		ros::Time now = ros::Time::now();
		double timeDiff = (msg->timestamp - now).toSec();
		if(timeDiff < -10.0 || timeDiff > 5.0)
			ROS_WARN_THROTTLE(10.0, "A team packet received from the %s has a timestamp of %+.1fs relative to the current local time => Bad network or a time sync problem?", robot.c_str(), timeDiff);
		m_data = *msg;
		m_data.timestamp = now; // Override the timestamp with our local ROS time...
	}
}
// EOF