<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_UTILS_LOG_HPP
#define OUZEL_UTILS_LOG_HPP

#include <atomic>
#include <cstring>
#include <mutex>
#include <string>
#include <type_traits>
#include "../math/Matrix.hpp"
#include "../math/Quaternion.hpp"
#include "../math/Size.hpp"
#include "../math/Vector.hpp"
#include "../storage/Path.hpp"
#include "../thread/Thread.hpp"
#include "Utils.hpp"

namespace ouzel
{
    class Logger;

    template<typename T, typename = void>
    struct isContainer: std::false_type {};

    template<typename T>
    struct isContainer<T,
        std::void_t<decltype(std::declval<T>().begin()),
            decltype(std::declval<T>().end())
        >>: std::true_type {};

    template <typename T> constexpr bool isContainerV = isContainer<T>::value;

    class Log final
    {
    public:
        enum class Level
        {
            off,
            error,
            warning,
            info,
            all
        };

        explicit Log(const Logger& initLogger, Level initLevel = Level::info):
            logger(initLogger), level(initLevel)
        {
        }

        Log(const Log& other):
            logger(other.logger),
            level(other.level),
            s(other.s)
        {
        }

        Log(Log&& other) noexcept:
            logger(other.logger),
            level(other.level),
            s(std::move(other.s))
        {
            other.level = Level::info;
        }

        Log& operator=(const Log& other)
        {
            if (&other == this) return *this;

            level = other.level;
            s = other.s;

            return *this;
        }

        Log& operator=(Log&& other) noexcept
        {
            if (&other == this) return *this;

            level = other.level;
            other.level = Level::info;
            s = std::move(other.s);

            return *this;
        }

        ~Log();

        Log& operator<<(const bool val)
        {
            s += val ? "true" : "false";
            return *this;
        }

        Log& operator<<(char val)
        {
            s += val;
            return *this;
        }

        Log& operator<<(const std::uint8_t val)
        {
            constexpr char digits[] = "0123456789abcdef";
            s.push_back(digits[(val >> 4) & 0x0F]);
            s.push_back(digits[(val >> 0) & 0x0F]);
            return *this;
        }

        template <typename T, std::enable_if_t<std::is_arithmetic_v<T> &&
            !std::is_same_v<T, bool> &&
            !std::is_same_v<T, std::uint8_t>>* = nullptr>
        Log& operator<<(const T val)
        {
            s += std::to_string(val);
            return *this;
        }

        Log& operator<<(const std::string& val)
        {
            s += val;
            return *this;
        }

        Log& operator<<(const char* val)
        {
            s += val;
            return *this;
        }

        template <typename T, std::enable_if_t<!std::is_same_v<T, char>>* = nullptr>
        Log& operator<<(const T* val)
        {
            constexpr char digits[] = "0123456789abcdef";

            const auto ptrValue = bitCast<std::uintptr_t>(val);

            for (std::size_t i = 0; i < sizeof(val) * 2; ++i)
                s.push_back(digits[(ptrValue >> (sizeof(ptrValue) * 2 - i - 1) * 4) & 0x0F]);

            return *this;
        }

        Log& operator<<(const storage::Path& val)
        {
            s += val;
            return *this;
        }

        template <typename T, std::enable_if_t<isContainerV<T> || std::is_array_v<T>>* = nullptr>
        Log& operator<<(const T& val)
        {
            bool first = true;
            for (const auto& i : val)
            {
                if (!first) s += ", ";
                first = false;
                operator<<(i);
            }

            return *this;
        }

        template <std::size_t N, std::size_t M, class T>
        Log& operator<<(const Matrix<N, M, T>& val)
        {
            bool first = true;

            for (const T c : val.m)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }

            return *this;
        }

        template <typename T>
        Log& operator<<(const Quaternion<T>& val)
        {
            s += std::to_string(val.v[0]) + "," + std::to_string(val.v[1]) + "," +
                std::to_string(val.v[2]) + "," + std::to_string(val.v[3]);
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Size<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Vector<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

    private:
        const Logger& logger;
        Level level = Level::info;
        std::string s;
    };

    class Logger final
    {
    public:
        explicit Logger(Log::Level initThreshold = Log::Level::all):
            threshold(initThreshold)
        {
        }

        Logger(const Logger&) = delete;
        Logger& operator=(const Logger&) = delete;
        Logger(Logger&&) = delete;
        Logger& operator=(Logger&&) = delete;

        Log log(const Log::Level level = Log::Level::info) const
        {
            return Log(*this, level);
        }

        void log(const std::string& str, const Log::Level level = Log::Level::info) const
        {
            if (level <= threshold)
            {
#if defined(__EMSCRIPTEN__)
                std::scoped_lock lock(logMutex);
#endif
                logString(str, level);
            }
        }

    private:
        static void logString(const std::string& str, const Log::Level level = Log::Level::info);

#ifdef DEBUG
        std::atomic<Log::Level> threshold{Log::Level::all};
#else
        std::atomic<Log::Level> threshold{Log::Level::info};
#endif

#if !defined(__EMSCRIPTEN__)
        mutable std::mutex logMutex;
#endif
    };

    inline Log::~Log()
    {
        if (!s.empty())
            logger.log(s, level);
    }

    inline Logger logger;
}

#endif // OUZEL_UTILS_LOG_HPP
<commit_msg>Fix the condition check<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_UTILS_LOG_HPP
#define OUZEL_UTILS_LOG_HPP

#include <atomic>
#include <cstring>
#include <mutex>
#include <string>
#include <type_traits>
#include "../math/Matrix.hpp"
#include "../math/Quaternion.hpp"
#include "../math/Size.hpp"
#include "../math/Vector.hpp"
#include "../storage/Path.hpp"
#include "../thread/Thread.hpp"
#include "Utils.hpp"

namespace ouzel
{
    class Logger;

    template<typename T, typename = void>
    struct isContainer: std::false_type {};

    template<typename T>
    struct isContainer<T,
        std::void_t<decltype(std::declval<T>().begin()),
            decltype(std::declval<T>().end())
        >>: std::true_type {};

    template <typename T> constexpr bool isContainerV = isContainer<T>::value;

    class Log final
    {
    public:
        enum class Level
        {
            off,
            error,
            warning,
            info,
            all
        };

        explicit Log(const Logger& initLogger, Level initLevel = Level::info):
            logger(initLogger), level(initLevel)
        {
        }

        Log(const Log& other):
            logger(other.logger),
            level(other.level),
            s(other.s)
        {
        }

        Log(Log&& other) noexcept:
            logger(other.logger),
            level(other.level),
            s(std::move(other.s))
        {
            other.level = Level::info;
        }

        Log& operator=(const Log& other)
        {
            if (&other == this) return *this;

            level = other.level;
            s = other.s;

            return *this;
        }

        Log& operator=(Log&& other) noexcept
        {
            if (&other == this) return *this;

            level = other.level;
            other.level = Level::info;
            s = std::move(other.s);

            return *this;
        }

        ~Log();

        Log& operator<<(const bool val)
        {
            s += val ? "true" : "false";
            return *this;
        }

        Log& operator<<(char val)
        {
            s += val;
            return *this;
        }

        Log& operator<<(const std::uint8_t val)
        {
            constexpr char digits[] = "0123456789abcdef";
            s.push_back(digits[(val >> 4) & 0x0F]);
            s.push_back(digits[(val >> 0) & 0x0F]);
            return *this;
        }

        template <typename T, std::enable_if_t<std::is_arithmetic_v<T> &&
            !std::is_same_v<T, bool> &&
            !std::is_same_v<T, std::uint8_t>>* = nullptr>
        Log& operator<<(const T val)
        {
            s += std::to_string(val);
            return *this;
        }

        Log& operator<<(const std::string& val)
        {
            s += val;
            return *this;
        }

        Log& operator<<(const char* val)
        {
            s += val;
            return *this;
        }

        template <typename T, std::enable_if_t<!std::is_same_v<T, char>>* = nullptr>
        Log& operator<<(const T* val)
        {
            constexpr char digits[] = "0123456789abcdef";

            const auto ptrValue = bitCast<std::uintptr_t>(val);

            for (std::size_t i = 0; i < sizeof(val) * 2; ++i)
                s.push_back(digits[(ptrValue >> (sizeof(ptrValue) * 2 - i - 1) * 4) & 0x0F]);

            return *this;
        }

        Log& operator<<(const storage::Path& val)
        {
            s += val;
            return *this;
        }

        template <typename T, std::enable_if_t<isContainerV<T> || std::is_array_v<T>>* = nullptr>
        Log& operator<<(const T& val)
        {
            bool first = true;
            for (const auto& i : val)
            {
                if (!first) s += ", ";
                first = false;
                operator<<(i);
            }

            return *this;
        }

        template <std::size_t N, std::size_t M, class T>
        Log& operator<<(const Matrix<N, M, T>& val)
        {
            bool first = true;

            for (const T c : val.m)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }

            return *this;
        }

        template <typename T>
        Log& operator<<(const Quaternion<T>& val)
        {
            s += std::to_string(val.v[0]) + "," + std::to_string(val.v[1]) + "," +
                std::to_string(val.v[2]) + "," + std::to_string(val.v[3]);
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Size<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Vector<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

    private:
        const Logger& logger;
        Level level = Level::info;
        std::string s;
    };

    class Logger final
    {
    public:
        explicit Logger(Log::Level initThreshold = Log::Level::all):
            threshold(initThreshold)
        {
        }

        Logger(const Logger&) = delete;
        Logger& operator=(const Logger&) = delete;
        Logger(Logger&&) = delete;
        Logger& operator=(Logger&&) = delete;

        Log log(const Log::Level level = Log::Level::info) const
        {
            return Log(*this, level);
        }

        void log(const std::string& str, const Log::Level level = Log::Level::info) const
        {
            if (level <= threshold)
            {
#if !defined(__EMSCRIPTEN__)
                std::scoped_lock lock(logMutex);
#endif
                logString(str, level);
            }
        }

    private:
        static void logString(const std::string& str, const Log::Level level = Log::Level::info);

#ifdef DEBUG
        std::atomic<Log::Level> threshold{Log::Level::all};
#else
        std::atomic<Log::Level> threshold{Log::Level::info};
#endif

#if !defined(__EMSCRIPTEN__)
        mutable std::mutex logMutex;
#endif
    };

    inline Log::~Log()
    {
        if (!s.empty())
            logger.log(s, level);
    }

    inline Logger logger;
}

#endif // OUZEL_UTILS_LOG_HPP
<|endoftext|>


<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_UTILS_LOG_HPP
#define OUZEL_UTILS_LOG_HPP

#include <atomic>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <type_traits>
#include "../math/Matrix.hpp"
#include "../math/Quaternion.hpp"
#include "../math/Size.hpp"
#include "../math/Vector.hpp"
#include "../storage/Path.hpp"
#include "Thread.hpp"
#include "Utils.hpp"

namespace ouzel
{
    class Logger;

    class Log final
    {
    public:
        enum class Level
        {
            off,
            error,
            warning,
            info,
            all
        };

        explicit Log(const Logger& initLogger, Level initLevel = Level::info):
            logger(initLogger), level(initLevel)
        {
        }

        Log(const Log& other):
            logger(other.logger),
            level(other.level),
            s(other.s)
        {
        }

        Log(Log&& other) noexcept:
            logger(other.logger),
            level(other.level),
            s(std::move(other.s))
        {
            other.level = Level::info;
        }

        Log& operator=(const Log& other)
        {
            if (&other == this) return *this;

            level = other.level;
            s = other.s;

            return *this;
        }

        Log& operator=(Log&& other) noexcept
        {
            if (&other == this) return *this;

            level = other.level;
            other.level = Level::info;
            s = std::move(other.s);

            return *this;
        }

        ~Log();

        Log& operator<<(const bool val)
        {
            s += val ? "true" : "false";
            return *this;
        }

        Log& operator<<(const uint8_t val)
        {
            constexpr char digits[] = "0123456789abcdef";

            for (std::uint32_t p = 0; p < 2; ++p)
                s.push_back(digits[(val >> (4 - p * 4)) & 0x0F]);

            return *this;
        }

        template <typename T, typename std::enable_if_t<std::is_arithmetic_v<T> &&
            !std::is_same_v<T, bool> &&
            !std::is_same_v<T, std::uint8_t>>* = nullptr>
        Log& operator<<(const T val)
        {
            s += std::to_string(val);
            return *this;
        }

        Log& operator<<(const std::string& val)
        {
            s += val;
            return *this;
        }

        Log& operator<<(const char* val)
        {
            s += val;
            return *this;
        }

        template <typename T, typename std::enable_if_t<!std::is_same_v<T, char>>* = nullptr>
        Log& operator<<(const T* val)
        {
            constexpr char digits[] = "0123456789abcdef";

            const auto ptrValue = bitCast<std::uintptr_t>(val);

            for (std::size_t i = 0; i < sizeof(val) * 2; ++i)
                s.push_back(digits[(ptrValue >> (sizeof(ptrValue) * 2 - i - 1) * 4) & 0x0F]);

            return *this;
        }

        Log& operator<<(const storage::Path& val)
        {
            s += val;
            return *this;
        }

        template<typename T, typename = void>
        struct isContainer: std::false_type {};

        template<typename ...> using toVoid = void;

        template<typename T>
        struct isContainer<T,
            toVoid<decltype(begin(std::declval<T&>())),
                decltype(end(std::declval<T&>()))
            >>: std::true_type {};

        template <typename T, typename std::enable_if_t<isContainer<T>::value>* = nullptr>
        Log& operator<<(const T& val)
        {
            auto beginIterator = begin(val);
            auto endIterator = end(val);

            for (auto i = beginIterator; i != endIterator; ++i)
            {
                if (i != beginIterator) s += ", ";
                operator<<(*i);
            }

            return *this;
        }

        template <std::size_t N, std::size_t M, class T>
        Log& operator<<(const Matrix<N, M, T>& val)
        {
            bool first = true;

            for (const T c : val.m)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }

            return *this;
        }

        template <typename T>
        Log& operator<<(const Quaternion<T>& val)
        {
            s += std::to_string(val.v[0]) + "," + std::to_string(val.v[1]) + "," +
                std::to_string(val.v[2]) + "," + std::to_string(val.v[3]);
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Size<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Vector<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

    private:
        const Logger& logger;
        Level level = Level::info;
        std::string s;
    };

    class Logger final
    {
    public:
        explicit Logger(Log::Level initThreshold = Log::Level::all):
            threshold(initThreshold)
        {
#if !defined(__EMSCRIPTEN__)
            logThread = Thread(&Logger::logLoop, this);
#endif
        }

        Logger(const Logger&) = delete;
        Logger& operator=(const Logger&) = delete;
        Logger(Logger&&) = delete;
        Logger& operator=(Logger&&) = delete;

        ~Logger()
        {
#if !defined(__EMSCRIPTEN__)
            std::unique_lock lock(queueMutex);
            commandQueue.push(Command(Command::Type::quit));
            lock.unlock();
            queueCondition.notify_all();
#endif
        }

        Log log(const Log::Level level = Log::Level::info) const
        {
            return Log(*this, level);
        }

        void log(const std::string& str, const Log::Level level = Log::Level::info) const
        {
            if (level <= threshold)
            {
#if defined(__EMSCRIPTEN__)
                logString(str, level);
#else
                std::unique_lock lock(queueMutex);
                commandQueue.push(Command(Command::Type::logString, level, str));
                lock.unlock();
                queueCondition.notify_all();
#endif
            }
        }

    private:
        static void logString(const std::string& str, const Log::Level level = Log::Level::info);

#ifdef DEBUG
        std::atomic<Log::Level> threshold{Log::Level::all};
#else
        std::atomic<Log::Level> threshold{Log::Level::info};
#endif

#if !defined(__EMSCRIPTEN__)
        class Command final
        {
        public:
            enum class Type
            {
                logString,
                quit
            };

            explicit Command(const Type initType):
                type(initType)
            {
            }

            Command(const Type initType, const Log::Level initLevel,
                    const std::string& initString):
                type(initType),
                level(initLevel),
                str(initString)
            {
            }

            Type type;
            Log::Level level = Log::Level::all;
            std::string str;
        };

        void logLoop()
        {
            for (;;)
            {
                std::unique_lock lock(queueMutex);
                while (commandQueue.empty()) queueCondition.wait(lock);
                auto command = std::move(commandQueue.front());
                commandQueue.pop();
                lock.unlock();

                if (command.type == Command::Type::logString)
                    logString(command.str, command.level);
                else if (command.type == Command::Type::quit)
                    break;
            }
        }

        mutable std::condition_variable queueCondition;
        mutable std::mutex queueMutex;
        mutable std::queue<Command> commandQueue;
        Thread logThread;
#endif
    };
}

#endif // OUZEL_UTILS_LOG_HPP
<commit_msg>Use std::void_t instead of toVoid<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_UTILS_LOG_HPP
#define OUZEL_UTILS_LOG_HPP

#include <atomic>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <type_traits>
#include "../math/Matrix.hpp"
#include "../math/Quaternion.hpp"
#include "../math/Size.hpp"
#include "../math/Vector.hpp"
#include "../storage/Path.hpp"
#include "Thread.hpp"
#include "Utils.hpp"

namespace ouzel
{
    class Logger;

    class Log final
    {
    public:
        enum class Level
        {
            off,
            error,
            warning,
            info,
            all
        };

        explicit Log(const Logger& initLogger, Level initLevel = Level::info):
            logger(initLogger), level(initLevel)
        {
        }

        Log(const Log& other):
            logger(other.logger),
            level(other.level),
            s(other.s)
        {
        }

        Log(Log&& other) noexcept:
            logger(other.logger),
            level(other.level),
            s(std::move(other.s))
        {
            other.level = Level::info;
        }

        Log& operator=(const Log& other)
        {
            if (&other == this) return *this;

            level = other.level;
            s = other.s;

            return *this;
        }

        Log& operator=(Log&& other) noexcept
        {
            if (&other == this) return *this;

            level = other.level;
            other.level = Level::info;
            s = std::move(other.s);

            return *this;
        }

        ~Log();

        Log& operator<<(const bool val)
        {
            s += val ? "true" : "false";
            return *this;
        }

        Log& operator<<(const uint8_t val)
        {
            constexpr char digits[] = "0123456789abcdef";

            for (std::uint32_t p = 0; p < 2; ++p)
                s.push_back(digits[(val >> (4 - p * 4)) & 0x0F]);

            return *this;
        }

        template <typename T, typename std::enable_if_t<std::is_arithmetic_v<T> &&
            !std::is_same_v<T, bool> &&
            !std::is_same_v<T, std::uint8_t>>* = nullptr>
        Log& operator<<(const T val)
        {
            s += std::to_string(val);
            return *this;
        }

        Log& operator<<(const std::string& val)
        {
            s += val;
            return *this;
        }

        Log& operator<<(const char* val)
        {
            s += val;
            return *this;
        }

        template <typename T, typename std::enable_if_t<!std::is_same_v<T, char>>* = nullptr>
        Log& operator<<(const T* val)
        {
            constexpr char digits[] = "0123456789abcdef";

            const auto ptrValue = bitCast<std::uintptr_t>(val);

            for (std::size_t i = 0; i < sizeof(val) * 2; ++i)
                s.push_back(digits[(ptrValue >> (sizeof(ptrValue) * 2 - i - 1) * 4) & 0x0F]);

            return *this;
        }

        Log& operator<<(const storage::Path& val)
        {
            s += val;
            return *this;
        }

        template<typename T, typename = void>
        struct isContainer: std::false_type {};

        template<typename T>
        struct isContainer<T,
            std::void_t<decltype(begin(std::declval<T&>())),
                decltype(end(std::declval<T&>()))
            >>: std::true_type {};

        template <typename T, typename std::enable_if_t<isContainer<T>::value>* = nullptr>
        Log& operator<<(const T& val)
        {
            auto beginIterator = begin(val);
            auto endIterator = end(val);

            for (auto i = beginIterator; i != endIterator; ++i)
            {
                if (i != beginIterator) s += ", ";
                operator<<(*i);
            }

            return *this;
        }

        template <std::size_t N, std::size_t M, class T>
        Log& operator<<(const Matrix<N, M, T>& val)
        {
            bool first = true;

            for (const T c : val.m)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }

            return *this;
        }

        template <typename T>
        Log& operator<<(const Quaternion<T>& val)
        {
            s += std::to_string(val.v[0]) + "," + std::to_string(val.v[1]) + "," +
                std::to_string(val.v[2]) + "," + std::to_string(val.v[3]);
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Size<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

        template <std::size_t N, class T>
        Log& operator<<(const Vector<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

    private:
        const Logger& logger;
        Level level = Level::info;
        std::string s;
    };

    class Logger final
    {
    public:
        explicit Logger(Log::Level initThreshold = Log::Level::all):
            threshold(initThreshold)
        {
#if !defined(__EMSCRIPTEN__)
            logThread = Thread(&Logger::logLoop, this);
#endif
        }

        Logger(const Logger&) = delete;
        Logger& operator=(const Logger&) = delete;
        Logger(Logger&&) = delete;
        Logger& operator=(Logger&&) = delete;

        ~Logger()
        {
#if !defined(__EMSCRIPTEN__)
            std::unique_lock lock(queueMutex);
            commandQueue.push(Command(Command::Type::quit));
            lock.unlock();
            queueCondition.notify_all();
#endif
        }

        Log log(const Log::Level level = Log::Level::info) const
        {
            return Log(*this, level);
        }

        void log(const std::string& str, const Log::Level level = Log::Level::info) const
        {
            if (level <= threshold)
            {
#if defined(__EMSCRIPTEN__)
                logString(str, level);
#else
                std::unique_lock lock(queueMutex);
                commandQueue.push(Command(Command::Type::logString, level, str));
                lock.unlock();
                queueCondition.notify_all();
#endif
            }
        }

    private:
        static void logString(const std::string& str, const Log::Level level = Log::Level::info);

#ifdef DEBUG
        std::atomic<Log::Level> threshold{Log::Level::all};
#else
        std::atomic<Log::Level> threshold{Log::Level::info};
#endif

#if !defined(__EMSCRIPTEN__)
        class Command final
        {
        public:
            enum class Type
            {
                logString,
                quit
            };

            explicit Command(const Type initType):
                type(initType)
            {
            }

            Command(const Type initType, const Log::Level initLevel,
                    const std::string& initString):
                type(initType),
                level(initLevel),
                str(initString)
            {
            }

            Type type;
            Log::Level level = Log::Level::all;
            std::string str;
        };

        void logLoop()
        {
            for (;;)
            {
                std::unique_lock lock(queueMutex);
                while (commandQueue.empty()) queueCondition.wait(lock);
                auto command = std::move(commandQueue.front());
                commandQueue.pop();
                lock.unlock();

                if (command.type == Command::Type::logString)
                    logString(command.str, command.level);
                else if (command.type == Command::Type::quit)
                    break;
            }
        }

        mutable std::condition_variable queueCondition;
        mutable std::mutex queueMutex;
        mutable std::queue<Command> commandQueue;
        Thread logThread;
#endif
    };
}

#endif // OUZEL_UTILS_LOG_HPP
<|endoftext|>


<commit_before>#include "Logic/NodeType.h"
#include "Logic/NodeFactory.h"
#include "Kommon/HighResolutionClock.h"
#include "Kommon/StringUtils.h"

#include <chrono>
#include <thread>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

class VideoFromFileNodeType : public NodeType
{
public:
	VideoFromFileNodeType()
#ifdef QT_DEBUG
		: _videoPath("video-4.mkv")
#else
		: _videoPath("")
#endif
		, _startFrame(0)
		, _endFrame(0)
		, _frameInterval(0)
		, _ignoreFps(false)
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::VideoPath:
			_videoPath = newValue.toFilepath();
			return true;
		case pid::StartFrame:
			_startFrame = newValue.toInt();
			return true;
		case pid::EndFrame:
			_endFrame = newValue.toInt();
			return true;
		case pid::IgnoreFps:
			_ignoreFps = newValue.toBool();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::VideoPath: return _videoPath;
		case pid::StartFrame: return int(_startFrame);
		case pid::EndFrame: return int(_endFrame);
		case pid::IgnoreFps: return _ignoreFps;
		}

		return NodeProperty();
	}

	bool restart() override
	{
		// This isn't really necessary as subsequent open()
		// should call it automatically
		if(_capture.isOpened())
			_capture.release();

		_capture.open(_videoPath.data());
		if(!_capture.isOpened())
			return false;

		if(_startFrame > 0)
			_capture.set(CV_CAP_PROP_POS_FRAMES, _startFrame);

		double fps = _ignoreFps ? 0 : _capture.get(CV_CAP_PROP_FPS);

		_frameInterval = (fps != 0)
			? unsigned(ceil(1000.0 / fps))
			: 0;
		_timeStamp = std::chrono::high_resolution_clock::time_point();
		_maxFrames = static_cast<unsigned>(_capture.get(CV_CAP_PROP_FRAME_COUNT));
		_currentFrame = _startFrame > 0 ? _startFrame : 0;

		return true;
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		double start = _clock.currentTimeInSeconds();

		if(!_capture.isOpened())
			return ExecutionStatus(EStatus::Ok);

		// We need this so we won't replace previous good frame with current bad (for instance - end of video)
		cv::Mat buffer;
		++_currentFrame;

		if(_endFrame > 0 && _currentFrame >= _endFrame)
		{
			// No more data (for us)
			return ExecutionStatus(EStatus::Ok, string_format("Frame image width: %d\nFrame image height: %d\nFrame size in kbytes: %d\nFrame: %d/%d",
				buffer.cols, buffer.rows, buffer.cols * buffer.rows * sizeof(uchar) * buffer.channels() / 1024, _currentFrame - 1, _maxFrames));
		}

		if(_frameInterval == 0)
		{
			_capture.read(buffer);
		}
		else
		{
			using namespace std::chrono;

			// This should keep FPS in sync
			high_resolution_clock::time_point s = high_resolution_clock::now();
			milliseconds dura = duration_cast<milliseconds>(s - _timeStamp);
			if(dura.count() < _frameInterval)
			{
				milliseconds waitDuration = milliseconds(_frameInterval) - dura;
				std::this_thread::sleep_for(waitDuration);
				// New start time
				start = _clock.currentTimeInSeconds();
			}

			_capture.read(buffer);

			_timeStamp = std::chrono::high_resolution_clock::now();
		}

		cv::Mat& output = writer.acquireSocket(0).getImage();

		if(!buffer.empty())
		{
			cv::cvtColor(buffer, output, CV_BGR2GRAY);
			double stop = _clock.currentTimeInSeconds();
			double elapsed = (stop - start) * 1e3;

			return ExecutionStatus(EStatus::Tag, elapsed,
				string_format("Frame image width: %d\nFrame image height: %d\nFrame size in kbytes: %d\nFrame: %d/%d",
					output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024, _currentFrame, _maxFrames));
		}
		else
		{
			// No more data
			return ExecutionStatus(EStatus::Ok);
		}
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Filepath, "Video path", "filter:Video files (*.mkv *.mp4 *.avi *.flv)" },
			{ EPropertyType::Integer, "Start frame", "min:0" },
			{ EPropertyType::Integer, "End frame", "min:0" },
			{ EPropertyType::Boolean, "Ignore FPS", "" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Provides video frames from specified stream.";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
		nodeConfig.flags = ENodeConfig::HasState | ENodeConfig::AutoTag | ENodeConfig::OverridesTimeComputation;
	}

private:
	enum class pid
	{
		VideoPath,
		StartFrame,
		EndFrame,
		IgnoreFps,
	};

	Filepath _videoPath;
	cv::VideoCapture _capture;
	unsigned _startFrame;
	unsigned _endFrame;
	unsigned _frameInterval;
	unsigned _currentFrame;
	unsigned _maxFrames;
	std::chrono::high_resolution_clock::time_point _timeStamp;
	static HighResolutionClock _clock;
	bool _ignoreFps;
};
HighResolutionClock VideoFromFileNodeType::_clock;

class ImageFromFileNodeType : public NodeType
{
public:
	ImageFromFileNodeType()
#ifdef QT_DEBUG
		: _filePath("lena.jpg")
#else
		: _filePath("")
#endif
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		if(propId == 0)
		{
			_filePath = newValue.toFilepath();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		if(propId == 0)
			return _filePath;

		return NodeProperty();
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();

		output = cv::imread(_filePath.data(), CV_LOAD_IMAGE_GRAYSCALE);

		if(output.empty())
			return ExecutionStatus(EStatus::Error, "File not found");
		return ExecutionStatus(EStatus::Ok,
			string_format("Image image width: %d\nImage image height: %d\nImage size in kbytes: %d",
				output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024));
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Filepath, "File path", "filter:"
			"Popular image formats (*.bmp *.jpeg *.jpg *.png *.tiff);;"
			"Windows bitmaps (*.bmp *.dib);;"
			"JPEG files (*.jpeg *.jpg *.jpe);;"
			"JPEG 2000 files (*.jp2);;"
			"Portable Network Graphics (*.png);;"
			"Portable image format (*.pbm *.pgm *.ppm);;"
			"Sun rasters (*.sr *.ras);;"
			"TIFF files (*.tiff *.tif);;"
			"All files (*.*)" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Loads image from a given location.";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
	}

protected:
	Filepath _filePath;
};

class ImageFromFileStreamNodeType : public ImageFromFileNodeType
{
public:
	bool restart() override
	{
		_img = cv::imread(_filePath.data(), CV_LOAD_IMAGE_GRAYSCALE);

		return !_img.empty();
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();

		output = _img;

		return ExecutionStatus(EStatus::Tag,
			string_format("Image image width: %d\nImage image height: %d\nImage size in kbytes: %d",
			output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024));
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		ImageFromFileNodeType::configuration(nodeConfig);
		nodeConfig.flags = ENodeConfig::AutoTag | ENodeConfig::HasState;
	}

private:
	cv::Mat _img;
};

class CameraCaptureNodeType : public NodeType
{
public:
	CameraCaptureNodeType()
		: _deviceId(0)
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::DeviceId:
			_deviceId = newValue.toInt();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::DeviceId: return _deviceId;
		}

		return NodeProperty();
	}

	bool restart() override
	{
		_capture.open(_deviceId);
		if(!_capture.isOpened())
			return false;
		return true;
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();

		/// TODO: Could be moved to background thread (just like JAI)
		cv::Mat tmp;
		_capture >> tmp;

		if(tmp.empty())
			return ExecutionStatus(EStatus::Ok);
		if(tmp.channels() > 1)
			cvtColor(tmp, output, CV_BGR2GRAY);

		return ExecutionStatus(EStatus::Tag,
			string_format("Frame image width: %d\nFrame image height: %d\nFrame size in kbytes: %d",
				output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024));
	}

	void finish() override
	{
		if(_capture.isOpened())
			_capture.release();
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Integer, "Device Id", "min:0" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Provides video frames from specified camera device.";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
		nodeConfig.flags = ENodeConfig::HasState | ENodeConfig::AutoTag;
	}

private:
	enum class pid
	{
		DeviceId
	};

	int _deviceId;
	cv::VideoCapture _capture;
};

REGISTER_NODE("Sources/Image from file stream", ImageFromFileStreamNodeType)
REGISTER_NODE("Sources/Camera capture", CameraCaptureNodeType)
REGISTER_NODE("Sources/Video from file", VideoFromFileNodeType)
REGISTER_NODE("Sources/Image from file", ImageFromFileNodeType)
<commit_msg>added node for generating solid image (one-color)<commit_after>#include "Logic/NodeType.h"
#include "Logic/NodeFactory.h"
#include "Kommon/HighResolutionClock.h"
#include "Kommon/StringUtils.h"

#include <chrono>
#include <thread>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

class VideoFromFileNodeType : public NodeType
{
public:
	VideoFromFileNodeType()
#ifdef QT_DEBUG
		: _videoPath("video-4.mkv")
#else
		: _videoPath("")
#endif
		, _startFrame(0)
		, _endFrame(0)
		, _frameInterval(0)
		, _ignoreFps(false)
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::VideoPath:
			_videoPath = newValue.toFilepath();
			return true;
		case pid::StartFrame:
			_startFrame = newValue.toInt();
			return true;
		case pid::EndFrame:
			_endFrame = newValue.toInt();
			return true;
		case pid::IgnoreFps:
			_ignoreFps = newValue.toBool();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::VideoPath: return _videoPath;
		case pid::StartFrame: return int(_startFrame);
		case pid::EndFrame: return int(_endFrame);
		case pid::IgnoreFps: return _ignoreFps;
		}

		return NodeProperty();
	}

	bool restart() override
	{
		// This isn't really necessary as subsequent open()
		// should call it automatically
		if(_capture.isOpened())
			_capture.release();

		_capture.open(_videoPath.data());
		if(!_capture.isOpened())
			return false;

		if(_startFrame > 0)
			_capture.set(CV_CAP_PROP_POS_FRAMES, _startFrame);

		double fps = _ignoreFps ? 0 : _capture.get(CV_CAP_PROP_FPS);

		_frameInterval = (fps != 0)
			? unsigned(ceil(1000.0 / fps))
			: 0;
		_timeStamp = std::chrono::high_resolution_clock::time_point();
		_maxFrames = static_cast<unsigned>(_capture.get(CV_CAP_PROP_FRAME_COUNT));
		_currentFrame = _startFrame > 0 ? _startFrame : 0;

		return true;
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		double start = _clock.currentTimeInSeconds();

		if(!_capture.isOpened())
			return ExecutionStatus(EStatus::Ok);

		// We need this so we won't replace previous good frame with current bad (for instance - end of video)
		cv::Mat buffer;
		++_currentFrame;

		if(_endFrame > 0 && _currentFrame >= _endFrame)
		{
			// No more data (for us)
			return ExecutionStatus(EStatus::Ok, string_format("Frame image width: %d\nFrame image height: %d\nFrame size in kbytes: %d\nFrame: %d/%d",
				buffer.cols, buffer.rows, buffer.cols * buffer.rows * sizeof(uchar) * buffer.channels() / 1024, _currentFrame - 1, _maxFrames));
		}

		if(_frameInterval == 0)
		{
			_capture.read(buffer);
		}
		else
		{
			using namespace std::chrono;

			// This should keep FPS in sync
			high_resolution_clock::time_point s = high_resolution_clock::now();
			milliseconds dura = duration_cast<milliseconds>(s - _timeStamp);
			if(dura.count() < _frameInterval)
			{
				milliseconds waitDuration = milliseconds(_frameInterval) - dura;
				std::this_thread::sleep_for(waitDuration);
				// New start time
				start = _clock.currentTimeInSeconds();
			}

			_capture.read(buffer);

			_timeStamp = std::chrono::high_resolution_clock::now();
		}

		cv::Mat& output = writer.acquireSocket(0).getImage();

		if(!buffer.empty())
		{
			cv::cvtColor(buffer, output, CV_BGR2GRAY);
			double stop = _clock.currentTimeInSeconds();
			double elapsed = (stop - start) * 1e3;

			return ExecutionStatus(EStatus::Tag, elapsed,
				string_format("Frame image width: %d\nFrame image height: %d\nFrame size in kbytes: %d\nFrame: %d/%d",
					output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024, _currentFrame, _maxFrames));
		}
		else
		{
			// No more data
			return ExecutionStatus(EStatus::Ok);
		}
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Filepath, "Video path", "filter:Video files (*.mkv *.mp4 *.avi *.flv)" },
			{ EPropertyType::Integer, "Start frame", "min:0" },
			{ EPropertyType::Integer, "End frame", "min:0" },
			{ EPropertyType::Boolean, "Ignore FPS", "" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Provides video frames from specified stream.";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
		nodeConfig.flags = ENodeConfig::HasState | ENodeConfig::AutoTag | ENodeConfig::OverridesTimeComputation;
	}

private:
	enum class pid
	{
		VideoPath,
		StartFrame,
		EndFrame,
		IgnoreFps,
	};

	Filepath _videoPath;
	cv::VideoCapture _capture;
	unsigned _startFrame;
	unsigned _endFrame;
	unsigned _frameInterval;
	unsigned _currentFrame;
	unsigned _maxFrames;
	std::chrono::high_resolution_clock::time_point _timeStamp;
	static HighResolutionClock _clock;
	bool _ignoreFps;
};
HighResolutionClock VideoFromFileNodeType::_clock;

class ImageFromFileNodeType : public NodeType
{
public:
	ImageFromFileNodeType()
#ifdef QT_DEBUG
		: _filePath("lena.jpg")
#else
		: _filePath("")
#endif
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		if(propId == 0)
		{
			_filePath = newValue.toFilepath();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		if(propId == 0)
			return _filePath;

		return NodeProperty();
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();

		output = cv::imread(_filePath.data(), CV_LOAD_IMAGE_GRAYSCALE);

		if(output.empty())
			return ExecutionStatus(EStatus::Error, "File not found");
		return ExecutionStatus(EStatus::Ok,
			string_format("Image image width: %d\nImage image height: %d\nImage size in kbytes: %d",
				output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024));
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Filepath, "File path", "filter:"
			"Popular image formats (*.bmp *.jpeg *.jpg *.png *.tiff);;"
			"Windows bitmaps (*.bmp *.dib);;"
			"JPEG files (*.jpeg *.jpg *.jpe);;"
			"JPEG 2000 files (*.jp2);;"
			"Portable Network Graphics (*.png);;"
			"Portable image format (*.pbm *.pgm *.ppm);;"
			"Sun rasters (*.sr *.ras);;"
			"TIFF files (*.tiff *.tif);;"
			"All files (*.*)" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Loads image from a given location.";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
	}

protected:
	Filepath _filePath;
};

class ImageFromFileStreamNodeType : public ImageFromFileNodeType
{
public:
	bool restart() override
	{
		_img = cv::imread(_filePath.data(), CV_LOAD_IMAGE_GRAYSCALE);

		return !_img.empty();
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();

		output = _img;

		return ExecutionStatus(EStatus::Tag,
			string_format("Image image width: %d\nImage image height: %d\nImage size in kbytes: %d",
			output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024));
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		ImageFromFileNodeType::configuration(nodeConfig);
		nodeConfig.flags = ENodeConfig::AutoTag | ENodeConfig::HasState;
	}

private:
	cv::Mat _img;
};

class CameraCaptureNodeType : public NodeType
{
public:
	CameraCaptureNodeType()
		: _deviceId(0)
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::DeviceId:
			_deviceId = newValue.toInt();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::DeviceId: return _deviceId;
		}

		return NodeProperty();
	}

	bool restart() override
	{
		_capture.open(_deviceId);
		if(!_capture.isOpened())
			return false;
		return true;
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();

		/// TODO: Could be moved to background thread (just like JAI)
		cv::Mat tmp;
		_capture >> tmp;

		if(tmp.empty())
			return ExecutionStatus(EStatus::Ok);
		if(tmp.channels() > 1)
			cvtColor(tmp, output, CV_BGR2GRAY);

		return ExecutionStatus(EStatus::Tag,
			string_format("Frame image width: %d\nFrame image height: %d\nFrame size in kbytes: %d",
				output.cols, output.rows, output.cols * output.rows * sizeof(uchar) * output.channels() / 1024));
	}

	void finish() override
	{
		if(_capture.isOpened())
			_capture.release();
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Integer, "Device Id", "min:0" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Provides video frames from specified camera device.";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
		nodeConfig.flags = ENodeConfig::HasState | ENodeConfig::AutoTag;
	}

private:
	enum class pid
	{
		DeviceId
	};

	int _deviceId;
	cv::VideoCapture _capture;
};

class SolidImageNodeType : public NodeType
{
public:
	SolidImageNodeType()
		: _width(512)
		, _height(512)
		, _gray(0)
	{
	}

	bool setProperty(PropertyID propId, const NodeProperty& newValue) override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::Width:
			_width = newValue.toInt();
			return true;
		case pid::Height:
			_height = newValue.toInt();
			return true;
		case pid::Gray:
			_gray = newValue.toInt();
			return true;
		}

		return false;
	}

	NodeProperty property(PropertyID propId) const override
	{
		switch(static_cast<pid>(propId))
		{
		case pid::Width: return _width;
		case pid::Height: return _height;
		case pid::Gray: return _gray;
		}

		return NodeProperty();
	}

	ExecutionStatus execute(NodeSocketReader&, NodeSocketWriter& writer) override
	{
		cv::Mat& output = writer.acquireSocket(0).getImage();
		if(_width * _height > 0)
			output = cv::Mat(_height, _width, CV_8UC1, cv::Scalar(_gray));
		return ExecutionStatus(EStatus::Ok);
	}

	void configuration(NodeConfig& nodeConfig) const override
	{
		static const OutputSocketConfig out_config[] = {
			{ ENodeFlowDataType::Image, "output", "Output", "" },
			{ ENodeFlowDataType::Invalid, "", "", "" }
		};
		static const PropertyConfig prop_config[] = {
			{ EPropertyType::Integer, "Width", "min:1, max:4096" },
			{ EPropertyType::Integer, "Height", "min:1, max:4096" },
			{ EPropertyType::Integer, "Gray", "min:0, max:255" },
			{ EPropertyType::Unknown, "", "" }
		};

		nodeConfig.description = "Creates solid one-color (grayscale) image";
		nodeConfig.pOutputSockets = out_config;
		nodeConfig.pProperties = prop_config;
	}

private:
	enum class pid
	{
		Width,
		Height,
		Gray
	};

	int _width;
	int _height;
	int _gray;
};

REGISTER_NODE("Sources/Solid image", SolidImageNodeType)
REGISTER_NODE("Sources/Image from file stream", ImageFromFileStreamNodeType)
REGISTER_NODE("Sources/Camera capture", CameraCaptureNodeType)
REGISTER_NODE("Sources/Video from file", VideoFromFileNodeType)
REGISTER_NODE("Sources/Image from file", ImageFromFileNodeType)
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>

/* C++ supports vectors as part of the standard template library (STL). Unlike lists
   the memory allocated for the nodes is contingous and random access is possible.
*/

int main(int argc, char* argv[])
{
    std::vector<int> x(3);         // declares a vector of integers of size 3

    // elements can be written into a vector using []
    x[0] = 7;
    x[1] = 8;
    x[2] = 19;

    // elements can also be read using []
    std::cout << x[0] << " " << x[1] << " " << x[2] << std::endl;

    // elements can be accessed also using at() which checks the bounds
    std::cout << x.at(0) << " " << x.at(1) << " " << x.at(2) << std::endl;

    std::vector<int> y;         // declare a vector of unspecified size
    y.push_back(2);             // add elements at the end of the vector
    y.push_back(3);
    y.push_back(4);
    x.push_back(4);             // this also works if x was declared previously of size 3
    // elements can still be read using []
    std::cout << y[0] << " " << y[1] << " " << y[2] << std::endl;

    // the size of the vector is dynamically expanded as needed and can be retrieved via
    std::cout << "y.size: " << y.size() << std::endl;

    /* Expanding the size of the vector while still ensuring contiguous memory
       might require reallocation and copying from time to time, which costs performance.
       The size of the allocated region is the capacity and in general different from the
       currently used size. */

    y.reserve(10);      // increases size of capacity, i.e. allocated memory
    std::cout << "y.capacity: " << y.capacity() << std::endl;

    // iterate via random access
    for (int i = 0; i < x.size(); i++)
        std::cout << x[i] << " ";
    std::cout << std::endl;

    // iterate via iterator
    for (std::vector<int>::const_iterator p = x.begin(); p != x.end(); p++)
        std::cout << *p << " ";
    std::cout << std::endl;

    // insert elements in the middle
    std::vector<int>::const_iterator p = x.begin();
    p += 2;
    x.insert(p, 3, 2);                      // inserts at p three times the element 2
    for (int i = 0; i < x.size(); i++)
        std::cout << x[i] << " ";
    std::cout << std::endl;

    // delete elements
    std::vector<int>::iterator q = x.begin();
    q += 3;
    x.erase(q, q + 2);                      // delete the next 2 elements after q starting with *q
    for (int i = 0; i < x.size(); i++)
        std::cout << x[i] << " ";
    std::cout << std::endl;

    // advanced initialization
    std::vector<int> w(3, 7);   // declares a vector of size 3 and initializes all entries with 7
    std::vector<int> u(w);      // declares a vector that is a copy of w
    std::vector<std::vector<int>> z(3, std::vector<int>(4)); // declares a vector of length 3 of vectors of length 4
    z[1][2] = 17;   // access like this

    return EXIT_SUCCESS;
}
<commit_msg>added const vectors<commit_after>#include <iostream>
#include <vector>

/* C++ supports vectors as part of the standard template library (STL). Unlike lists
   the memory allocated for the nodes is contingous and random access is possible.
*/

int main(int argc, char* argv[])
{
    std::vector<int> x(3);         // declares a vector of integers of size 3

    // elements can be written into a vector using []
    x[0] = 7;
    x[1] = 8;
    x[2] = 19;

    // elements can also be read using []
    std::cout << x[0] << " " << x[1] << " " << x[2] << std::endl;

    // elements can be accessed also using at() which checks the bounds
    std::cout << x.at(0) << " " << x.at(1) << " " << x.at(2) << std::endl;

    std::vector<int> y;         // declare a vector of unspecified size
    y.push_back(2);             // add elements at the end of the vector
    y.push_back(3);
    y.push_back(4);
    x.push_back(4);             // this also works if x was declared previously of size 3
    // elements can still be read using []
    std::cout << y[0] << " " << y[1] << " " << y[2] << std::endl;

    // the size of the vector is dynamically expanded as needed and can be retrieved via
    std::cout << "y.size: " << y.size() << std::endl;

    /* Expanding the size of the vector while still ensuring contiguous memory
       might require reallocation and copying from time to time, which costs performance.
       The size of the allocated region is the capacity and in general different from the
       currently used size. */

    y.reserve(10);      // increases size of capacity, i.e. allocated memory
    std::cout << "y.capacity: " << y.capacity() << std::endl;

    // iterate via random access
    for (int i = 0; i < x.size(); i++)
        std::cout << x[i] << " ";
    std::cout << std::endl;

    // iterate via iterator
    for (std::vector<int>::const_iterator p = x.begin(); p != x.end(); p++)
        std::cout << *p << " ";
    std::cout << std::endl;

    // insert elements in the middle
    std::vector<int>::const_iterator p = x.begin();
    p += 2;
    x.insert(p, 3, 2);                      // inserts at p three times the element 2
    for (int i = 0; i < x.size(); i++)
        std::cout << x[i] << " ";
    std::cout << std::endl;

    // delete elements
    std::vector<int>::iterator q = x.begin();
    q += 3;
    x.erase(q, q + 2);                      // delete the next 2 elements after q starting with *q
    for (int i = 0; i < x.size(); i++)
        std::cout << x[i] << " ";
    std::cout << std::endl;

    // advanced initialization
    std::vector<int> w(3, 7);   // declares a vector of size 3 and initializes all entries with 7
    std::vector<int> u(w);      // declares a vector that is a copy of w
    std::vector<std::vector<int>> z(3, std::vector<int>(4)); // declares a vector of length 3 of vectors of length 4
    z[1][2] = 17;   // access like this

    // const vectors
    const std::vector<int> mycv = {1, 2, 3};    // size of the vector is const and entries cannot be modified

    return EXIT_SUCCESS;
}

/*
    FURTHER READING
    Const Vectors: https://stackoverflow.com/questions/17313062/vector-of-const-objects-giving-compile-error
*/<|endoftext|>


<commit_before>#include "converterconfigurationdialog.h"
#include "checkboxdelegate.h"
#include "cmdlinehighlighterdelegate.h"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QDebug>
#include <QApplication>
#include <QToolButton>

ConverterConfigurationDialog::ConverterConfigurationDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f)
{
    // allocate
    auto headingLabel = new QLabel("Configure External Converters");
    mainConverterLocationLabel = new QLabel("Location of Main Converter:");
    mainConverterLocationEdit = new FancyLineEdit;
    contextMenu = new QMenu(this);
    contextToolBar = new QToolBar(this);
    browseButton = new QPushButton("Browse ...");
    additionalConvertersLabel = new QLabel("Additional converters:");
    auto stdButtons = new QDialogButtonBox(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    auto mainLayout = new QVBoxLayout;
    auto mainConverterLayout = new QHBoxLayout;

    // set model
    tableView.setModel(&convertersModel);

    // configure view
    tableView.verticalHeader()->setHidden(true);
    tableView.setSelectionMode(QAbstractItemView::SingleSelection);
    tableView.setSelectionBehavior(QAbstractItemView::SelectRows);
    tableView.setContextMenuPolicy(Qt::CustomContextMenu);
    tableView.horizontalHeader()->setStretchLastSection(true);

    // configure menu & toolbar
    initMenu();
    initToolBar();
    contextToolBar->hide();

    // configure fonts
    QFont defaultFont{qApp->font()};
    QFont heading2Font{defaultFont};
    QFont heading1Font{defaultFont};
    heading2Font.setPointSize(defaultFont.pointSize() + 2);
    heading1Font.setPointSize(defaultFont.pointSize() + 4);

    // configure widgets
    headingLabel->setFont(heading1Font);
    headingLabel->setAlignment(Qt::AlignHCenter);
    mainConverterLocationEdit->hideEditButton();
    mainConverterLocationLabel->setFont(heading2Font);
    additionalConvertersLabel->setFont(heading2Font);

    // attach widgets to main layout
    mainLayout->addWidget(headingLabel);
    mainLayout->addSpacing(6);
    mainLayout->addWidget(mainConverterLocationLabel);
    mainConverterLayout->addWidget(mainConverterLocationEdit);
    mainConverterLayout->addWidget(browseButton);
    mainLayout->addLayout(mainConverterLayout);
    mainLayout->addSpacing(6);
    mainLayout->addWidget(additionalConvertersLabel);
    mainLayout->addWidget(&tableView);
    mainLayout->addWidget(stdButtons);
    setLayout(mainLayout);

    // hide unnecessary columns
    tableView.setColumnHidden(0, true);
    tableView.setColumnHidden(3, true);
    tableView.setColumnHidden(6, true);
    tableView.setColumnHidden(9, true);
    tableView.setColumnHidden(10, true);

    // set delegates
    tableView.setItemDelegateForColumn(8, new CmdLineHighlighterDelegate(this));
    tableView.setItemDelegateForColumn(1, new CheckBoxDelegate(this));

    // connect signals / slots
    connect(mainConverterLocationEdit, &QLineEdit::editingFinished, this, [this] {
       mainConverterPath = mainConverterLocationEdit->text();
    });

    connect(browseButton, &QPushButton::clicked, this, &ConverterConfigurationDialog::promptForResamplerLocation);
    connect(&tableView, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos){
        contextMenu->popup(QPoint{this->mapToGlobal(pos).x(), this->mapToGlobal(pos).y() + contextMenu->sizeHint().height()});
        contextToolBar->hide();
    });

    connect(&tableView, &QTableView::clicked, this, [this](const QModelIndex& modelIndex) {
        if(modelIndex.column() == 1)
            return;

        Q_UNUSED(modelIndex);
        QPoint p = QCursor::pos();
        contextToolBar->move(mapFromGlobal(p) + QPoint{25 /*tableView.columnWidth(modelIndex.column()) / 2*/, -contextToolBar->sizeHint().height() / 2});
        contextToolBar->show();
        contextToolBar->raise();
    });

    connect(stdButtons, &QDialogButtonBox::clicked, this, [this, stdButtons](QAbstractButton* b){
        if(b == stdButtons->button(QDialogButtonBox::RestoreDefaults)){
            onRestoreDefaults();
        }
    });

    connect(stdButtons, &QDialogButtonBox::accepted, this, &QDialog::accept);
    connect(stdButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}

void ConverterConfigurationDialog::initMenu() {
    contextMenu->addAction("New", [this] {
       onNewRequested(tableView.currentIndex());
    }, QKeySequence::New);

    contextMenu->addAction("Edit ...", [this] {
       onEditRequested(tableView.currentIndex());
    });

    contextMenu->addAction("Clone", [this] {
       onCloneRequested(tableView.currentIndex());
    }, QKeySequence::Copy);

    contextMenu->addAction("Delete", [this] {
       onDeleteRequested(tableView.currentIndex());
    }, {QKeySequence::Delete});

    contextMenu->addAction("Move Up", [this] {
        onMoveUpRequested(tableView.currentIndex());
    });

    contextMenu->addAction("Move Down", [this] {
        onMoveDownRequested(tableView.currentIndex());
    });

}

void ConverterConfigurationDialog::initToolBar() {
    QList<QAction*> actions;

    actions.append(contextToolBar->addAction("New", [this] {
       onNewRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Edit ...", [this] {
       onEditRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Clone", [this] {
       onCloneRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Delete", [this] {
       onDeleteRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Move Up", [this] {
        onMoveUpRequested(tableView.currentIndex());
        //contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Move Down", [this] {
        onMoveDownRequested(tableView.currentIndex());
        //contextToolBar->hide();
    }));

    contextToolBar->setContentsMargins(0, 0, 0, 0);
    contextToolBar->setMinimumWidth(tableView.width() / 2);
    contextToolBar->setMaximumWidth(tableView.width());
    contextToolBar->setFixedHeight(40);
    contextToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    contextToolBar->setStyleSheet("QToolBar {background-color: rgba(0,0,0,0);}");

    for(QAction* a : actions) {
        QWidget* w = contextToolBar->widgetForAction(a);
        if(QString(w->metaObject()->className()) == "QToolButton") {
            QToolButton* t = qobject_cast<QToolButton*>(w);
            t->setStyleSheet("QToolButton {padding: 5px; border-radius: 3px}");
        }
    }

}

void ConverterConfigurationDialog::showEvent(QShowEvent* event) {

    if(mainConverterPath.isEmpty() || !QFile::exists(mainConverterPath)) {
       promptForResamplerLocation();
    }

    mainConverterLocationLabel->setText(QString{"Location of Main Converter (%1):"}.arg(expectedMainConverter));
    mainConverterLocationEdit->setText(mainConverterPath);

    QDialog::showEvent(event);
}

void ConverterConfigurationDialog::resizeEvent(QResizeEvent *event)
{
    int tw = event->size().width();

    static const QVector<double> columnWidths {
        0,      /* "Priority" */
        10,     /* "Enabled" */
        20,     /* "Name" */
        0 ,     /* "Comment" */
        12,     /* "Input File Extension" */
        12,     /* "Output File Extension" */
        0 ,     /* "Executable" */
        20,     /* "Executable Path" */
        20,     /* "Command Line" */
        0 ,     /* "Download Locations" */
        0      /* "Operating Systems" */
    };

    for(int col = 0; col < convertersModel.columnCount({}) - 1; col++) {
        tableView.horizontalHeader()->resizeSection(col, tw * columnWidths.at(col)/100.0);
    }

    tableView.horizontalHeader()->setHidden(false);

    QDialog::resizeEvent(event);
}

QVector<ConverterDefinition> ConverterConfigurationDialog::getConverterDefinitions() const
{
    return convertersModel.getConverterDefinitions();
}

void ConverterConfigurationDialog::setConverterDefinitions(const QVector<ConverterDefinition> &value)
{
    convertersModel.setConverterDefinitions(value);
}

void ConverterConfigurationDialog::promptForResamplerLocation() {
    QString s(tr("Please locate the file: "));
    s.append(expectedMainConverter);

#if defined (Q_OS_WIN)
    QString filter = "*.exe";
#else
    QString filter = "";
#endif

    QString cp = QFileDialog::getOpenFileName(this, s, mainConverterPath,  filter);

    if(!cp.isNull()) {
        mainConverterPath = cp;
        if(mainConverterPath.lastIndexOf(expectedMainConverter, -1, Qt::CaseInsensitive) == -1) { // safeguard against wrong executable being configured
            mainConverterPath.clear();
            QMessageBox::warning(this, tr("Converter Location"), tr("That is not the right program!\n"), QMessageBox::Ok);
        }
    }
}

QString ConverterConfigurationDialog::getMainConverterPath() const
{
    return mainConverterPath;
}

void ConverterConfigurationDialog::setMainConverterPath(const QString &value)
{
    mainConverterPath = value;
}

QString ConverterConfigurationDialog::getExpectedMainConverter() const
{
    return expectedMainConverter;
}

void ConverterConfigurationDialog::setExpectedMainConverter(const QString &value)
{
    expectedMainConverter = value;
    // set tooltips
    mainConverterLocationLabel->setToolTip(QString{"Please enter the location of %1 in the box below.\n"
                                           "(%1 is the command-line audio converter that Ferocious was designed to work with.)"
                                           }.arg(expectedMainConverter));
    mainConverterLocationEdit->setToolTip(QString{"Location of %1\n"
                                          "Note: you can also use drag-and-drop, or the 'Browse' button"}.arg(expectedMainConverter));
    browseButton->setToolTip(QString{"Browse to location of %1"}.arg(expectedMainConverter));
    additionalConvertersLabel->setToolTip("Use the table below to cofigure additional converters for specialized file formats.");

}

void ConverterConfigurationDialog::onNewRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 0 )
        return;

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        converterDefinitions.insert(row, {});
        convertersModel.setConverterDefinitions(converterDefinitions);
    }
}

void ConverterConfigurationDialog::onEditRequested(const QModelIndex& modelIndex)
{
    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if (converterDefinitions.isEmpty())
        return;

    int row = modelIndex.row();

    if (row < 0)
        return;

    auto dlg = new ConverterConfigurationEditDialog(this);
    if(row < converterDefinitions.count()) {
        dlg->setConverterDefinition(converterDefinitions.at(row));
        int result = dlg->exec();
        if(result == QDialog::Accepted) {
            converterDefinitions[row] = dlg->getConverterDefinition();
            convertersModel.setConverterDefinitions(converterDefinitions);
        }
    }
}

void ConverterConfigurationDialog::onDeleteRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 0 )
        return;

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        converterDefinitions.removeAt(row);
        convertersModel.setConverterDefinitions(converterDefinitions);
    }
}

void ConverterConfigurationDialog::onCloneRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 0 )
        return;

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        converterDefinitions.insert(row, converterDefinitions.at(row));
        convertersModel.setConverterDefinitions(converterDefinitions);
    }
}

void ConverterConfigurationDialog::onMoveUpRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 1 ) {
        return;
    }

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        qSwap(converterDefinitions[row], converterDefinitions[row - 1]);
        convertersModel.setConverterDefinitions(converterDefinitions);
        tableView.selectRow(row - 1);
    }
}

void ConverterConfigurationDialog::onMoveDownRequested(const QModelIndex& modelIndex)
{

    int row = modelIndex.row();

    if(row < 0 ) {
        return;
    }

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count() - 1) {
        qSwap(converterDefinitions[row], converterDefinitions[row + 1]);
        convertersModel.setConverterDefinitions(converterDefinitions);
        tableView.selectRow(row + 1);
    }
}

void ConverterConfigurationDialog::onRestoreDefaults() {
    setConverterDefinitions(ConverterDefinition::loadConverterDefinitions(":/converters.json"));
}
<commit_msg>chabged popup position of toolbox to align with 4th column<commit_after>#include "converterconfigurationdialog.h"
#include "checkboxdelegate.h"
#include "cmdlinehighlighterdelegate.h"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QDebug>
#include <QApplication>
#include <QToolButton>

ConverterConfigurationDialog::ConverterConfigurationDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f)
{
    // allocate
    auto headingLabel = new QLabel("Configure External Converters");
    mainConverterLocationLabel = new QLabel("Location of Main Converter:");
    mainConverterLocationEdit = new FancyLineEdit;
    contextMenu = new QMenu(this);
    contextToolBar = new QToolBar(this);
    browseButton = new QPushButton("Browse ...");
    additionalConvertersLabel = new QLabel("Additional converters:");
    auto stdButtons = new QDialogButtonBox(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    auto mainLayout = new QVBoxLayout;
    auto mainConverterLayout = new QHBoxLayout;

    // set model
    tableView.setModel(&convertersModel);

    // configure view
    tableView.verticalHeader()->setHidden(true);
    tableView.setSelectionMode(QAbstractItemView::SingleSelection);
    tableView.setSelectionBehavior(QAbstractItemView::SelectRows);
    tableView.setContextMenuPolicy(Qt::CustomContextMenu);
    tableView.horizontalHeader()->setStretchLastSection(true);

    // configure menu & toolbar
    initMenu();
    initToolBar();
    contextToolBar->hide();

    // configure fonts
    QFont defaultFont{qApp->font()};
    QFont heading2Font{defaultFont};
    QFont heading1Font{defaultFont};
    heading2Font.setPointSize(defaultFont.pointSize() + 2);
    heading1Font.setPointSize(defaultFont.pointSize() + 4);

    // configure widgets
    headingLabel->setFont(heading1Font);
    headingLabel->setAlignment(Qt::AlignHCenter);
    mainConverterLocationEdit->hideEditButton();
    mainConverterLocationLabel->setFont(heading2Font);
    additionalConvertersLabel->setFont(heading2Font);

    // attach widgets to main layout
    mainLayout->addWidget(headingLabel);
    mainLayout->addSpacing(6);
    mainLayout->addWidget(mainConverterLocationLabel);
    mainConverterLayout->addWidget(mainConverterLocationEdit);
    mainConverterLayout->addWidget(browseButton);
    mainLayout->addLayout(mainConverterLayout);
    mainLayout->addSpacing(6);
    mainLayout->addWidget(additionalConvertersLabel);
    mainLayout->addWidget(&tableView);
    mainLayout->addWidget(stdButtons);
    setLayout(mainLayout);

    // hide unnecessary columns
    tableView.setColumnHidden(0, true);
    tableView.setColumnHidden(3, true);
    tableView.setColumnHidden(6, true);
    tableView.setColumnHidden(9, true);
    tableView.setColumnHidden(10, true);

    // set delegates
    tableView.setItemDelegateForColumn(8, new CmdLineHighlighterDelegate(this));
    tableView.setItemDelegateForColumn(1, new CheckBoxDelegate(this));

    // connect signals / slots
    connect(mainConverterLocationEdit, &QLineEdit::editingFinished, this, [this] {
       mainConverterPath = mainConverterLocationEdit->text();
    });

    connect(browseButton, &QPushButton::clicked, this, &ConverterConfigurationDialog::promptForResamplerLocation);
    connect(&tableView, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos){
        contextMenu->popup(QPoint{this->mapToGlobal(pos).x(), this->mapToGlobal(pos).y() + contextMenu->sizeHint().height()});
        contextToolBar->hide();
    });

    connect(&tableView, &QTableView::clicked, this, [this](const QModelIndex& modelIndex) {
        if(modelIndex.column() == 1)
            return;

    //    QPoint p = QCursor::pos();
    //    contextToolBar->move(mapFromGlobal(p) + QPoint{25 /*tableView.columnWidth(modelIndex.column()) / 2*/, -contextToolBar->sizeHint().height() / 2});
        QPoint p = QPoint{tableView.columnViewportPosition(4), tableView.rowViewportPosition(modelIndex.row())};
        QPoint q = tableView.mapTo(this, p + QPoint{0, tableView.rowHeight(modelIndex.row()) / 2});
        contextToolBar->move(q);
        contextToolBar->show();
        contextToolBar->raise();
    });

    connect(stdButtons, &QDialogButtonBox::clicked, this, [this, stdButtons](QAbstractButton* b){
        if(b == stdButtons->button(QDialogButtonBox::RestoreDefaults)){
            onRestoreDefaults();
        }
    });

    connect(stdButtons, &QDialogButtonBox::accepted, this, &QDialog::accept);
    connect(stdButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}

void ConverterConfigurationDialog::initMenu() {
    contextMenu->addAction("New", [this] {
       onNewRequested(tableView.currentIndex());
    }, QKeySequence::New);

    contextMenu->addAction("Edit ...", [this] {
       onEditRequested(tableView.currentIndex());
    });

    contextMenu->addAction("Clone", [this] {
       onCloneRequested(tableView.currentIndex());
    }, QKeySequence::Copy);

    contextMenu->addAction("Delete", [this] {
       onDeleteRequested(tableView.currentIndex());
    }, {QKeySequence::Delete});

    contextMenu->addAction("Move Up", [this] {
        onMoveUpRequested(tableView.currentIndex());
    });

    contextMenu->addAction("Move Down", [this] {
        onMoveDownRequested(tableView.currentIndex());
    });

}

void ConverterConfigurationDialog::initToolBar() {
    QList<QAction*> actions;

    actions.append(contextToolBar->addAction("New", [this] {
       onNewRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Edit ...", [this] {
       onEditRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Clone", [this] {
       onCloneRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Delete", [this] {
       onDeleteRequested(tableView.currentIndex());
       contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Move Up", [this] {
        onMoveUpRequested(tableView.currentIndex());
        //contextToolBar->hide();
    }));

    actions.append(contextToolBar->addAction("Move Down", [this] {
        onMoveDownRequested(tableView.currentIndex());
        //contextToolBar->hide();
    }));

    contextToolBar->setContentsMargins(0, 0, 0, 0);
    contextToolBar->setMinimumWidth(tableView.width() / 2);
    contextToolBar->setMaximumWidth(tableView.width());
    contextToolBar->setFixedHeight(40);
    contextToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    contextToolBar->setStyleSheet("QToolBar {background-color: rgba(0,0,0,0);}");

    for(QAction* a : actions) {
        QWidget* w = contextToolBar->widgetForAction(a);
        if(QString(w->metaObject()->className()) == "QToolButton") {
            QToolButton* t = qobject_cast<QToolButton*>(w);
            t->setStyleSheet("QToolButton {padding: 5px; border-radius: 3px}");
        }
    }

}

void ConverterConfigurationDialog::showEvent(QShowEvent* event) {

    if(mainConverterPath.isEmpty() || !QFile::exists(mainConverterPath)) {
       promptForResamplerLocation();
    }

    mainConverterLocationLabel->setText(QString{"Location of Main Converter (%1):"}.arg(expectedMainConverter));
    mainConverterLocationEdit->setText(mainConverterPath);

    QDialog::showEvent(event);
}

void ConverterConfigurationDialog::resizeEvent(QResizeEvent *event)
{
    int tw = event->size().width();

    static const QVector<double> columnWidths {
        0,      /* "Priority" */
        10,     /* "Enabled" */
        20,     /* "Name" */
        0 ,     /* "Comment" */
        12,     /* "Input File Extension" */
        12,     /* "Output File Extension" */
        0 ,     /* "Executable" */
        20,     /* "Executable Path" */
        20,     /* "Command Line" */
        0 ,     /* "Download Locations" */
        0      /* "Operating Systems" */
    };

    for(int col = 0; col < convertersModel.columnCount({}) - 1; col++) {
        tableView.horizontalHeader()->resizeSection(col, tw * columnWidths.at(col)/100.0);
    }

    tableView.horizontalHeader()->setHidden(false);

    QDialog::resizeEvent(event);
}

QVector<ConverterDefinition> ConverterConfigurationDialog::getConverterDefinitions() const
{
    return convertersModel.getConverterDefinitions();
}

void ConverterConfigurationDialog::setConverterDefinitions(const QVector<ConverterDefinition> &value)
{
    convertersModel.setConverterDefinitions(value);
}

void ConverterConfigurationDialog::promptForResamplerLocation() {
    QString s(tr("Please locate the file: "));
    s.append(expectedMainConverter);

#if defined (Q_OS_WIN)
    QString filter = "*.exe";
#else
    QString filter = "";
#endif

    QString cp = QFileDialog::getOpenFileName(this, s, mainConverterPath,  filter);

    if(!cp.isNull()) {
        mainConverterPath = cp;
        if(mainConverterPath.lastIndexOf(expectedMainConverter, -1, Qt::CaseInsensitive) == -1) { // safeguard against wrong executable being configured
            mainConverterPath.clear();
            QMessageBox::warning(this, tr("Converter Location"), tr("That is not the right program!\n"), QMessageBox::Ok);
        }
    }
}

QString ConverterConfigurationDialog::getMainConverterPath() const
{
    return mainConverterPath;
}

void ConverterConfigurationDialog::setMainConverterPath(const QString &value)
{
    mainConverterPath = value;
}

QString ConverterConfigurationDialog::getExpectedMainConverter() const
{
    return expectedMainConverter;
}

void ConverterConfigurationDialog::setExpectedMainConverter(const QString &value)
{
    expectedMainConverter = value;
    // set tooltips
    mainConverterLocationLabel->setToolTip(QString{"Please enter the location of %1 in the box below.\n"
                                           "(%1 is the command-line audio converter that Ferocious was designed to work with.)"
                                           }.arg(expectedMainConverter));
    mainConverterLocationEdit->setToolTip(QString{"Location of %1\n"
                                          "Note: you can also use drag-and-drop, or the 'Browse' button"}.arg(expectedMainConverter));
    browseButton->setToolTip(QString{"Browse to location of %1"}.arg(expectedMainConverter));
    additionalConvertersLabel->setToolTip("Use the table below to cofigure additional converters for specialized file formats.");

}

void ConverterConfigurationDialog::onNewRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 0 )
        return;

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        converterDefinitions.insert(row, {});
        convertersModel.setConverterDefinitions(converterDefinitions);
    }
}

void ConverterConfigurationDialog::onEditRequested(const QModelIndex& modelIndex)
{
    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if (converterDefinitions.isEmpty())
        return;

    int row = modelIndex.row();

    if (row < 0)
        return;

    auto dlg = new ConverterConfigurationEditDialog(this);
    if(row < converterDefinitions.count()) {
        dlg->setConverterDefinition(converterDefinitions.at(row));
        int result = dlg->exec();
        if(result == QDialog::Accepted) {
            converterDefinitions[row] = dlg->getConverterDefinition();
            convertersModel.setConverterDefinitions(converterDefinitions);
        }
    }
}

void ConverterConfigurationDialog::onDeleteRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 0 )
        return;

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        converterDefinitions.removeAt(row);
        convertersModel.setConverterDefinitions(converterDefinitions);
    }
}

void ConverterConfigurationDialog::onCloneRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 0 )
        return;

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        converterDefinitions.insert(row, converterDefinitions.at(row));
        convertersModel.setConverterDefinitions(converterDefinitions);
    }
}

void ConverterConfigurationDialog::onMoveUpRequested(const QModelIndex& modelIndex)
{
    int row = modelIndex.row();

    if(row < 1 ) {
        return;
    }

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count()) {
        qSwap(converterDefinitions[row], converterDefinitions[row - 1]);
        convertersModel.setConverterDefinitions(converterDefinitions);
        tableView.selectRow(row - 1);
    }
}

void ConverterConfigurationDialog::onMoveDownRequested(const QModelIndex& modelIndex)
{

    int row = modelIndex.row();

    if(row < 0 ) {
        return;
    }

    QVector<ConverterDefinition> converterDefinitions = convertersModel.getConverterDefinitions();
    if(row < converterDefinitions.count() - 1) {
        qSwap(converterDefinitions[row], converterDefinitions[row + 1]);
        convertersModel.setConverterDefinitions(converterDefinitions);
        tableView.selectRow(row + 1);
    }
}

void ConverterConfigurationDialog::onRestoreDefaults() {
    setConverterDefinitions(ConverterDefinition::loadConverterDefinitions(":/converters.json"));
}
<|endoftext|>


<commit_before>#include "RTPSAsNonReliableSocketReader.hpp"
#include "RTPSAsNonReliableSocketWriter.hpp"
#include "RTPSAsReliableSocketReader.hpp"
#include "RTPSAsReliableSocketWriter.hpp"
#include "RTPSAsNonReliableWithRegistrationReader.hpp"
#include "RTPSAsNonReliableWithRegistrationWriter.hpp"
#include "RTPSAsReliableWithRegistrationReader.hpp"
#include "RTPSAsReliableWithRegistrationWriter.hpp"
#include "PubSubAsNonReliableHelloWorldReader.hpp"
#include "PubSubAsNonReliableHelloWorldWriter.hpp"
#include "PubSubAsReliableHelloWorldReader.hpp"
#include "PubSubAsReliableHelloWorldWriter.hpp"

#include <fastrtps/rtps/RTPSDomain.h>

#include <gtest/gtest.h>

class BlackboxEnvironment : public ::testing::Environment
{
    public:

        void SetUp() {}

        void TearDown()
        {
            eprosima::fastrtps::rtps::RTPSDomain::stopAll();
        }
};

TEST(BlackBox, RTPSAsNonReliableSocket)
{
    RTPSAsNonReliableSocketReader reader;
    RTPSAsNonReliableSocketWriter writer;
    std::string ip("239.255.1.4");
    const uint32_t port = 22222;
    const uint16_t nmsgs = 100;

    reader.init(ip, port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init(ip, port);

    if(!writer.isInitialized())
        return;

    for(unsigned int tries = 0; tries < 20; ++tries)
    {
        std::list<uint16_t> msgs = reader.getNonReceivedMessages();
        if(msgs.empty())
            break;

        writer.send(msgs);
        reader.block(*msgs.rbegin(), std::chrono::seconds(1));
    }

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, RTPSAsReliableSocket)
{
    RTPSAsReliableSocketReader reader;
    RTPSAsReliableSocketWriter writer;
    std::string ip("239.255.1.4");
    const uint32_t port = 7400;
    const uint16_t nmsgs = 100;

    reader.init(ip, port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init(ip, port);

    if(!writer.isInitialized())
        return;

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();

    writer.send(msgs);
    reader.block(*msgs.rbegin(), std::chrono::seconds(60));

    msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, RTPSAsNonReliableWithRegistration)
{
    RTPSAsNonReliableWithRegistrationReader reader;
    RTPSAsNonReliableWithRegistrationWriter writer;
    const uint32_t port = 22222;
    const uint16_t nmsgs = 100;

    reader.init(port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    for(unsigned int tries = 0; tries < 20; ++tries)
    {
        std::list<uint16_t> msgs = reader.getNonReceivedMessages();
        if(msgs.empty())
            break;

        writer.send(msgs);
        reader.block(*msgs.rbegin(), std::chrono::seconds(1));
    }

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, RTPSAsReliableWithRegistration)
{
    RTPSAsReliableWithRegistrationReader reader;
    RTPSAsReliableWithRegistrationWriter writer;
    const uint32_t port = 7400;
    const uint16_t nmsgs = 100;

    reader.init(port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    // Because its volatile the durability
    reader.waitDiscovery();

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();

    writer.send(msgs);
    reader.block(*msgs.rbegin(), std::chrono::seconds(60));

    msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, PubSubAsNonReliableHelloworld)
{
    PubSubAsNonReliableHelloWorldReader reader;
    PubSubAsNonReliableHelloWorldWriter writer;
    const uint16_t nmsgs = 100;

    reader.init(nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    for(unsigned int tries = 0; tries < 20; ++tries)
    {
        std::list<uint16_t> msgs = reader.getNonReceivedMessages();
        if(msgs.empty())
            break;

        writer.send(msgs);
        reader.block(*msgs.rbegin(), std::chrono::seconds(1));
    }

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, PubSubAsReliableHelloworld)
{
    PubSubAsReliableHelloWorldReader reader;
    PubSubAsReliableHelloWorldWriter writer;
    const uint16_t nmsgs = 100;

    reader.init(nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    // Because its volatile the durability
    reader.waitDiscovery();

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();

    writer.send(msgs);
    reader.block(*msgs.rbegin(), std::chrono::seconds(60));

    msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    testing::AddGlobalTestEnvironment(new BlackboxEnvironment);
    return RUN_ALL_TESTS();
}
<commit_msg>Fixed test on Windows in debug mode<commit_after>#include "RTPSAsNonReliableSocketReader.hpp"
#include "RTPSAsNonReliableSocketWriter.hpp"
#include "RTPSAsReliableSocketReader.hpp"
#include "RTPSAsReliableSocketWriter.hpp"
#include "RTPSAsNonReliableWithRegistrationReader.hpp"
#include "RTPSAsNonReliableWithRegistrationWriter.hpp"
#include "RTPSAsReliableWithRegistrationReader.hpp"
#include "RTPSAsReliableWithRegistrationWriter.hpp"
#include "PubSubAsNonReliableHelloWorldReader.hpp"
#include "PubSubAsNonReliableHelloWorldWriter.hpp"
#include "PubSubAsReliableHelloWorldReader.hpp"
#include "PubSubAsReliableHelloWorldWriter.hpp"

#include <fastrtps/rtps/RTPSDomain.h>

#include <gtest/gtest.h>

class BlackboxEnvironment : public ::testing::Environment
{
    public:

        void SetUp() {}

        void TearDown()
        {
            eprosima::fastrtps::rtps::RTPSDomain::stopAll();
        }
};

TEST(BlackBox, RTPSAsNonReliableSocket)
{
    RTPSAsNonReliableSocketReader reader;
    RTPSAsNonReliableSocketWriter writer;
    std::string ip("239.255.1.4");
    const uint32_t port = 22222;
    const uint16_t nmsgs = 100;

    reader.init(ip, port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init(ip, port);

    if(!writer.isInitialized())
        return;

    for(unsigned int tries = 0; tries < 20; ++tries)
    {
        std::list<uint16_t> msgs = reader.getNonReceivedMessages();
        if(msgs.empty())
            break;

        writer.send(msgs);
        reader.block(*msgs.rbegin(), std::chrono::seconds(1));
    }

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, RTPSAsReliableSocket)
{
    RTPSAsReliableSocketReader reader;
    RTPSAsReliableSocketWriter writer;
    std::string ip("239.255.1.4");
    const uint32_t port = 7400;
    const uint16_t nmsgs = 100;

    reader.init(ip, port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init(ip, port);

    if(!writer.isInitialized())
        return;

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();

    writer.send(msgs);
    reader.block(*msgs.rbegin(), std::chrono::seconds(60));

    msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, RTPSAsNonReliableWithRegistration)
{
    RTPSAsNonReliableWithRegistrationReader reader;
    RTPSAsNonReliableWithRegistrationWriter writer;
    const uint32_t port = 22222;
    const uint16_t nmsgs = 100;

    reader.init(port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    for(unsigned int tries = 0; tries < 20; ++tries)
    {
        std::list<uint16_t> msgs = reader.getNonReceivedMessages();
        if(msgs.empty())
            break;

        writer.send(msgs);
        reader.block(*msgs.rbegin(), std::chrono::seconds(1));
    }

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, RTPSAsReliableWithRegistration)
{
    RTPSAsReliableWithRegistrationReader reader;
    RTPSAsReliableWithRegistrationWriter writer;
    const uint32_t port = 7400;
    const uint16_t nmsgs = 100;

    reader.init(port, nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    // Because its volatile the durability
    reader.waitDiscovery();

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();

    writer.send(msgs);
    reader.block(*msgs.rbegin(), std::chrono::seconds(60));

    msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, PubSubAsNonReliableHelloworld)
{
    PubSubAsNonReliableHelloWorldReader reader;
    PubSubAsNonReliableHelloWorldWriter writer;
    const uint16_t nmsgs = 100;

    reader.init(nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    for(unsigned int tries = 0; tries < 20; ++tries)
    {
        std::list<uint16_t> msgs = reader.getNonReceivedMessages();
        if(msgs.empty())
            break;

        writer.send(msgs);
        reader.block(*msgs.rbegin(), std::chrono::seconds(1));
    }

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

TEST(BlackBox, PubSubAsReliableHelloworld)
{
    PubSubAsReliableHelloWorldReader reader;
    PubSubAsReliableHelloWorldWriter writer;
    const uint16_t nmsgs = 100;

    reader.init(nmsgs);

    if(!reader.isInitialized())
        return;

    writer.init();

    if(!writer.isInitialized())
        return;

    // Because its volatile the durability
    reader.waitDiscovery();

    std::list<uint16_t> msgs = reader.getNonReceivedMessages();

    writer.send(msgs);
    reader.block(*msgs.rbegin(), std::chrono::seconds(60));

    msgs = reader.getNonReceivedMessages();
    if(msgs.size() != 0)
    {
        std::cout << "Samples not received:";
        for(std::list<uint16_t>::iterator it = msgs.begin(); it != msgs.end(); ++it)
            std::cout << " " << *it << " ";
        std::cout << std::endl;
    }
    ASSERT_EQ(msgs.size(), 0);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    testing::AddGlobalTestEnvironment(new BlackboxEnvironment);
#if defined(WIN32) && defined(_DEBUG)
    eprosima::Log::setVerbosity(eprosima::LOG_VERBOSITY_LVL::VERB_ERROR);
#endif
    return RUN_ALL_TESTS();
}
<|endoftext|>