<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2018 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 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 <warn/push>
#include <warn/ignore/all>
#include <gtest/gtest.h>
#include <warn/pop>

#include <inviwo/core/common/inviwo.h>
#include <inviwo/core/common/inviwoapplication.h>

#include <inviwo/core/processors/processor.h>
#include <inviwo/core/network/processornetwork.h>
#include <inviwo/core/network/processornetworkevaluator.h>
#include <inviwo/core/network/networklock.h>

#include <inviwo/core/ports/datainport.h>
#include <inviwo/core/ports/dataoutport.h>

#include <functional>

namespace inviwo {

namespace {

struct TestProcessor : Processor {
    TestProcessor(const std::string& id) : Processor(id, id) {}

    virtual const ProcessorInfo getProcessorInfo() const override { return processorInfo_; }

    static const ProcessorInfo processorInfo_;

    virtual void initializeResources() override {
        if (onInitializeResources) onInitializeResources(*this);
    }
    virtual void process() override {
        if (onProcess) onProcess(*this);
    }
    virtual void doIfNotReady() override {
        if (onDoIfNotReady) onDoIfNotReady(*this);
    }

    std::function<void(TestProcessor&)> onInitializeResources;
    std::function<void(TestProcessor&)> onProcess;
    std::function<void(TestProcessor&)> onDoIfNotReady;
};

// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme
const ProcessorInfo TestProcessor::processorInfo_{
    "org.inviwo.TestProcessor",  // Class identifier
    "TestProcessor",             // Display name
    "Testing",                   // Category
    CodeState::Stable,           // Code state
    Tags::CPU,                   // Tags
};

struct Instrument {
    Instrument(TestProcessor& p) {
        name = p.getIdentifier();
        p.onInitializeResources = [this](TestProcessor&) { ++initRes; };
        p.onProcess = [this](TestProcessor&) { ++process; };
        p.onDoIfNotReady = [this](TestProcessor&) { ++notReady; };
    }

    void check(int exprectedInitRes, int exprectedProcess, int exprectedNotReady) {
        SCOPED_TRACE(name);
        EXPECT_EQ(initRes, exprectedInitRes);
        EXPECT_EQ(process, exprectedProcess);
        EXPECT_EQ(notReady, exprectedNotReady);
    }
    void reset() {
        initRes = 0;
        process = 0;
        notReady = 0;
    }
    void checkAndReset(int exprectedInitRes, int exprectedProcess, int exprectedNotReady) {
        check(exprectedInitRes, exprectedProcess, exprectedNotReady);
        reset();
    }

    std::string name = "";
    int initRes = 0;
    int process = 0;
    int notReady = 0;
};

}  // namespace

const auto createA = []() {
    auto at = std::make_unique<TestProcessor>("a");
    at->addPort(std::make_unique<DataOutport<int>>("out"));
    return at;
};

const auto createB = []() {
    auto bt = std::make_unique<TestProcessor>("b");
    bt->addPort(std::make_unique<DataInport<int>>("in"));
    return bt;
};

TEST(NetworkEvaluator, Eval) {
    ProcessorNetwork network{InviwoApplication::getPtr()};
    ProcessorNetworkEvaluator evaluator{&network};

    auto at = createA();
    auto a = at.get();
    Instrument ai(*a);

    a->onProcess = [func = a->onProcess](TestProcessor& p) {
        func(p);
        static_cast<DataOutport<int>*>(p.getOutports()[0])->setData(std::make_shared<int>(0));
    };

    {
        SCOPED_TRACE("Add a");
        network.addProcessor(std::move(at));
        ai.checkAndReset(0, 0, 0);
    }

    auto bt = createB();
    auto b = bt.get();
    Instrument bi(*b);

    {
        SCOPED_TRACE("Add b");
        network.addProcessor(std::move(bt));
        bi.checkAndReset(0, 0, 1);
    }

    {
        SCOPED_TRACE("Add connection");
        network.addConnection(a->getOutports()[0], b->getInports()[0]);
        ai.checkAndReset(1, 1, 0);
        bi.checkAndReset(1, 1, 0);
    }

    {
        SCOPED_TRACE("Invalid valid");
        a->invalidate(InvalidationLevel::Valid);
        ai.checkAndReset(0, 0, 0);
        bi.checkAndReset(0, 0, 0);
    }
    {
        SCOPED_TRACE("Invalid output");
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }
    {
        SCOPED_TRACE("Invalid resources");
        a->invalidate(InvalidationLevel::InvalidResources);
        ai.checkAndReset(1, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }
    {
        SCOPED_TRACE("Locked network");
        NetworkLock lock(&network);
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 0, 0);
        bi.checkAndReset(0, 0, 0);
        EXPECT_FALSE(a->isValid());
        EXPECT_FALSE(b->isValid());
    }
    {
        SCOPED_TRACE("Unlocked network");
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
        EXPECT_TRUE(a->isValid());
        EXPECT_TRUE(b->isValid());
    }
}

TEST(NetworkEvaluator, Error) {
    ProcessorNetwork network{InviwoApplication::getPtr()};
    ProcessorNetworkEvaluator evaluator{&network};

    auto at = createA();
    auto a = at.get();
    Instrument ai(*a);

    bool shouldThrow = false;
    a->onProcess = [func = a->onProcess, &shouldThrow](TestProcessor& p) {
        func(p);
        static_cast<DataOutport<int>*>(p.getOutports()[0])->setData(std::make_shared<int>(0));
        if (shouldThrow) {
            throw Exception("Error", IvwContextCustom("TestProcessor"));
        }
    };

    {
        SCOPED_TRACE("Add a");
        network.addProcessor(std::move(at));
        ai.checkAndReset(0, 0, 0);
    }

    auto bt = createB();
    auto b = bt.get();
    Instrument bi(*b);

    {
        SCOPED_TRACE("Add b");
        network.addProcessor(std::move(bt));
        bi.checkAndReset(0, 0, 1);
    }

    {
        SCOPED_TRACE("Add connection");
        network.addConnection(a->getOutports()[0], b->getInports()[0]);
        ai.checkAndReset(1, 1, 0);
        bi.checkAndReset(1, 1, 0);
    }

    {
        SCOPED_TRACE("Invalid output");
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }

    {
        SCOPED_TRACE("Invalid output with throw");
        shouldThrow = true;
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }

}

}  // namespace inviwo
<commit_msg>Testing: VS fix for failing core unit test on build * the default log message by the StandardEvaluationErrorHandler (caused by a non-caught exception) was interpreted by Visual Studio as a build error, thereby failing the unit test on build<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2018 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 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 <warn/push>
#include <warn/ignore/all>
#include <gtest/gtest.h>
#include <warn/pop>

#include <inviwo/core/common/inviwo.h>
#include <inviwo/core/common/inviwoapplication.h>

#include <inviwo/core/processors/processor.h>
#include <inviwo/core/network/processornetwork.h>
#include <inviwo/core/network/processornetworkevaluator.h>
#include <inviwo/core/network/networklock.h>

#include <inviwo/core/ports/datainport.h>
#include <inviwo/core/ports/dataoutport.h>

#include <functional>

namespace inviwo {

namespace {

struct TestProcessor : Processor {
    TestProcessor(const std::string& id) : Processor(id, id) {}

    virtual const ProcessorInfo getProcessorInfo() const override { return processorInfo_; }

    static const ProcessorInfo processorInfo_;

    virtual void initializeResources() override {
        if (onInitializeResources) onInitializeResources(*this);
    }
    virtual void process() override {
        if (onProcess) onProcess(*this);
    }
    virtual void doIfNotReady() override {
        if (onDoIfNotReady) onDoIfNotReady(*this);
    }

    std::function<void(TestProcessor&)> onInitializeResources;
    std::function<void(TestProcessor&)> onProcess;
    std::function<void(TestProcessor&)> onDoIfNotReady;
};

// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme
const ProcessorInfo TestProcessor::processorInfo_{
    "org.inviwo.TestProcessor",  // Class identifier
    "TestProcessor",             // Display name
    "Testing",                   // Category
    CodeState::Stable,           // Code state
    Tags::CPU,                   // Tags
};

struct Instrument {
    Instrument(TestProcessor& p) {
        name = p.getIdentifier();
        p.onInitializeResources = [this](TestProcessor&) { ++initRes; };
        p.onProcess = [this](TestProcessor&) { ++process; };
        p.onDoIfNotReady = [this](TestProcessor&) { ++notReady; };
    }

    void check(int exprectedInitRes, int exprectedProcess, int exprectedNotReady) {
        SCOPED_TRACE(name);
        EXPECT_EQ(initRes, exprectedInitRes);
        EXPECT_EQ(process, exprectedProcess);
        EXPECT_EQ(notReady, exprectedNotReady);
    }
    void reset() {
        initRes = 0;
        process = 0;
        notReady = 0;
    }
    void checkAndReset(int exprectedInitRes, int exprectedProcess, int exprectedNotReady) {
        check(exprectedInitRes, exprectedProcess, exprectedNotReady);
        reset();
    }

    std::string name = "";
    int initRes = 0;
    int process = 0;
    int notReady = 0;
};

}  // namespace

const auto createA = []() {
    auto at = std::make_unique<TestProcessor>("a");
    at->addPort(std::make_unique<DataOutport<int>>("out"));
    return at;
};

const auto createB = []() {
    auto bt = std::make_unique<TestProcessor>("b");
    bt->addPort(std::make_unique<DataInport<int>>("in"));
    return bt;
};

TEST(NetworkEvaluator, Eval) {
    ProcessorNetwork network{InviwoApplication::getPtr()};
    ProcessorNetworkEvaluator evaluator{&network};

    auto at = createA();
    auto a = at.get();
    Instrument ai(*a);

    a->onProcess = [func = a->onProcess](TestProcessor& p) {
        func(p);
        static_cast<DataOutport<int>*>(p.getOutports()[0])->setData(std::make_shared<int>(0));
    };

    {
        SCOPED_TRACE("Add a");
        network.addProcessor(std::move(at));
        ai.checkAndReset(0, 0, 0);
    }

    auto bt = createB();
    auto b = bt.get();
    Instrument bi(*b);

    {
        SCOPED_TRACE("Add b");
        network.addProcessor(std::move(bt));
        bi.checkAndReset(0, 0, 1);
    }

    {
        SCOPED_TRACE("Add connection");
        network.addConnection(a->getOutports()[0], b->getInports()[0]);
        ai.checkAndReset(1, 1, 0);
        bi.checkAndReset(1, 1, 0);
    }

    {
        SCOPED_TRACE("Invalid valid");
        a->invalidate(InvalidationLevel::Valid);
        ai.checkAndReset(0, 0, 0);
        bi.checkAndReset(0, 0, 0);
    }
    {
        SCOPED_TRACE("Invalid output");
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }
    {
        SCOPED_TRACE("Invalid resources");
        a->invalidate(InvalidationLevel::InvalidResources);
        ai.checkAndReset(1, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }
    {
        SCOPED_TRACE("Locked network");
        NetworkLock lock(&network);
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 0, 0);
        bi.checkAndReset(0, 0, 0);
        EXPECT_FALSE(a->isValid());
        EXPECT_FALSE(b->isValid());
    }
    {
        SCOPED_TRACE("Unlocked network");
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
        EXPECT_TRUE(a->isValid());
        EXPECT_TRUE(b->isValid());
    }
}

TEST(NetworkEvaluator, Error) {
    ProcessorNetwork network{InviwoApplication::getPtr()};
    ProcessorNetworkEvaluator evaluator{&network};

    auto at = createA();
    auto a = at.get();
    Instrument ai(*a);

    bool shouldThrow = false;
    a->onProcess = [func = a->onProcess, &shouldThrow](TestProcessor& p) {
        func(p);
        static_cast<DataOutport<int>*>(p.getOutports()[0])->setData(std::make_shared<int>(0));
        if (shouldThrow) {
            throw Exception("Error", IvwContextCustom("TestProcessor"));
        }
    };

    {
        SCOPED_TRACE("Add a");
        network.addProcessor(std::move(at));
        ai.checkAndReset(0, 0, 0);
    }

    auto bt = createB();
    auto b = bt.get();
    Instrument bi(*b);

    {
        SCOPED_TRACE("Add b");
        network.addProcessor(std::move(bt));
        bi.checkAndReset(0, 0, 1);
    }

    {
        SCOPED_TRACE("Add connection");
        network.addConnection(a->getOutports()[0], b->getInports()[0]);
        ai.checkAndReset(1, 1, 0);
        bi.checkAndReset(1, 1, 0);
    }

    {
        SCOPED_TRACE("Invalid output");
        a->invalidate(InvalidationLevel::InvalidOutput);
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }

    {
        SCOPED_TRACE("Invalid output with throw");
        unsigned int throwCount = 0;
        evaluator.setExceptionHandler(
            [&throwCount](Processor*, EvaluationType, ExceptionContext) { ++throwCount; });

        shouldThrow = true;
        a->invalidate(InvalidationLevel::InvalidOutput);
        EXPECT_EQ(throwCount, 1);
        ai.checkAndReset(0, 1, 0);
        bi.checkAndReset(0, 1, 0);
    }
}

}  // namespace inviwo
<|endoftext|>


<commit_before>/*
 * NotebookCache.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "SessionRnbParser.hpp"
#include "NotebookCache.hpp"
#include "NotebookChunkDefs.hpp"
#include "NotebookPaths.hpp"

#include <boost/foreach.hpp>

#include <session/SessionUserSettings.hpp>
#include <session/SessionModuleContext.hpp>
#include <session/SessionSourceDatabase.hpp>

#include <r/RExec.hpp>
#include <r/RRoutines.hpp>

// The version identifier for the cache format. Changing this invalidates old
// caches, and should be done only when making breaking changes to the
// cache format.
#define kCacheVersion "1"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {
namespace {

void onDocRemoved(const std::string& docId, const std::string& docPath)
{
   Error error;

   FilePath cacheFolder = chunkCacheFolder(docPath, docId);
   FilePath defFile = chunkDefinitionsPath(docPath, docId,
         userSettings().contextId());
   if (!docPath.empty() && defFile.exists())
   {
      // for saved documents, we want to keep the cache folder around even when
      // the document is closed, but only if the chunk definitions aren't out
      // of sync.
      FilePath docFile = module_context::resolveAliasedPath(docPath);
      std::time_t writeTime;
      error = getChunkDefs(docPath, docId, &writeTime, NULL);

      if (writeTime <= docFile.lastWriteTime())
      {
         // the doc has been saved since the last time the chunks defs were
         // updated, so no work to do here
         return;
      }
   }
   error = cacheFolder.removeIfExists();
   if (error)
      LOG_ERROR(error);
}

void onDocRenamed(const std::string& oldPath,
                  boost::shared_ptr<source_database::SourceDocument> pDoc)
{
   Error error;
   bool removeOldDir = false;

   // compute cache folders and ignore if we can't safely adjust them
   FilePath oldCacheDir = chunkCacheFolder(oldPath, pDoc->id());
   FilePath newCacheDir = chunkCacheFolder(pDoc->path(), pDoc->id());
   if (!oldCacheDir.exists() || newCacheDir.exists())
      return;

   // if the doc was previously unsaved, we can just move the whole folder
   // to its newly saved location
   if (oldPath.empty())
   {
      error = oldCacheDir.move(newCacheDir);
      if (error)
      {
         // if we can't move the cache to the new location, we'll fall back to
         // copy/remove
         removeOldDir = true;
      }
      else
         return;
   }

   error = oldCacheDir.copyDirectoryRecursive(newCacheDir);
   if (error)
   {
      LOG_ERROR(error);
   }
   else if (removeOldDir)
   {
      // remove old dir if we couldn't move the folder above
      error = oldCacheDir.remove();
      if (error)
         LOG_ERROR(error);
   }
}

void onDocAdded(const std::string& id)
{
   std::string path;
   Error error = source_database::getPath(id, &path);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // ignore empty paths and non-R Markdown files
   if (path.empty())
      return;
   FilePath docPath = module_context::resolveAliasedPath(path);
   if (docPath.extensionLowerCase() != ".rmd")
      return;

   FilePath cachePath = chunkCacheFolder(path, id);
   FilePath nbPath = docPath.parent().complete(docPath.stem() + ".Rnb");

   // clean up incompatible cache versions (as we're about to invalidate them
   // by mutating the document without updating them)
   if (cachePath.parent().exists())
   {
      std::vector<FilePath> versions;
      cachePath.parent().children(&versions);
      BOOST_FOREACH(const FilePath& version, versions)
      {
         if (version.isDirectory() && version.filename() != kCacheVersion)
         {
            error = version.remove();
            if (error)
               LOG_ERROR(error);
         }
      }
   }

   if (!cachePath.exists() && nbPath.exists())
   {
      // we have a saved representation, but no cache -- populate the cache
      // from the saved representation
      error = parseRnb(nbPath, cachePath);
      if (error)
      {
         LOG_ERROR(error);
         return;
      }
   }

   // TODO: consider write times of document, cache, and .Rnb -- are there
   // combinations which would suggest we should overwrite the cache with the
   // contents of the notebook?
}

FilePath unsavedNotebookCache()
{
   return module_context::sessionScratchPath().childPath("unsaved-notebooks");
}

SEXP rs_populateNotebookCache(SEXP fileSEXP)
{
   std::string file = r::sexp::safeAsString(fileSEXP);
   FilePath cacheFolder =
      chunkCacheFolder(file, "", userSettings().contextId());
   Error error = parseRnb(module_context::resolveAliasedPath(file),
                          cacheFolder);
   if (error)
      LOG_ERROR(error);

   r::sexp::Protect rProtect;
   return r::sexp::create(cacheFolder.absolutePath(), &rProtect);
}

} // anonymous namespace

FilePath notebookCacheRoot()
{
   return module_context::sharedScratchPath().childPath("notebooks");
}

FilePath chunkCacheFolder(const std::string& docPath, const std::string& docId,
      const std::string& contextId)
{
   FilePath folder;
   std::string stem;

   if (docPath.empty())
   {
      // the doc hasn't been saved, so keep its chunk output in the scratch
      // path
      folder = unsavedNotebookCache().childPath(docId);
   }
   else
   {
      // the doc has been saved, so keep its chunk output alongside the doc
      // itself
      FilePath path = module_context::resolveAliasedPath(docPath);

      std::string id;
      Error error = notebookPathToId(path, contextId, &id);
      if (error)
         LOG_ERROR(error);

      folder = notebookCacheRoot().childPath(contextId + "-" + id + "-" +
            path.stem());
   }

   return folder.childPath(kCacheVersion);
}

FilePath chunkCacheFolder(const std::string& docPath, const std::string& docId)
{
   return chunkCacheFolder(docPath, docId, userSettings().contextId());
}

Error initCache()
{
   source_database::events().onDocRenamed.connect(onDocRenamed);
   source_database::events().onDocRemoved.connect(onDocRemoved);
   source_database::events().onDocAdded.connect(onDocAdded);

   RS_REGISTER_CALL_METHOD(rs_populateNotebookCache, 1);

   return Success();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<commit_msg>expose method to R<commit_after>/*
 * NotebookCache.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "SessionRnbParser.hpp"
#include "NotebookCache.hpp"
#include "NotebookChunkDefs.hpp"
#include "NotebookPaths.hpp"

#include <boost/foreach.hpp>

#include <session/SessionUserSettings.hpp>
#include <session/SessionModuleContext.hpp>
#include <session/SessionSourceDatabase.hpp>

#include <r/RExec.hpp>
#include <r/RRoutines.hpp>

// The version identifier for the cache format. Changing this invalidates old
// caches, and should be done only when making breaking changes to the
// cache format.
#define kCacheVersion "1"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {
namespace {

void onDocRemoved(const std::string& docId, const std::string& docPath)
{
   Error error;

   FilePath cacheFolder = chunkCacheFolder(docPath, docId);
   FilePath defFile = chunkDefinitionsPath(docPath, docId,
         userSettings().contextId());
   if (!docPath.empty() && defFile.exists())
   {
      // for saved documents, we want to keep the cache folder around even when
      // the document is closed, but only if the chunk definitions aren't out
      // of sync.
      FilePath docFile = module_context::resolveAliasedPath(docPath);
      std::time_t writeTime;
      error = getChunkDefs(docPath, docId, &writeTime, NULL);

      if (writeTime <= docFile.lastWriteTime())
      {
         // the doc has been saved since the last time the chunks defs were
         // updated, so no work to do here
         return;
      }
   }
   error = cacheFolder.removeIfExists();
   if (error)
      LOG_ERROR(error);
}

void onDocRenamed(const std::string& oldPath,
                  boost::shared_ptr<source_database::SourceDocument> pDoc)
{
   Error error;
   bool removeOldDir = false;

   // compute cache folders and ignore if we can't safely adjust them
   FilePath oldCacheDir = chunkCacheFolder(oldPath, pDoc->id());
   FilePath newCacheDir = chunkCacheFolder(pDoc->path(), pDoc->id());
   if (!oldCacheDir.exists() || newCacheDir.exists())
      return;

   // if the doc was previously unsaved, we can just move the whole folder
   // to its newly saved location
   if (oldPath.empty())
   {
      error = oldCacheDir.move(newCacheDir);
      if (error)
      {
         // if we can't move the cache to the new location, we'll fall back to
         // copy/remove
         removeOldDir = true;
      }
      else
         return;
   }

   error = oldCacheDir.copyDirectoryRecursive(newCacheDir);
   if (error)
   {
      LOG_ERROR(error);
   }
   else if (removeOldDir)
   {
      // remove old dir if we couldn't move the folder above
      error = oldCacheDir.remove();
      if (error)
         LOG_ERROR(error);
   }
}

void onDocAdded(const std::string& id)
{
   std::string path;
   Error error = source_database::getPath(id, &path);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // ignore empty paths and non-R Markdown files
   if (path.empty())
      return;
   FilePath docPath = module_context::resolveAliasedPath(path);
   if (docPath.extensionLowerCase() != ".rmd")
      return;

   FilePath cachePath = chunkCacheFolder(path, id);
   FilePath nbPath = docPath.parent().complete(docPath.stem() + ".Rnb");

   // clean up incompatible cache versions (as we're about to invalidate them
   // by mutating the document without updating them)
   if (cachePath.parent().exists())
   {
      std::vector<FilePath> versions;
      cachePath.parent().children(&versions);
      BOOST_FOREACH(const FilePath& version, versions)
      {
         if (version.isDirectory() && version.filename() != kCacheVersion)
         {
            error = version.remove();
            if (error)
               LOG_ERROR(error);
         }
      }
   }

   if (!cachePath.exists() && nbPath.exists())
   {
      // we have a saved representation, but no cache -- populate the cache
      // from the saved representation
      error = parseRnb(nbPath, cachePath);
      if (error)
      {
         LOG_ERROR(error);
         return;
      }
   }

   // TODO: consider write times of document, cache, and .Rnb -- are there
   // combinations which would suggest we should overwrite the cache with the
   // contents of the notebook?
}

FilePath unsavedNotebookCache()
{
   return module_context::sessionScratchPath().childPath("unsaved-notebooks");
}

SEXP rs_populateNotebookCache(SEXP fileSEXP)
{
   std::string file = r::sexp::safeAsString(fileSEXP);
   FilePath cacheFolder =
      chunkCacheFolder(file, "", userSettings().contextId());
   Error error = parseRnb(module_context::resolveAliasedPath(file),
                          cacheFolder);
   if (error)
      LOG_ERROR(error);

   r::sexp::Protect rProtect;
   return r::sexp::create(cacheFolder.absolutePath(), &rProtect);
}

SEXP rs_chunkCacheFolder(SEXP fileSEXP)
{
   std::string file = r::sexp::safeAsString(fileSEXP);
   FilePath cacheFolder =
         chunkCacheFolder(file, "", userSettings().contextId());

   r::sexp::Protect protect;
   return r::sexp::create(cacheFolder.absolutePath(), &protect);
}

} // anonymous namespace

FilePath notebookCacheRoot()
{
   return module_context::sharedScratchPath().childPath("notebooks");
}

FilePath chunkCacheFolder(const std::string& docPath, const std::string& docId,
      const std::string& contextId)
{
   FilePath folder;
   std::string stem;

   if (docPath.empty())
   {
      // the doc hasn't been saved, so keep its chunk output in the scratch
      // path
      folder = unsavedNotebookCache().childPath(docId);
   }
   else
   {
      // the doc has been saved, so keep its chunk output alongside the doc
      // itself
      FilePath path = module_context::resolveAliasedPath(docPath);

      std::string id;
      Error error = notebookPathToId(path, contextId, &id);
      if (error)
         LOG_ERROR(error);

      folder = notebookCacheRoot().childPath(contextId + "-" + id + "-" +
            path.stem());
   }

   return folder.childPath(kCacheVersion);
}

FilePath chunkCacheFolder(const std::string& docPath, const std::string& docId)
{
   return chunkCacheFolder(docPath, docId, userSettings().contextId());
}

Error initCache()
{
   source_database::events().onDocRenamed.connect(onDocRenamed);
   source_database::events().onDocRemoved.connect(onDocRemoved);
   source_database::events().onDocAdded.connect(onDocAdded);

   RS_REGISTER_CALL_METHOD(rs_populateNotebookCache, 1);
   RS_REGISTER_CALL_METHOD(rs_chunkCacheFolder, 1);

   return Success();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<|endoftext|>


<commit_before>/*
 * NotebookPlots.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "NotebookPlots.hpp"
#include "../SessionPlots.hpp"

#include <boost/format.hpp>
#include <boost/foreach.hpp>
#include <boost/signals/connection.hpp>

#include <core/system/FileMonitor.hpp>
#include <core/StringUtils.hpp>

#include <session/SessionModuleContext.hpp>

#include <r/RExec.hpp>
#include <r/RSexp.hpp>
#include <r/session/RGraphics.hpp>

#define kPlotPrefix "_rs_chunk_plot_"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {
namespace {

class PlotState
{
public:
   PlotState():
      hasPlots(false)
   {
   }

   bool hasPlots;
   r::sexp::PreservedSEXP sexpMargins;
   boost::signals::connection onConsolePrompt;
   boost::signals::connection onNewPlot;
};

bool isPlotPath(const FilePath& path)
{
   return path.hasExtensionLowerCase(".png") &&
          string_utils::isPrefixOf(path.stem(), kPlotPrefix);
}

void processPlots(const FilePath& plotFolder,
                  bool ignoreEmpty,
                  boost::shared_ptr<PlotState> pPlotState)
{
   // ensure plot folder exists
   if (!plotFolder.exists())
      return;

   // collect plots from the folder
   std::vector<FilePath> folderContents;
   Error error = plotFolder.children(&folderContents);
   if (error)
      LOG_ERROR(error);

   BOOST_FOREACH(const FilePath& path, folderContents)
   {
      if (isPlotPath(path))
      {
         // we might find an empty plot file if it hasn't been flushed to disk
         // yet--ignore these
         if (ignoreEmpty && path.size() == 0)
            continue;

#ifdef _WIN32
   // on Windows, turning off the PNG device writes an empty PNG file if no
   // plot output occurs; we avoid treating that empty file as an actual plot
   // by only emitting an event if a plot occurred.
   //
   // TODO: not all plot libraries cause the new plot hooks to invoke, so this
   // heuristic may cause us to miss a plot on Windows; we may need some
   // mechanism by which we can determine whether the device or its output is
   // empty.
         if (pPlotState->hasPlots)
#endif
            events().onPlotOutput(path);

         // clean up the plot so it isn't emitted twice
         error = path.removeIfExists();
         if (error)
            LOG_ERROR(error);
      }
   }
}

void removeGraphicsDevice(const FilePath& plotFolder,
                          boost::shared_ptr<PlotState> pPlotState)
{

   // restore the figure margins
   Error error = r::exec::RFunction("par", pPlotState->sexpMargins).call();
   if (error)
      LOG_ERROR(error);

   // turn off the graphics device -- this has the side effect of writing the
   // device's remaining output to files
   error = r::exec::RFunction("dev.off").call();
   if (error)
      LOG_ERROR(error);

   processPlots(plotFolder, false, pPlotState);
}

void onNewPlot(const FilePath& plotFolder,
               boost::shared_ptr<PlotState> pPlotState)
{
   pPlotState->hasPlots = true;
   processPlots(plotFolder, true, pPlotState);
}

void onConsolePrompt(const FilePath& plotFolder,
                     boost::shared_ptr<PlotState> pPlotState,
                     const std::string& )
{
   removeGraphicsDevice(plotFolder, pPlotState);
   pPlotState->onConsolePrompt.disconnect();
   pPlotState->onNewPlot.disconnect();
}

} // anonymous namespace

// begins capturing plot output
core::Error beginPlotCapture(const FilePath& plotFolder)
{
   // clean up any stale plots from the folder
   std::vector<FilePath> folderContents;
   Error error = plotFolder.children(&folderContents);
   if (error)
      return error;

   BOOST_FOREACH(const core::FilePath& file, folderContents)
   {
      // remove if it looks like a plot
      if (isPlotPath(file))
      {
         error = file.remove();
         if (error)
         {
            // this is non-fatal
            LOG_ERROR(error);
         }
      }
   }

   // generate code for creating PNG device
   boost::format fmt("{ require(grDevices, quietly=TRUE); "
                     "  png(file = \"%1%/" kPlotPrefix "%%03d.png\", "
                     "  width = 6.5, height = 4, "
                     "  units=\"in\", res = 96 %2%)"
                     "}");

   // marker for content; this is necessary because on Windows, turning off
   // the png device writes an empty PNG file even if nothing was plotted, and
   // we need to avoid treating that file as though it were an actual plot
   boost::shared_ptr<PlotState> pPlotState = boost::make_shared<PlotState>();

   // create the PNG device
   error = r::exec::executeString(
         (fmt % string_utils::utf8ToSystem(plotFolder.absolutePath())
              % r::session::graphics::extraBitmapParams()).str());
   if (error)
      return error;

   // save old plot state
   r::exec::RFunction par("par");
   par.addParam("no.readonly", true);
   r::sexp::Protect protect;
   SEXP sexpMargins;
   error = par.call(&sexpMargins, &protect);
   if (error)
      LOG_ERROR(error);

   // preserve until chunk is finished executing
   pPlotState->sexpMargins.set(sexpMargins);

   // set notebook-friendly figure margins
   //                                          bot  left top  right
   error = r::exec::executeString("par(mar = c(5.1, 4.1, 2.1, 2.1))");
   if (error)
      LOG_ERROR(error);

   // complete the capture on the next console prompt
   pPlotState->onConsolePrompt = module_context::events().onConsolePrompt.connect(
         boost::bind(onConsolePrompt, plotFolder, pPlotState, _1));

   pPlotState->onNewPlot = plots::events().onNewPlot.connect(
         boost::bind(onNewPlot, plotFolder, pPlotState));

   return Success();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<commit_msg>begin recording snapshots before plots are emitted<commit_after>/*
 * NotebookPlots.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "NotebookPlots.hpp"
#include "../SessionPlots.hpp"

#include <boost/format.hpp>
#include <boost/foreach.hpp>
#include <boost/signals/connection.hpp>

#include <core/system/FileMonitor.hpp>
#include <core/StringUtils.hpp>

#include <session/SessionModuleContext.hpp>

#include <r/RExec.hpp>
#include <r/RSexp.hpp>
#include <r/session/RGraphics.hpp>

#define kPlotPrefix "_rs_chunk_plot_"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {
namespace {

class PlotState
{
public:
   PlotState(const FilePath& folder):
      plotFolder(folder),
      hasPlots(false)
   {
   }

   FilePath plotFolder;
   bool hasPlots;
   FilePath snapshotFile;
   r::sexp::PreservedSEXP sexpMargins;
   boost::signals::connection onConsolePrompt;
   boost::signals::connection onBeforeNewPlot;
   boost::signals::connection onNewPlot;
};

bool isPlotPath(const FilePath& path)
{
   return path.hasExtensionLowerCase(".png") &&
          string_utils::isPrefixOf(path.stem(), kPlotPrefix);
}

void processPlots(bool ignoreEmpty,
                  boost::shared_ptr<PlotState> pPlotState)
{
   // ensure plot folder exists
   if (!pPlotState->plotFolder.exists())
      return;

   // collect plots from the folder
   std::vector<FilePath> folderContents;
   Error error = pPlotState->plotFolder.children(&folderContents);
   if (error)
      LOG_ERROR(error);

   BOOST_FOREACH(const FilePath& path, folderContents)
   {
      if (isPlotPath(path))
      {
         // we might find an empty plot file if it hasn't been flushed to disk
         // yet--ignore these
         if (ignoreEmpty && path.size() == 0)
            continue;

#ifdef _WIN32
   // on Windows, turning off the PNG device writes an empty PNG file if no
   // plot output occurs; we avoid treating that empty file as an actual plot
   // by only emitting an event if a plot occurred.
   //
   // TODO: not all plot libraries cause the new plot hooks to invoke, so this
   // heuristic may cause us to miss a plot on Windows; we may need some
   // mechanism by which we can determine whether the device or its output is
   // empty.
         if (pPlotState->hasPlots)
#endif
            events().onPlotOutput(path);

         // clean up the plot so it isn't emitted twice
         error = path.removeIfExists();
         if (error)
            LOG_ERROR(error);
      }
   }
}

void removeGraphicsDevice(boost::shared_ptr<PlotState> pPlotState)
{
   // restore the figure margins
   Error error = r::exec::RFunction("par", pPlotState->sexpMargins).call();
   if (error)
      LOG_ERROR(error);

   // turn off the graphics device -- this has the side effect of writing the
   // device's remaining output to files
   error = r::exec::RFunction("dev.off").call();
   if (error)
      LOG_ERROR(error);

   processPlots(false, pPlotState);
}

void onBeforeNewPlot(boost::shared_ptr<PlotState> pPlotState)
{
   if (pPlotState->hasPlots)
   {
      // if there's a plot on the device, write its display list before it's
      // cleared for the next page
      FilePath outputFile = pPlotState->plotFolder.complete(
            core::system::generateUuid(false) + ".snapshot");
      Error error = r::exec::RFunction(".rs.saveGraphics",
            outputFile.absolutePath()).call();
      if (error)
      {
         pPlotState->snapshotFile = FilePath();
         LOG_ERROR(error);
      }
      else
      {
         pPlotState->snapshotFile = outputFile;
      }
   }
   pPlotState->hasPlots = true;
}

void onNewPlot(boost::shared_ptr<PlotState> pPlotState)
{
   pPlotState->hasPlots = true;
   processPlots(true, pPlotState);
}

void onConsolePrompt(boost::shared_ptr<PlotState> pPlotState,
                     const std::string& )
{
   removeGraphicsDevice(pPlotState);
   pPlotState->onConsolePrompt.disconnect();
   pPlotState->onNewPlot.disconnect();
   pPlotState->onBeforeNewPlot.disconnect();
}

} // anonymous namespace

// begins capturing plot output
core::Error beginPlotCapture(const FilePath& plotFolder)
{
   // clean up any stale plots from the folder
   std::vector<FilePath> folderContents;
   Error error = plotFolder.children(&folderContents);
   if (error)
      return error;

   BOOST_FOREACH(const core::FilePath& file, folderContents)
   {
      // remove if it looks like a plot
      if (isPlotPath(file))
      {
         error = file.remove();
         if (error)
         {
            // this is non-fatal
            LOG_ERROR(error);
         }
      }
   }

   // generate code for creating PNG device
   boost::format fmt("{ require(grDevices, quietly=TRUE); "
                     "  png(file = \"%1%/" kPlotPrefix "%%03d.png\", "
                     "  width = 6.5, height = 4, "
                     "  units=\"in\", res = 96 %2%)"
                     "}");

   // marker for content; this is necessary because on Windows, turning off
   // the png device writes an empty PNG file even if nothing was plotted, and
   // we need to avoid treating that file as though it were an actual plot
   boost::shared_ptr<PlotState> pPlotState = boost::make_shared<PlotState>(
         plotFolder);

   // create the PNG device
   error = r::exec::executeString(
         (fmt % string_utils::utf8ToSystem(plotFolder.absolutePath())
              % r::session::graphics::extraBitmapParams()).str());
   if (error)
      return error;

   // save old plot state
   r::exec::RFunction par("par");
   par.addParam("no.readonly", true);
   r::sexp::Protect protect;
   SEXP sexpMargins;
   error = par.call(&sexpMargins, &protect);
   if (error)
      LOG_ERROR(error);

   // preserve until chunk is finished executing
   pPlotState->sexpMargins.set(sexpMargins);

   // set notebook-friendly figure margins
   //                                          bot  left top  right
   error = r::exec::executeString("par(mar = c(5.1, 4.1, 2.1, 2.1))");
   if (error)
      LOG_ERROR(error);

   // complete the capture on the next console prompt
   pPlotState->onConsolePrompt = module_context::events().onConsolePrompt.connect(
         boost::bind(onConsolePrompt, pPlotState, _1));

   pPlotState->onBeforeNewPlot = plots::events().onBeforeNewPlot.connect(
         boost::bind(onBeforeNewPlot, pPlotState));

   pPlotState->onNewPlot = plots::events().onNewPlot.connect(
         boost::bind(onNewPlot, pPlotState));

   return Success();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

//TESTED_COMPONENT=src/systeminfo

#include <QtTest/QtTest>
#include "qsysteminfo.h"
#include "qsysteminfo_simulator_p.h"

QTM_USE_NAMESPACE
Q_DECLARE_METATYPE(QSystemStorageInfo::DriveType);
Q_DECLARE_METATYPE(QSystemStorageInfo::StorageState);

/**
 * Starts an event loop that runs until the given signal is received.
 * Optionally the event loop can return earlier on a timeout.
 *
 * \return \p true if the requested signal was received
 *         \p false on timeout
 */
static bool waitForSignal(QObject *obj, const char *signal, int timeout = 0)
{
    QEventLoop loop;
    QObject::connect(obj, signal, &loop, SLOT(quit()));
    QTimer timer;
    QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
    if (timeout > 0) {
        QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
        timer.setSingleShot(true);
        timer.start(timeout);
    }
    loop.exec();
    return timeoutSpy.isEmpty();
}

class ChangeStorageThread : public QThread
{
public:
    void run()
    {
        QMutexLocker locker(&mutex);
        SystemInfoConnection si;
        QSystemStorageInfoPrivate *st = si.storageInfoPrivate();

//        if (addIt) {
//            if (!remove) {
//                st->addDrive(volName, driveType, totalSpace, availableSpace,"XXX");
//            } else {
//                st->removeDrive(volName);
//            }
//        } else {
//            st->setTotalSpace(volName,totalSpace);
//            st->setAvailableSpace(volName,availableSpace);
//            st->setStorageState(volName,state);
//        }

        this->exit();

    }
    QString volName;
    quint64 totalSpace;
    quint64 availableSpace;
    QSystemStorageInfo::StorageState state;
    QSystemStorageInfo::DriveType driveType;
    bool addIt;
    bool remove;
    QMutex mutex;
};

class tst_QSystemStorageInfo : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();

    void tst_totalDiskSpace();
    void tst_availableDiskSpace();
    void tst_logicalDrives();
    void tst_typeForDrive();

    void tst_getStorageState();

    void tst_storageStateChanged_data();
    void tst_storageStateChanged();

    void tst_logicalDriveChanged_data();
    void tst_logicalDriveChanged();

    void logicalDriveChanged(bool added,const QString &vol);
    void storageStateChanged(const QString &vol, QSystemStorageInfo::StorageState state); //1.2

private:
    void newDrives();
    QSystemStorageInfo::StorageState sstate;
    QSystemStorageInfo::DriveType driveType;
    bool driveAdded;
    QString dName;
};

void tst_QSystemStorageInfo::initTestCase()
{
    qRegisterMetaType<QSystemStorageInfo::DriveType>("QSystemStorageInfo::VolumeType");
}

void tst_QSystemStorageInfo::tst_totalDiskSpace()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QVERIFY(mi.totalDiskSpace(vol) > -1);
    }
}

void tst_QSystemStorageInfo::tst_availableDiskSpace()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QVERIFY(mi.availableDiskSpace(vol) > -1);
    }
}

void tst_QSystemStorageInfo::tst_logicalDrives()
{
    QSystemStorageInfo mi;
    QVERIFY(mi.logicalDrives().count() > -1);
}

void tst_QSystemStorageInfo::tst_typeForDrive()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QSystemStorageInfo::DriveType type = mi.typeForDrive(vol);

        QVERIFY(type == QSystemStorageInfo::NoDrive
                || type == QSystemStorageInfo::CdromDrive
                || type == QSystemStorageInfo::InternalDrive
                || type == QSystemStorageInfo::RemoteDrive
                || type == QSystemStorageInfo::RemovableDrive
                || type == QSystemStorageInfo::InternalFlashDrive
                || type == QSystemStorageInfo::RamDrive
                );
    }
}

void tst_QSystemStorageInfo::tst_getStorageState()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QSystemStorageInfo::StorageState state = mi.getStorageState(vol);
        QVERIFY(state == QSystemStorageInfo::UnknownStorageState
                || state == QSystemStorageInfo::NormalStorageState
                || state == QSystemStorageInfo::LowStorageState
                || state == QSystemStorageInfo::VeryLowStorageState
                || state == QSystemStorageInfo::CriticalStorageState);
    }
}

void tst_QSystemStorageInfo::tst_storageStateChanged_data()
{
    QTest::addColumn<QSystemStorageInfo::DriveType>("drivetype");
    QTest::addColumn<QSystemStorageInfo::StorageState>("storagestate");
    QTest::addColumn<QString>("drivename");

    QTest::newRow("NoDrive") << QSystemStorageInfo::NoDrive
                             << QSystemStorageInfo::UnknownStorageState
                             << "NoDrive";
    QTest::newRow("InternalDrive") << QSystemStorageInfo::InternalDrive
                                      << QSystemStorageInfo::NormalStorageState
                                      << "InternalDrive";
    QTest::newRow("RemovableDrive") << QSystemStorageInfo::RemovableDrive
                                       << QSystemStorageInfo::LowStorageState
                                           << "RemovableDrive";
    QTest::newRow("RemoteDrive") << QSystemStorageInfo::RemoteDrive
                                    << QSystemStorageInfo::VeryLowStorageState
                                       << "RemoteDrive";
    QTest::newRow("CdromDrive") << QSystemStorageInfo::CdromDrive
                                   << QSystemStorageInfo::CriticalStorageState
                                       << "CdromDrive";
    QTest::newRow("InternalFlashDrive") << QSystemStorageInfo::InternalFlashDrive
                                        << QSystemStorageInfo::UnknownStorageState
                                           << "InternalFlashDrive";
    QTest::newRow("RamDrive") << QSystemStorageInfo::RamDrive
                                 << QSystemStorageInfo::UnknownStorageState
                                 << "RamDrive";
    SystemInfoConnection si;
    QSystemStorageInfo sti;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();
    QStringList curDrives = sti.logicalDrives();
    foreach (const QString &name, curDrives)
        st->removeDrive(name);

}

void tst_QSystemStorageInfo::newDrives()
{
    QFETCH(QString, drivename);
    QFETCH(QSystemStorageInfo::DriveType, drivetype);

    QSystemStorageInfo sti;
    QStringList curDrives = sti.logicalDrives();
    SystemInfoConnection si;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();

    st->addDrive(drivename, drivetype, 100, 90,"YYY");
}

void tst_QSystemStorageInfo::tst_storageStateChanged()
{
    QFETCH(QSystemStorageInfo::DriveType, drivetype);
    QFETCH(QString, drivename);

    QSystemStorageInfo sti;
    QStringList curDrives = sti.logicalDrives();
    SystemInfoConnection si;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();
    driveAdded = true;
    dName = drivename;
    st->addDrive(drivename, drivetype, 100, 50,"YYY");
    connect(&sti,SIGNAL(storageStateChanged(const QString &, QSystemStorageInfo::StorageState)),
            this,SLOT(storageStateChanged(const QString &, QSystemStorageInfo::StorageState)));

    sstate = QSystemStorageInfo::CriticalStorageState;
    st->setAvailableSpace(drivename,1);
    QVERIFY(sti.availableDiskSpace(drivename) == 1);
    sstate = QSystemStorageInfo::NormalStorageState;
    st->setAvailableSpace(drivename,99);
    QVERIFY(sti.availableDiskSpace(drivename) == 99);
    sstate = QSystemStorageInfo::NormalStorageState;
    st->setAvailableSpace(drivename,41);
    QVERIFY(sti.availableDiskSpace(drivename) == 41);
    sstate = QSystemStorageInfo::LowStorageState;
    st->setAvailableSpace(drivename,40);
    QVERIFY(sti.availableDiskSpace(drivename) == 40);
    sstate = QSystemStorageInfo::VeryLowStorageState;
    st->setAvailableSpace(drivename,9);
    QVERIFY(sti.availableDiskSpace(drivename) == 9);

    driveAdded = false;
    st->removeDrive(drivename);
}

void tst_QSystemStorageInfo::tst_logicalDriveChanged_data()
{
    tst_storageStateChanged_data();
}

void tst_QSystemStorageInfo::tst_logicalDriveChanged()
{
    QFETCH(QSystemStorageInfo::DriveType, drivetype);
    QFETCH(QString, drivename);

    QSystemStorageInfo sti;
    QStringList curDrives = sti.logicalDrives();
    SystemInfoConnection si;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();

    connect(&sti,SIGNAL(logicalDriveChanged(bool,const QString &)),
            this,SLOT(logicalDriveChanged(bool,const QString &)));

    driveAdded = true;
    dName = drivename;
    st->addDrive(drivename, drivetype, 100, 50,"YYY");

    driveAdded = false;
    st->removeDrive(drivename);
}

void tst_QSystemStorageInfo::logicalDriveChanged(bool added,const QString &vol)
{
    QVERIFY(added == driveAdded);
    QVERIFY(vol == dName);
}

void tst_QSystemStorageInfo::storageStateChanged(const QString &vol, QSystemStorageInfo::StorageState state)
{
    QVERIFY(vol == dName);
    QVERIFY(state == sstate);
}

QTEST_MAIN(tst_QSystemStorageInfo)
#include "tst_qsystemstorageinfo.moc"
<commit_msg>remove unused thread code<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

//TESTED_COMPONENT=src/systeminfo

#include <QtTest/QtTest>
#include "qsysteminfo.h"
#include "qsysteminfo_simulator_p.h"

QTM_USE_NAMESPACE
Q_DECLARE_METATYPE(QSystemStorageInfo::DriveType);
Q_DECLARE_METATYPE(QSystemStorageInfo::StorageState);

/**
 * Starts an event loop that runs until the given signal is received.
 * Optionally the event loop can return earlier on a timeout.
 *
 * \return \p true if the requested signal was received
 *         \p false on timeout
 */
static bool waitForSignal(QObject *obj, const char *signal, int timeout = 0)
{
    QEventLoop loop;
    QObject::connect(obj, signal, &loop, SLOT(quit()));
    QTimer timer;
    QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
    if (timeout > 0) {
        QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
        timer.setSingleShot(true);
        timer.start(timeout);
    }
    loop.exec();
    return timeoutSpy.isEmpty();
}

class tst_QSystemStorageInfo : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();

    void tst_totalDiskSpace();
    void tst_availableDiskSpace();
    void tst_logicalDrives();
    void tst_typeForDrive();

    void tst_getStorageState();

    void tst_storageStateChanged_data();
    void tst_storageStateChanged();

    void tst_logicalDriveChanged_data();
    void tst_logicalDriveChanged();

    void logicalDriveChanged(bool added,const QString &vol);
    void storageStateChanged(const QString &vol, QSystemStorageInfo::StorageState state); //1.2

private:
    void newDrives();
    QSystemStorageInfo::StorageState sstate;
    QSystemStorageInfo::DriveType driveType;
    bool driveAdded;
    QString dName;
};

void tst_QSystemStorageInfo::initTestCase()
{
    qRegisterMetaType<QSystemStorageInfo::DriveType>("QSystemStorageInfo::VolumeType");
}

void tst_QSystemStorageInfo::tst_totalDiskSpace()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QVERIFY(mi.totalDiskSpace(vol) > -1);
    }
}

void tst_QSystemStorageInfo::tst_availableDiskSpace()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QVERIFY(mi.availableDiskSpace(vol) > -1);
    }
}

void tst_QSystemStorageInfo::tst_logicalDrives()
{
    QSystemStorageInfo mi;
    QVERIFY(mi.logicalDrives().count() > -1);
}

void tst_QSystemStorageInfo::tst_typeForDrive()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QSystemStorageInfo::DriveType type = mi.typeForDrive(vol);

        QVERIFY(type == QSystemStorageInfo::NoDrive
                || type == QSystemStorageInfo::CdromDrive
                || type == QSystemStorageInfo::InternalDrive
                || type == QSystemStorageInfo::RemoteDrive
                || type == QSystemStorageInfo::RemovableDrive
                || type == QSystemStorageInfo::InternalFlashDrive
                || type == QSystemStorageInfo::RamDrive
                );
    }
}

void tst_QSystemStorageInfo::tst_getStorageState()
{
    QSystemStorageInfo mi;
    QStringList volList = mi.logicalDrives();
    foreach(QString vol, volList) {
        QSystemStorageInfo::StorageState state = mi.getStorageState(vol);
        QVERIFY(state == QSystemStorageInfo::UnknownStorageState
                || state == QSystemStorageInfo::NormalStorageState
                || state == QSystemStorageInfo::LowStorageState
                || state == QSystemStorageInfo::VeryLowStorageState
                || state == QSystemStorageInfo::CriticalStorageState);
    }
}

void tst_QSystemStorageInfo::tst_storageStateChanged_data()
{
    QTest::addColumn<QSystemStorageInfo::DriveType>("drivetype");
    QTest::addColumn<QSystemStorageInfo::StorageState>("storagestate");
    QTest::addColumn<QString>("drivename");

    QTest::newRow("NoDrive") << QSystemStorageInfo::NoDrive
                             << QSystemStorageInfo::UnknownStorageState
                             << "NoDrive";
    QTest::newRow("InternalDrive") << QSystemStorageInfo::InternalDrive
                                      << QSystemStorageInfo::NormalStorageState
                                      << "InternalDrive";
    QTest::newRow("RemovableDrive") << QSystemStorageInfo::RemovableDrive
                                       << QSystemStorageInfo::LowStorageState
                                           << "RemovableDrive";
    QTest::newRow("RemoteDrive") << QSystemStorageInfo::RemoteDrive
                                    << QSystemStorageInfo::VeryLowStorageState
                                       << "RemoteDrive";
    QTest::newRow("CdromDrive") << QSystemStorageInfo::CdromDrive
                                   << QSystemStorageInfo::CriticalStorageState
                                       << "CdromDrive";
    QTest::newRow("InternalFlashDrive") << QSystemStorageInfo::InternalFlashDrive
                                        << QSystemStorageInfo::UnknownStorageState
                                           << "InternalFlashDrive";
    QTest::newRow("RamDrive") << QSystemStorageInfo::RamDrive
                                 << QSystemStorageInfo::UnknownStorageState
                                 << "RamDrive";
    SystemInfoConnection si;
    QSystemStorageInfo sti;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();
    QStringList curDrives = sti.logicalDrives();
    foreach (const QString &name, curDrives)
        st->removeDrive(name);

}

void tst_QSystemStorageInfo::newDrives()
{
    QFETCH(QString, drivename);
    QFETCH(QSystemStorageInfo::DriveType, drivetype);

    QSystemStorageInfo sti;
    QStringList curDrives = sti.logicalDrives();
    SystemInfoConnection si;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();

    st->addDrive(drivename, drivetype, 100, 90,"YYY");
}

void tst_QSystemStorageInfo::tst_storageStateChanged()
{
    QFETCH(QSystemStorageInfo::DriveType, drivetype);
    QFETCH(QString, drivename);

    QSystemStorageInfo sti;
    QStringList curDrives = sti.logicalDrives();
    SystemInfoConnection si;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();
    driveAdded = true;
    dName = drivename;
    st->addDrive(drivename, drivetype, 100, 50,"YYY");
    connect(&sti,SIGNAL(storageStateChanged(const QString &, QSystemStorageInfo::StorageState)),
            this,SLOT(storageStateChanged(const QString &, QSystemStorageInfo::StorageState)));

    sstate = QSystemStorageInfo::CriticalStorageState;
    st->setAvailableSpace(drivename,1);
    QVERIFY(sti.availableDiskSpace(drivename) == 1);
    sstate = QSystemStorageInfo::NormalStorageState;
    st->setAvailableSpace(drivename,99);
    QVERIFY(sti.availableDiskSpace(drivename) == 99);
    sstate = QSystemStorageInfo::NormalStorageState;
    st->setAvailableSpace(drivename,41);
    QVERIFY(sti.availableDiskSpace(drivename) == 41);
    sstate = QSystemStorageInfo::LowStorageState;
    st->setAvailableSpace(drivename,40);
    QVERIFY(sti.availableDiskSpace(drivename) == 40);
    sstate = QSystemStorageInfo::VeryLowStorageState;
    st->setAvailableSpace(drivename,9);
    QVERIFY(sti.availableDiskSpace(drivename) == 9);

    driveAdded = false;
    st->removeDrive(drivename);
}

void tst_QSystemStorageInfo::tst_logicalDriveChanged_data()
{
    tst_storageStateChanged_data();
}

void tst_QSystemStorageInfo::tst_logicalDriveChanged()
{
    QFETCH(QSystemStorageInfo::DriveType, drivetype);
    QFETCH(QString, drivename);

    QSystemStorageInfo sti;
    QStringList curDrives = sti.logicalDrives();
    SystemInfoConnection si;
    QSystemStorageInfoPrivate *st = si.storageInfoPrivate();

    connect(&sti,SIGNAL(logicalDriveChanged(bool,const QString &)),
            this,SLOT(logicalDriveChanged(bool,const QString &)));

    driveAdded = true;
    dName = drivename;
    st->addDrive(drivename, drivetype, 100, 50,"YYY");

    driveAdded = false;
    st->removeDrive(drivename);
}

void tst_QSystemStorageInfo::logicalDriveChanged(bool added,const QString &vol)
{
    QVERIFY(added == driveAdded);
    QVERIFY(vol == dName);
}

void tst_QSystemStorageInfo::storageStateChanged(const QString &vol, QSystemStorageInfo::StorageState state)
{
    QVERIFY(vol == dName);
    QVERIFY(state == sstate);
}

QTEST_MAIN(tst_QSystemStorageInfo)
#include "tst_qsystemstorageinfo.moc"
<|endoftext|>


<commit_before>//
// THIS CONTAINS THE IMPLEMENTATION FOR PUBLISHING ON-BOARD CAMERA IMAGES
// ON CRAZYFLIE USING THE CAMERA PUBLISHER CLASS
//
// COPYRIGHT BELONGS TO THE AUTHOR OF THIS CODE
//
// AUTHOR : LAKSHMAN KUMAR
// AFFILIATION : UNIVERSITY OF MARYLAND, MARYLAND ROBOTICS CENTER
// EMAIL : LKUMAR93@UMD.EDU
// LINKEDIN : WWW.LINKEDIN.COM/IN/LAKSHMANKUMAR1993
//
// THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THE MIT LICENSE
// THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF
// THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
//
// BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO
// BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS
// CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
// CONDITIONS.
//

///////////////////////////////////////////
//
//	LIBRARIES
//
///////////////////////////////////////////

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <string>
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <sensor_msgs/image_encodings.h>
#include <image_transport/image_transport.h>
#include <camera_info_manager/camera_info_manager.h>
#include <deep_learning_crazyflie/camera_parameters.h>

using namespace cv;
using namespace std;

///////////////////////////////////////////
//
//	CLASSES
//
///////////////////////////////////////////
class CameraPublisher
{

  //Declare all the necessary variables
  string camera_position;
  int camera_input_id ;
  VideoCapture cap;
  Mat InputImage;
  image_transport::CameraPublisher camera_pub;
  string prefix = "crazyflie/cameras/";
  string postfix = "/image" ;
  string camera_topic_name ;
  sensor_msgs::CameraInfo* camera_info;
  sensor_msgs::CameraInfoPtr camera_info_ptr;

  public:

  //Use the constructor to initialize variables
  CameraPublisher(string position, int id, image_transport::ImageTransport ImageTransporter, double FL_X ,double PP_X ,double FL_Y, double PP_Y, double* DistArray)
  {
	camera_position = position ;
	camera_input_id = id ;
	camera_topic_name = prefix + camera_position + postfix ;
	camera_pub = ImageTransporter.advertiseCamera(camera_topic_name, 1);
	camera_info = new sensor_msgs::CameraInfo ();
	camera_info_ptr = sensor_msgs::CameraInfoPtr (camera_info);

	camera_info_ptr->header.frame_id = camera_position+"_camera";
	camera_info->header.frame_id = camera_position+"_camera";
	camera_info->width = 640;
	camera_info->height = 480;

	camera_info->K.at(0) = FL_X;
	camera_info->K.at(2) = PP_X;
	camera_info->K.at(4) = FL_Y;
	camera_info->K.at(5) = PP_Y;
	camera_info->K.at(8) = 1;
	camera_info->P.at(0) = camera_info->K.at(0);
	camera_info->P.at(1) = 0;
	camera_info->P.at(2) = camera_info->K.at(2);
	camera_info->P.at(3) = 0;
	camera_info->P.at(4) = 0;
	camera_info->P.at(5) = camera_info->K.at(4);
	camera_info->P.at(6) = camera_info->K.at(5);
	camera_info->P.at(7) = 0;
	camera_info->P.at(8) = 0;
	camera_info->P.at(9) = 0;
	camera_info->P.at(10) = 1;
	camera_info->P.at(11) = 0;
	camera_info->distortion_model = "plumb_bob";

	// Make Rotation Matrix an identity matrix
	camera_info->R.at(0) = (double) 1;
	camera_info->R.at(1) = (double) 0;
	camera_info->R.at(2) = (double) 0;
	camera_info->R.at(3) = (double) 0;
	camera_info->R.at(4) = (double) 1;
	camera_info->R.at(5) = (double) 0;
	camera_info->R.at(6) = (double) 0;
	camera_info->R.at(7) = (double) 0;
	camera_info->R.at(8) = (double) 1;

	for (int i = 0; i < 5; i++)
	    camera_info->D.push_back (DistArray[i]);

  }

  // Initialize the camera
  bool Initialize()
  {
  	cap.open(camera_input_id);

    	if( !cap.isOpened() )
	    {
		ROS_ERROR("Could not initialize camera with id : %d ",camera_input_id);
		return false;
	    }

	return true;

  }

  // Publish the image from the camera to the corresponding topic
  void Publish()
  {
        cap >> InputImage;

        if( InputImage.empty() )
	{
		ROS_INFO("Empty image from camera with id : %d ",camera_input_id);
	}
	else
	{
	    sensor_msgs::ImagePtr msg;

	    msg  = cv_bridge::CvImage(std_msgs::Header(), "bgr8", InputImage).toImageMsg();
	    msg->header.stamp = ros::Time::now() ;
	    msg->header.frame_id = camera_position+"_camera";

	    camera_info->header.stamp = ros::Time::now();

	    camera_pub.publish(msg, camera_info_ptr) ;
	}
  }

};

///////////////////////////////////////////
//
//	MAIN FUNCTION
//
///////////////////////////////////////////

int main( int argc, char** argv )
{
    //Initialize the Crazyflie Camera Publisher Node
    ros::init(argc, argv, "crazyflie_camera_node");
    ros::NodeHandle nh;
    image_transport::ImageTransport it(nh);

    int camera_id;
    nh.param<int>("crazyflie_camera_node/cameras/bottom/id", camera_id, 0);

    //Create a publisher for the bottom facing camera
    CameraPublisher bottom_camera_pub("bottom", camera_id, it, FocalLength_X, FocalLength_Y, PrincipalPoint_X, PrincipalPoint_Y, Distortion);

    //Initialize the camera and check for errors
    bool Initialized = bottom_camera_pub.Initialize();

    //If the camera has been initialized and everything is ok , publish the images from the camera
    while(nh.ok() && Initialized)
    {
	bottom_camera_pub.Publish() ;
    }

    return 0;
}
<commit_msg>Denoise images from the camera<commit_after>//
// THIS CONTAINS THE IMPLEMENTATION FOR PUBLISHING ON-BOARD CAMERA IMAGES
// ON CRAZYFLIE USING THE CAMERA PUBLISHER CLASS
//
// COPYRIGHT BELONGS TO THE AUTHOR OF THIS CODE
//
// AUTHOR : LAKSHMAN KUMAR
// AFFILIATION : UNIVERSITY OF MARYLAND, MARYLAND ROBOTICS CENTER
// EMAIL : LKUMAR93@UMD.EDU
// LINKEDIN : WWW.LINKEDIN.COM/IN/LAKSHMANKUMAR1993
//
// THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THE MIT LICENSE
// THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF
// THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
//
// BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO
// BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS
// CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
// CONDITIONS.
//

///////////////////////////////////////////
//
//	LIBRARIES
//
///////////////////////////////////////////

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <string>
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <sensor_msgs/image_encodings.h>
#include <image_transport/image_transport.h>
#include <camera_info_manager/camera_info_manager.h>
#include <deep_learning_crazyflie/camera_parameters.h>
#include <opencv2/videostab.hpp>

using namespace cv;
using namespace std;

///////////////////////////////////////////
//
//	CLASSES
//
///////////////////////////////////////////
class CameraPublisher
{

  //Declare all the necessary variables
  string camera_position;
  int camera_input_id ;
  VideoCapture cap;
  Mat InputImage;
  Mat UndistortedImage;
  Mat DeNoisedImage;
  image_transport::CameraPublisher camera_pub;
  string prefix = "crazyflie/cameras/";
  string postfix = "/image" ;
  string camera_topic_name ;
  sensor_msgs::CameraInfo* camera_info;
  sensor_msgs::CameraInfoPtr camera_info_ptr;
  cv::Mat_<float> camera_matrix;
  cv::Mat_<float> distortion_params;

  public:

  //Use the constructor to initialize variables
  CameraPublisher(string position, int id, image_transport::ImageTransport ImageTransporter, double FL_X ,double PP_X ,double FL_Y, double PP_Y, double* DistArray)
	:camera_matrix(3,3), distortion_params(1,5)
  {
	camera_position = position ;
	camera_input_id = id ;
	camera_topic_name = prefix + camera_position + postfix ;
	camera_pub = ImageTransporter.advertiseCamera(camera_topic_name, 1);
	camera_info = new sensor_msgs::CameraInfo ();
	camera_info_ptr = sensor_msgs::CameraInfoPtr (camera_info);

	camera_info_ptr->header.frame_id = camera_position+"_camera";
	camera_info->header.frame_id = camera_position+"_camera";
	camera_info->width = 640;
	camera_info->height = 480;

	camera_info->K.at(0) = FL_X;
	camera_info->K.at(2) = PP_X;
	camera_info->K.at(4) = FL_Y;
	camera_info->K.at(5) = PP_Y;
	camera_info->K.at(8) = 1;
	camera_info->P.at(0) = camera_info->K.at(0);
	camera_info->P.at(1) = 0;
	camera_info->P.at(2) = camera_info->K.at(2);
	camera_info->P.at(3) = 0;
	camera_info->P.at(4) = 0;
	camera_info->P.at(5) = camera_info->K.at(4);
	camera_info->P.at(6) = camera_info->K.at(5);
	camera_info->P.at(7) = 0;
	camera_info->P.at(8) = 0;
	camera_info->P.at(9) = 0;
	camera_info->P.at(10) = 1;
	camera_info->P.at(11) = 0;
	camera_info->distortion_model = "plumb_bob";

	// Make Rotation Matrix an identity matrix
	camera_info->R.at(0) = (double) 1;
	camera_info->R.at(1) = (double) 0;
	camera_info->R.at(2) = (double) 0;
	camera_info->R.at(3) = (double) 0;
	camera_info->R.at(4) = (double) 1;
	camera_info->R.at(5) = (double) 0;
	camera_info->R.at(6) = (double) 0;
	camera_info->R.at(7) = (double) 0;
	camera_info->R.at(8) = (double) 1;

	for (int i = 0; i < 5; i++)
	    camera_info->D.push_back (DistArray[i]);

	camera_matrix << FL_X, 0, PP_X, 0, FL_Y, PP_Y, 0, 0, 1 ;
	distortion_params << DistArray[0],DistArray[1],DistArray[2],DistArray[3],DistArray[4];

  }

  // Initialize the camera
  bool Initialize()
  {
  	cap.open(camera_input_id);

    	if( !cap.isOpened() )
	    {
		ROS_ERROR("Could not initialize camera with id : %d ",camera_input_id);
		return false;
	    }

	return true;

  }

  // Publish the image from the camera to the corresponding topic
  void Publish()
  {
        cap >> InputImage;

        if( InputImage.empty() )
	{
		ROS_INFO("Empty image from camera with id : %d ",camera_input_id);
	}
	else
	{
	    sensor_msgs::ImagePtr msg;
	    //undistort(InputImage, UndistortedImage, camera_matrix, distortion_params);
	    GaussianBlur(InputImage,InputImage, Size(5,5),0,0);
	    medianBlur(InputImage,DeNoisedImage,5);
	    //fastNlMeansDenoisingColored(InputImage,DeNoisedImage,3,3,5,11);
	    msg  = cv_bridge::CvImage(std_msgs::Header(), "bgr8", DeNoisedImage).toImageMsg();
	    msg->header.stamp = ros::Time::now() ;
	    msg->header.frame_id = camera_position+"_camera";

	    camera_info->header.stamp = ros::Time::now();

	    camera_pub.publish(msg, camera_info_ptr) ;
	}
  }

};

///////////////////////////////////////////
//
//	MAIN FUNCTION
//
///////////////////////////////////////////

int main( int argc, char** argv )
{
    //Initialize the Crazyflie Camera Publisher Node
    ros::init(argc, argv, "crazyflie_camera_node");
    ros::NodeHandle nh;
    image_transport::ImageTransport it(nh);

    int camera_id;

    try
    {
        camera_id = atoi(argv[1]);
	cout<<"\n Camera ID = "<<camera_id;
    }

    catch (...)
    {
	cout << "Camera ID missing in Command Line";
	exit(0);
     }

    //Create a publisher for the bottom facing camera
    CameraPublisher bottom_camera_pub("bottom", camera_id, it, FocalLength_X, FocalLength_Y, PrincipalPoint_X, PrincipalPoint_Y, Distortion);

    //Initialize the camera and check for errors
    bool Initialized = bottom_camera_pub.Initialize();

    //If the camera has been initialized and everything is ok , publish the images from the camera
    while(nh.ok() && Initialized)
    {
	bottom_camera_pub.Publish() ;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "LikelihoodProfiler.h"

// from STL
#include <set>

// from ROOT
#include "TCanvas.h"
#include "TGraph.h"
#include "TH2D.h"
#include "TAxis.h"

// from RooFit
#include "RooFitResult.h"

// from DooCore
#include <doocore/io/MsgStream.h>
#include <doocore/io/Progress.h>
#include <doocore/lutils/lutils.h>

// from DooFit
#include "doofit/fitter/AbsFitter.h"
#include "doofit/toy/ToyStudyStd/ToyStudyStd.h"

doofit::plotting::profiles::LikelihoodProfiler::LikelihoodProfiler()
: num_samples_(30)
{}

std::vector<double> doofit::plotting::profiles::LikelihoodProfiler::SetSamplePoint(unsigned int step) {
  using namespace doocore::io;

  std::vector<double> sample_vals;
  int i(scan_vars_.size() - 1);

  //sdebug << "step = " << step << endmsg;

  for (auto var : scan_vars_) {
    int step_this = step/std::pow(num_samples_,i);
    step -= step_this*std::pow(num_samples_,i);
    --i;

    double val(start_vals_[var->GetName()] + 3*var->getError()*(-1.0 + 2.0/static_cast<double>(num_samples_)*step_this));
    var->setVal(val);
    var->setConstant(true);

    sample_vals.push_back(val);
    //sdebug << "  " << var->GetName() << " = " << step_this << " @ " << var->getVal() << endmsg;
  }

  return sample_vals;
}

void doofit::plotting::profiles::LikelihoodProfiler::Scan() {
  using namespace doocore::io;

  //RooRealVar* parameter_scan = scan_vars_.front();

  //num_samples_                   = 12;
  unsigned int num_dimensions    = scan_vars_.size();
  unsigned int num_total_samples = std::pow(num_samples_,num_dimensions);
  unsigned int step              = 0;

  // double value_start = parameter_scan->getVal();
  // double value_scan  = value_start - 5.0*parameter_scan->getError();

  for (auto var : scan_vars_) {
    start_vals_[var->GetName()] = var->getVal();
  }

  Progress p("Sampling likelihood", num_total_samples);
  while (step < num_total_samples) {
    //parameter_scan->setVal(value_scan);
    //parameter_scan->setConstant(true);

    //sinfo << "PROFILE: " << value_scan << endmsg;

    std::vector<double> sample_vals(SetSamplePoint(step));

    fitter_->set_shutup(true);
    fitter_->Fit();

    fit_results_.push_back(fitter_->fit_result());

    //sinfo << fitter_->NegativeLogLikelihood() << endmsg;
    //value_scan += 1*parameter_scan->getError();
    ++step;
    ++p;
  }
  p.Finish();
}

void doofit::plotting::profiles::LikelihoodProfiler::ReadFitResults(doofit::toy::ToyStudyStd& toy_study) {
  using namespace doofit::toy;

  FitResultContainer fit_result_container(toy_study.GetFitResult());
  const RooFitResult* fit_result(std::get<0>(fit_result_container));

  while (fit_result != nullptr) {
    fit_results_.push_back(fit_result);

    fit_result_container = toy_study.GetFitResult();
    fit_result = std::get<0>(fit_result_container);
  }
}

bool doofit::plotting::profiles::LikelihoodProfiler::FitResultOkay(const RooFitResult& fit_result) const {
  using namespace doocore::io;
  if (fit_result.covQual() < 2) {
    return false;
  } else if (fit_result.statusCodeHistory(0) < 0) {
    return false;
  } else {
    return true;
  }
}

void doofit::plotting::profiles::LikelihoodProfiler::PlotHandler(const std::string& plot_path) {
  using namespace doocore::io;

  std::map<std::string, std::vector<double>> val_scan;
  std::vector<double> val_nll;

  doocore::lutils::setStyle("2d");

  for (auto var : scan_vars_) {
    RooRealVar* var_fixed = dynamic_cast<RooRealVar*>(fit_results_.front()->constPars().find(var->GetName()));
    scan_vars_titles_.push_back(var_fixed->GetTitle());
  }

  double min_nll(0.0);
  for (auto result : fit_results_) {
    if (FitResultOkay(*result)) {
      for (auto var : scan_vars_) {
        RooRealVar* var_fixed = dynamic_cast<RooRealVar*>(result->constPars().find(var->GetName()));

        if (var_fixed == nullptr) {
          serr << "Error in LikelihoodProfiler::PlotHandler(): Cannot find fixed parameter "
               << var->GetName() << " in fit result!" << endmsg;
          throw;
        }

        val_scan[var->GetName()].push_back(var_fixed->getVal());
      }

      if (min_nll == 0.0 || min_nll > result->minNll()) {
        min_nll = result->minNll();
      }
      val_nll.push_back(result->minNll());
    }
  }

  for (auto &nll : val_nll) {
    if (nll != 0.0) {
      nll -= min_nll;
    }
  }

  TCanvas c("c", "c", 800, 600);

  if (val_scan.size() == 1) {
    const std::vector<double>& val_x = val_scan.begin()->second;

    TGraph graph(val_nll.size(), &val_x[0], &val_nll[0]);
    graph.Draw("AP");
    graph.GetXaxis()->SetTitle(scan_vars_titles_.at(0).c_str());
    graph.GetYaxis()->SetTitle("NLL");
    //c.SaveAs("profile.pdf");
    doocore::lutils::printPlot(&c, "profile", plot_path);
  } else if (val_scan.size() == 2) {
    const std::vector<double>& val_x = val_scan.begin()->second;
    const std::vector<double>& val_y = val_scan.rbegin()->second;

    auto minmax_x = std::minmax_element(val_x.begin(), val_x.end());
    auto minmax_y = std::minmax_element(val_y.begin(), val_y.end());

    std::set<double> distinct_x, distinct_y;
    for (auto x : val_x) {
      distinct_x.insert(x);
    }
    for (auto y : val_y) {
      distinct_y.insert(y);
    }

    double min_nll(0.0), max_nll(0.0);
    for (auto nll : val_nll) {
      if (nll != 0 && (min_nll == 0.0 || nll < min_nll)) {
        min_nll = nll;
      }
      if (nll != 0 && (max_nll == 0.0 || nll > max_nll)) {
        max_nll = nll;
      }
    }

    double min_x = *minmax_x.first  - (*minmax_x.second-*minmax_x.first)/(distinct_x.size()-1)*0.5;
    double max_x = *minmax_x.second + (*minmax_x.second-*minmax_x.first)/(distinct_x.size()-1)*0.5;
    double min_y = *minmax_y.first  - (*minmax_y.second-*minmax_y.first)/(distinct_y.size()-1)*0.5;
    double max_y = *minmax_y.second + (*minmax_y.second-*minmax_y.first)/(distinct_y.size()-1)*0.5;

    TH2D histogram("histogram", "histogram", distinct_x.size(), min_x, max_x, distinct_y.size(), min_y, max_y);

    // sdebug << "histogram x: " << *minmax_x.first << " - " <<  *minmax_x.second << endmsg;
    // sdebug << "histogram y: " << *minmax_y.first << " - " <<  *minmax_y.second << endmsg;

    for (int i=0; i<val_nll.size(); ++i) {
      // sdebug << val_x.at(i) << ", " << val_y.at(i) << " - " << val_nll.at(i) << endmsg;
      // sdebug << "Bin: " << histogram.FindBin(val_x.at(i), val_y.at(i)) << endmsg;

      int nbin_x, nbin_y, nbin_z;
      histogram.GetBinXYZ(histogram.FindBin(val_x.at(i), val_y.at(i)), nbin_x, nbin_y, nbin_z);

      // sdebug << "Bin center x: " << histogram.GetXaxis()->GetBinCenter(nbin_x) << endmsg;
      // sdebug << "Bin center y: " << histogram.GetYaxis()->GetBinCenter(nbin_y) << endmsg;

      histogram.SetBinContent(histogram.FindBin(val_x.at(i), val_y.at(i)), val_nll.at(i));
    }

    histogram.Draw("COLZ");
    histogram.GetZaxis()->SetRangeUser(min_nll, max_nll);
    histogram.SetXTitle(scan_vars_titles_.at(0).c_str());
    histogram.SetYTitle(scan_vars_titles_.at(1).c_str());
    histogram.SetZTitle("#DeltaLL");

    //c.SaveAs("profile.pdf");
    doocore::lutils::printPlot(&c, "profile", plot_path);
  } else {
    serr << "Error in LikelihoodProfiler::PlotHandler(): Cannot (yet) plot 3D likelihood." << endmsg;
    throw;
  }

}

<commit_msg>LikelihoodProfiler: plotting cosmetics<commit_after>#include "LikelihoodProfiler.h"

// from STL
#include <set>

// from ROOT
#include "TCanvas.h"
#include "TGraph.h"
#include "TH2D.h"
#include "TAxis.h"
#include "TStyle.h"

// from RooFit
#include "RooFitResult.h"

// from DooCore
#include <doocore/io/MsgStream.h>
#include <doocore/io/Progress.h>
#include <doocore/lutils/lutils.h>

// from DooFit
#include "doofit/fitter/AbsFitter.h"
#include "doofit/toy/ToyStudyStd/ToyStudyStd.h"

doofit::plotting::profiles::LikelihoodProfiler::LikelihoodProfiler()
: num_samples_(30)
{}

std::vector<double> doofit::plotting::profiles::LikelihoodProfiler::SetSamplePoint(unsigned int step) {
  using namespace doocore::io;

  std::vector<double> sample_vals;
  int i(scan_vars_.size() - 1);

  //sdebug << "step = " << step << endmsg;

  for (auto var : scan_vars_) {
    int step_this = step/std::pow(num_samples_,i);
    step -= step_this*std::pow(num_samples_,i);
    --i;

    double val(start_vals_[var->GetName()] + 3*var->getError()*(-1.0 + 2.0/static_cast<double>(num_samples_)*step_this));
    var->setVal(val);
    var->setConstant(true);

    sample_vals.push_back(val);
    //sdebug << "  " << var->GetName() << " = " << step_this << " @ " << var->getVal() << endmsg;
  }

  return sample_vals;
}

void doofit::plotting::profiles::LikelihoodProfiler::Scan() {
  using namespace doocore::io;

  //RooRealVar* parameter_scan = scan_vars_.front();

  //num_samples_                   = 12;
  unsigned int num_dimensions    = scan_vars_.size();
  unsigned int num_total_samples = std::pow(num_samples_,num_dimensions);
  unsigned int step              = 0;

  // double value_start = parameter_scan->getVal();
  // double value_scan  = value_start - 5.0*parameter_scan->getError();

  for (auto var : scan_vars_) {
    start_vals_[var->GetName()] = var->getVal();
  }

  Progress p("Sampling likelihood", num_total_samples);
  while (step < num_total_samples) {
    //parameter_scan->setVal(value_scan);
    //parameter_scan->setConstant(true);

    //sinfo << "PROFILE: " << value_scan << endmsg;

    std::vector<double> sample_vals(SetSamplePoint(step));

    fitter_->set_shutup(true);
    fitter_->Fit();

    fit_results_.push_back(fitter_->fit_result());

    //sinfo << fitter_->NegativeLogLikelihood() << endmsg;
    //value_scan += 1*parameter_scan->getError();
    ++step;
    ++p;
  }
  p.Finish();
}

void doofit::plotting::profiles::LikelihoodProfiler::ReadFitResults(doofit::toy::ToyStudyStd& toy_study) {
  using namespace doofit::toy;

  FitResultContainer fit_result_container(toy_study.GetFitResult());
  const RooFitResult* fit_result(std::get<0>(fit_result_container));

  while (fit_result != nullptr) {
    fit_results_.push_back(fit_result);

    fit_result_container = toy_study.GetFitResult();
    fit_result = std::get<0>(fit_result_container);
  }
}

bool doofit::plotting::profiles::LikelihoodProfiler::FitResultOkay(const RooFitResult& fit_result) const {
  using namespace doocore::io;
  if (fit_result.covQual() < 2) {
    return false;
  } else if (fit_result.statusCodeHistory(0) < 0) {
    return false;
  } else {
    return true;
  }
}

void doofit::plotting::profiles::LikelihoodProfiler::PlotHandler(const std::string& plot_path) {
  using namespace doocore::io;

  std::map<std::string, std::vector<double>> val_scan;
  std::vector<double> val_nll;

  if (val_scan.size() == 1) {
    doocore::lutils::setStyle();
  } else if (val_scan.size() == 2) {
    doocore::lutils::setStyle("2d");
    gStyle->SetNumberContours(999);
    gStyle->SetPadRightMargin(0.16);
    gStyle->SetTitleOffset(0.75, "z");
  }

  for (auto var : scan_vars_) {
    RooRealVar* var_fixed = dynamic_cast<RooRealVar*>(fit_results_.front()->constPars().find(var->GetName()));
    scan_vars_titles_.push_back(var_fixed->GetTitle());
  }

  double min_nll(0.0);
  for (auto result : fit_results_) {
    if (FitResultOkay(*result)) {
      for (auto var : scan_vars_) {
        RooRealVar* var_fixed = dynamic_cast<RooRealVar*>(result->constPars().find(var->GetName()));

        if (var_fixed == nullptr) {
          serr << "Error in LikelihoodProfiler::PlotHandler(): Cannot find fixed parameter "
               << var->GetName() << " in fit result!" << endmsg;
          throw;
        }

        val_scan[var->GetName()].push_back(var_fixed->getVal());
      }

      if (min_nll == 0.0 || min_nll > result->minNll()) {
        min_nll = result->minNll();
      }
      val_nll.push_back(result->minNll());
    }
  }

  for (auto &nll : val_nll) {
    if (nll != 0.0) {
      nll -= min_nll;
    }
  }

  TCanvas c("c", "c", 800, 600);

  if (val_scan.size() == 1) {
    const std::vector<double>& val_x = val_scan.begin()->second;

    TGraph graph(val_nll.size(), &val_x[0], &val_nll[0]);
    graph.Draw("AP");
    graph.GetXaxis()->SetTitle(scan_vars_titles_.at(0).c_str());
    graph.GetYaxis()->SetTitle("#DeltaLL");
    //c.SaveAs("profile.pdf");
    doocore::lutils::printPlot(&c, "profile", plot_path);
  } else if (val_scan.size() == 2) {
    const std::vector<double>& val_x = val_scan.begin()->second;
    const std::vector<double>& val_y = val_scan.rbegin()->second;

    auto minmax_x = std::minmax_element(val_x.begin(), val_x.end());
    auto minmax_y = std::minmax_element(val_y.begin(), val_y.end());

    std::set<double> distinct_x, distinct_y;
    for (auto x : val_x) {
      distinct_x.insert(x);
    }
    for (auto y : val_y) {
      distinct_y.insert(y);
    }

    double min_nll(0.0), max_nll(0.0);
    for (auto nll : val_nll) {
      if (nll != 0 && (min_nll == 0.0 || nll < min_nll)) {
        min_nll = nll;
      }
      if (nll != 0 && (max_nll == 0.0 || nll > max_nll)) {
        max_nll = nll;
      }
    }

    double min_x = *minmax_x.first  - (*minmax_x.second-*minmax_x.first)/(distinct_x.size()-1)*0.5;
    double max_x = *minmax_x.second + (*minmax_x.second-*minmax_x.first)/(distinct_x.size()-1)*0.5;
    double min_y = *minmax_y.first  - (*minmax_y.second-*minmax_y.first)/(distinct_y.size()-1)*0.5;
    double max_y = *minmax_y.second + (*minmax_y.second-*minmax_y.first)/(distinct_y.size()-1)*0.5;

    TH2D histogram("histogram", "histogram", distinct_x.size(), min_x, max_x, distinct_y.size(), min_y, max_y);

    // sdebug << "histogram x: " << *minmax_x.first << " - " <<  *minmax_x.second << endmsg;
    // sdebug << "histogram y: " << *minmax_y.first << " - " <<  *minmax_y.second << endmsg;

    for (int i=0; i<val_nll.size(); ++i) {
      // sdebug << val_x.at(i) << ", " << val_y.at(i) << " - " << val_nll.at(i) << endmsg;
      // sdebug << "Bin: " << histogram.FindBin(val_x.at(i), val_y.at(i)) << endmsg;

      int nbin_x, nbin_y, nbin_z;
      histogram.GetBinXYZ(histogram.FindBin(val_x.at(i), val_y.at(i)), nbin_x, nbin_y, nbin_z);

      // sdebug << "Bin center x: " << histogram.GetXaxis()->GetBinCenter(nbin_x) << endmsg;
      // sdebug << "Bin center y: " << histogram.GetYaxis()->GetBinCenter(nbin_y) << endmsg;

      histogram.SetBinContent(histogram.FindBin(val_x.at(i), val_y.at(i)), val_nll.at(i));
    }

    histogram.Draw("COLZ");
    histogram.GetZaxis()->SetRangeUser(min_nll, max_nll);
    histogram.SetXTitle(scan_vars_titles_.at(0).c_str());
    histogram.SetYTitle(scan_vars_titles_.at(1).c_str());
    histogram.SetZTitle("#DeltaLL");

    //c.SaveAs("profile.pdf");
    doocore::lutils::printPlot(&c, "profile", plot_path);
  } else {
    serr << "Error in LikelihoodProfiler::PlotHandler(): Cannot (yet) plot 3D likelihood." << endmsg;
    throw;
  }

}

<|endoftext|>