Browse Source

添加plog日志库

leon 4 tuần trước cách đây
mục cha
commit
e8ee4536f9
34 tập tin đã thay đổi với 3192 bổ sung2 xóa
  1. 4 1
      .vscode/settings.json
  2. 47 0
      src/3rd/plog/plog/Appenders/AndroidAppender.h
  3. 23 0
      src/3rd/plog/plog/Appenders/ArduinoAppender.h
  4. 108 0
      src/3rd/plog/plog/Appenders/ColorConsoleAppender.h
  5. 83 0
      src/3rd/plog/plog/Appenders/ConsoleAppender.h
  6. 16 0
      src/3rd/plog/plog/Appenders/DebugOutputAppender.h
  7. 42 0
      src/3rd/plog/plog/Appenders/DynamicAppender.h
  8. 117 0
      src/3rd/plog/plog/Appenders/EventLogAppender.h
  9. 16 0
      src/3rd/plog/plog/Appenders/IAppender.h
  10. 148 0
      src/3rd/plog/plog/Appenders/RollingFileAppender.h
  11. 44 0
      src/3rd/plog/plog/Converters/NativeEOLConverter.h
  12. 28 0
      src/3rd/plog/plog/Converters/UTF8Converter.h
  13. 57 0
      src/3rd/plog/plog/Formatters/CsvFormatter.h
  14. 23 0
      src/3rd/plog/plog/Formatters/FuncMessageFormatter.h
  15. 23 0
      src/3rd/plog/plog/Formatters/MessageOnlyFormatter.h
  16. 36 0
      src/3rd/plog/plog/Formatters/TxtFormatter.h
  17. 40 0
      src/3rd/plog/plog/Helpers/AscDump.h
  18. 79 0
      src/3rd/plog/plog/Helpers/HexDump.h
  19. 24 0
      src/3rd/plog/plog/Helpers/PrintVar.h
  20. 17 0
      src/3rd/plog/plog/Init.h
  21. 22 0
      src/3rd/plog/plog/Initializers/ConsoleInitializer.h
  22. 80 0
      src/3rd/plog/plog/Initializers/RollingFileInitializer.h
  23. 202 0
      src/3rd/plog/plog/Log.h
  24. 84 0
      src/3rd/plog/plog/Logger.h
  25. 465 0
      src/3rd/plog/plog/Record.h
  26. 61 0
      src/3rd/plog/plog/Severity.h
  27. 635 0
      src/3rd/plog/plog/Util.h
  28. 175 0
      src/3rd/plog/plog/WinApi.h
  29. 474 0
      src/graph/graph.hpp
  30. 2 0
      src/infer/trt/yolo/yolo.hpp
  31. 1 1
      src/nodes/analyze/analyzeNode.hpp
  32. 3 0
      src/nodes/analyze/function/cross.cpp
  33. 10 0
      src/nodes/analyze/function/cross.hpp
  34. 3 0
      src/nodes/infer/inferNode.cpp

+ 4 - 1
.vscode/settings.json

@@ -60,6 +60,9 @@
         "iterator": "cpp",
         "utility": "cpp",
         "chrono": "cpp",
-        "set": "cpp"
+        "set": "cpp",
+        "any": "cpp",
+        "span": "cpp",
+        "valarray": "cpp"
     }
 }

+ 47 - 0
src/3rd/plog/plog/Appenders/AndroidAppender.h

@@ -0,0 +1,47 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <android/log.h>
+
+namespace plog
+{
+    template<class Formatter>
+    class PLOG_LINKAGE_HIDDEN AndroidAppender : public IAppender
+    {
+    public:
+        AndroidAppender(const char* tag) : m_tag(tag)
+        {
+        }
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            std::string str = Formatter::format(record);
+
+            __android_log_print(toPriority(record.getSeverity()), m_tag, "%s", str.c_str());
+        }
+
+    private:
+        static android_LogPriority toPriority(Severity severity)
+        {
+            switch (severity)
+            {
+            case fatal:
+                return ANDROID_LOG_FATAL;
+            case error:
+                return ANDROID_LOG_ERROR;
+            case warning:
+                return ANDROID_LOG_WARN;
+            case info:
+                return ANDROID_LOG_INFO;
+            case debug:
+                return ANDROID_LOG_DEBUG;
+            case verbose:
+                return ANDROID_LOG_VERBOSE;
+            default:
+                return ANDROID_LOG_UNKNOWN;
+            }
+        }
+
+    private:
+        const char* const m_tag;
+    };
+}

+ 23 - 0
src/3rd/plog/plog/Appenders/ArduinoAppender.h

@@ -0,0 +1,23 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <Arduino.h>
+
+namespace plog
+{
+    template<class Formatter>
+    class PLOG_LINKAGE_HIDDEN ArduinoAppender : public IAppender
+    {
+    public:
+        ArduinoAppender(Stream &stream) : m_stream(stream)
+        {
+        }
+
+        virtual void write(const Record &record) PLOG_OVERRIDE
+        {
+            m_stream.print(Formatter::format(record).c_str());
+        }
+
+    private:
+        Stream &m_stream;
+    };
+}

+ 108 - 0
src/3rd/plog/plog/Appenders/ColorConsoleAppender.h

@@ -0,0 +1,108 @@
+#pragma once
+#include <plog/Appenders/ConsoleAppender.h>
+#include <plog/WinApi.h>
+
+namespace plog
+{
+    template<class Formatter>
+    class PLOG_LINKAGE_HIDDEN ColorConsoleAppender : public ConsoleAppender<Formatter>
+    {
+    public:
+#ifdef _WIN32
+#   ifdef _MSC_VER
+#       pragma warning(suppress: 26812) //  Prefer 'enum class' over 'enum'
+#   endif
+        ColorConsoleAppender(OutputStream outStream = streamStdOut)
+            : ConsoleAppender<Formatter>(outStream)
+            , m_originalAttr()
+        {
+            if (this->m_isatty)
+            {
+                CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+                GetConsoleScreenBufferInfo(this->m_outputHandle, &csbiInfo);
+
+                m_originalAttr = csbiInfo.wAttributes;
+            }
+        }
+#else
+        ColorConsoleAppender(OutputStream outStream = streamStdOut)
+            : ConsoleAppender<Formatter>(outStream)
+        {}
+#endif
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            util::nstring str = Formatter::format(record);
+            util::MutexLock lock(this->m_mutex);
+
+            setColor(record.getSeverity());
+            this->writestr(str);
+            resetColor();
+        }
+
+    protected:
+        void setColor(Severity severity)
+        {
+            if (this->m_isatty)
+            {
+                switch (severity)
+                {
+#ifdef _WIN32
+                case fatal:
+                    SetConsoleTextAttribute(this->m_outputHandle, foreground::kRed | foreground::kGreen | foreground::kBlue | foreground::kIntensity | background::kRed); // white on red background
+                    break;
+
+                case error:
+                    SetConsoleTextAttribute(this->m_outputHandle, static_cast<WORD>(foreground::kRed | foreground::kIntensity | (m_originalAttr & 0xf0))); // red
+                    break;
+
+                case warning:
+                    SetConsoleTextAttribute(this->m_outputHandle, static_cast<WORD>(foreground::kRed | foreground::kGreen | foreground::kIntensity | (m_originalAttr & 0xf0))); // yellow
+                    break;
+
+                case debug:
+                case verbose:
+                    SetConsoleTextAttribute(this->m_outputHandle, static_cast<WORD>(foreground::kGreen | foreground::kBlue | foreground::kIntensity | (m_originalAttr & 0xf0))); // cyan
+                    break;
+#else
+                case fatal:
+                    this->m_outputStream << "\x1B[97m\x1B[41m"; // white on red background
+                    break;
+
+                case error:
+                    this->m_outputStream << "\x1B[91m"; // red
+                    break;
+
+                case warning:
+                    this->m_outputStream << "\x1B[93m"; // yellow
+                    break;
+
+                case debug:
+                case verbose:
+                    this->m_outputStream << "\x1B[96m"; // cyan
+                    break;
+#endif
+                default:
+                    break;
+                }
+            }
+        }
+
+        void resetColor()
+        {
+            if (this->m_isatty)
+            {
+#ifdef _WIN32
+                SetConsoleTextAttribute(this->m_outputHandle, m_originalAttr);
+#else
+                this->m_outputStream << "\x1B[0m\x1B[0K";
+#endif
+            }
+        }
+
+    private:
+#ifdef _WIN32
+        WORD   m_originalAttr;
+#endif
+    };
+}

+ 83 - 0
src/3rd/plog/plog/Appenders/ConsoleAppender.h

@@ -0,0 +1,83 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <plog/Util.h>
+#include <plog/WinApi.h>
+#include <iostream>
+
+namespace plog
+{
+    enum OutputStream
+    {
+        streamStdOut,
+        streamStdErr
+    };
+
+    template<class Formatter>
+    class PLOG_LINKAGE_HIDDEN ConsoleAppender : public IAppender
+    {
+    public:
+#ifdef _WIN32
+#   ifdef _MSC_VER
+#       pragma warning(suppress: 26812) //  Prefer 'enum class' over 'enum'
+#   endif
+        ConsoleAppender(OutputStream outStream = streamStdOut)
+            : m_isatty(!!_isatty(_fileno(outStream == streamStdOut ? stdout : stderr)))
+            , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr)
+            , m_outputHandle()
+        {
+            if (m_isatty)
+            {
+                m_outputHandle = GetStdHandle(outStream == streamStdOut ? stdHandle::kOutput : stdHandle::kErrorOutput);
+            }
+        }
+#else
+        ConsoleAppender(OutputStream outStream = streamStdOut)
+            : m_isatty(!!isatty(fileno(outStream == streamStdOut ? stdout : stderr)))
+            , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr)
+        {}
+#endif
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            util::nstring str = Formatter::format(record);
+            util::MutexLock lock(m_mutex);
+
+            writestr(str);
+        }
+
+    protected:
+        void writestr(const util::nstring& str)
+        {
+#ifdef _WIN32
+            if (m_isatty)
+            {
+                const std::wstring& wstr = util::toWide(str);
+                WriteConsoleW(m_outputHandle, wstr.c_str(), static_cast<DWORD>(wstr.size()), NULL, NULL);
+            }
+            else
+            {
+#   if PLOG_CHAR_IS_UTF8
+                m_outputStream << str << std::flush;
+#   else
+                m_outputStream << util::toNarrow(str, codePage::kActive) << std::flush;
+#   endif
+            }
+#else
+            m_outputStream << str << std::flush;
+#endif
+        }
+
+    private:
+#ifdef __BORLANDC__
+        static int _isatty(int fd) { return ::isatty(fd); }
+#endif
+
+    protected:
+        util::Mutex m_mutex;
+        const bool  m_isatty;
+        std::ostream& m_outputStream;
+#ifdef _WIN32
+        HANDLE      m_outputHandle;
+#endif
+    };
+}

+ 16 - 0
src/3rd/plog/plog/Appenders/DebugOutputAppender.h

@@ -0,0 +1,16 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <plog/WinApi.h>
+
+namespace plog
+{
+    template<class Formatter>
+    class PLOG_LINKAGE_HIDDEN DebugOutputAppender : public IAppender
+    {
+    public:
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            OutputDebugStringW(util::toWide(Formatter::format(record)).c_str());
+        }
+    };
+}

+ 42 - 0
src/3rd/plog/plog/Appenders/DynamicAppender.h

@@ -0,0 +1,42 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <set>
+
+namespace plog
+{
+    class PLOG_LINKAGE_HIDDEN DynamicAppender : public IAppender
+    {
+    public:
+        DynamicAppender& addAppender(IAppender* appender)
+        {
+            assert(appender != this);
+
+            util::MutexLock lock(m_mutex);
+            m_appenders.insert(appender);
+
+            return *this;
+        }
+
+        DynamicAppender& removeAppender(IAppender* appender)
+        {
+            util::MutexLock lock(m_mutex);
+            m_appenders.erase(appender);
+
+            return *this;
+        }
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            util::MutexLock lock(m_mutex);
+
+            for (std::set<IAppender*>::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it)
+            {
+                (*it)->write(record);
+            }
+        }
+
+    private:
+        mutable util::Mutex     m_mutex;
+        std::set<IAppender*>    m_appenders;
+    };
+}

+ 117 - 0
src/3rd/plog/plog/Appenders/EventLogAppender.h

@@ -0,0 +1,117 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <plog/WinApi.h>
+
+namespace plog
+{
+    template <class Formatter>
+    class PLOG_LINKAGE_HIDDEN EventLogAppender : public IAppender
+    {
+    public:
+        EventLogAppender(const util::nchar* sourceName) : m_eventSource(RegisterEventSourceW(NULL, util::toWide(sourceName).c_str()))
+        {
+        }
+
+        ~EventLogAppender()
+        {
+            DeregisterEventSource(m_eventSource);
+        }
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            util::nstring str = Formatter::format(record);
+
+            write(record.getSeverity(), util::toWide(str).c_str());
+        }
+
+    private:
+        void write(Severity severity, const wchar_t* str)
+        {
+            const wchar_t* logMessagePtr[] = { str };
+
+            ReportEventW(m_eventSource, logSeverityToType(severity), static_cast<WORD>(severity), 0, NULL, 1, 0, logMessagePtr, NULL);
+        }
+
+        static WORD logSeverityToType(plog::Severity severity)
+        {
+            switch (severity)
+            {
+            case plog::fatal:
+            case plog::error:
+                return eventLog::kErrorType;
+
+            case plog::warning:
+                return eventLog::kWarningType;
+
+            case plog::info:
+            case plog::debug:
+            case plog::verbose:
+            default:
+                return eventLog::kInformationType;
+            }
+        }
+
+    private:
+        HANDLE m_eventSource;
+    };
+
+    class EventLogAppenderRegistry
+    {
+    public:
+        static bool add(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application"))
+        {
+            std::wstring logKeyName;
+            std::wstring sourceKeyName;
+            getKeyNames(sourceName, logName, sourceKeyName, logKeyName);
+
+            HKEY sourceKey;
+            if (0 != RegCreateKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, NULL, 0, regSam::kSetValue, NULL, &sourceKey, NULL))
+            {
+                return false;
+            }
+
+            const DWORD kTypesSupported = eventLog::kErrorType | eventLog::kWarningType | eventLog::kInformationType;
+            RegSetValueExW(sourceKey, L"TypesSupported", 0, regType::kDword, reinterpret_cast<const BYTE*>(&kTypesSupported), sizeof(kTypesSupported));
+
+            const wchar_t kEventMessageFile[] = L"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\EventLogMessages.dll;%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll";
+            RegSetValueExW(sourceKey, L"EventMessageFile", 0, regType::kExpandSz, reinterpret_cast<const BYTE*>(kEventMessageFile), sizeof(kEventMessageFile) - sizeof(*kEventMessageFile));
+
+            RegCloseKey(sourceKey);
+            return true;
+        }
+
+        static bool exists(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application"))
+        {
+            std::wstring logKeyName;
+            std::wstring sourceKeyName;
+            getKeyNames(sourceName, logName, sourceKeyName, logKeyName);
+
+            HKEY sourceKey;
+            if (0 != RegOpenKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, regSam::kQueryValue, &sourceKey))
+            {
+                return false;
+            }
+
+            RegCloseKey(sourceKey);
+            return true;
+        }
+
+        static void remove(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application"))
+        {
+            std::wstring logKeyName;
+            std::wstring sourceKeyName;
+            getKeyNames(sourceName, logName, sourceKeyName, logKeyName);
+
+            RegDeleteKeyW(hkey::kLocalMachine, sourceKeyName.c_str());
+            RegDeleteKeyW(hkey::kLocalMachine, logKeyName.c_str());
+        }
+
+    private:
+        static void getKeyNames(const util::nchar* sourceName, const util::nchar* logName, std::wstring& sourceKeyName, std::wstring& logKeyName)
+        {
+            const std::wstring kPrefix = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
+            logKeyName = kPrefix + util::toWide(logName);
+            sourceKeyName = logKeyName + L"\\" + util::toWide(sourceName);
+        }
+    };
+}

+ 16 - 0
src/3rd/plog/plog/Appenders/IAppender.h

@@ -0,0 +1,16 @@
+#pragma once
+#include <plog/Record.h>
+#include <plog/Util.h>
+
+namespace plog
+{
+    class PLOG_LINKAGE IAppender
+    {
+    public:
+        virtual ~IAppender()
+        {
+        }
+
+        virtual void write(const Record& record) = 0;
+    };
+}

+ 148 - 0
src/3rd/plog/plog/Appenders/RollingFileAppender.h

@@ -0,0 +1,148 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <plog/Converters/UTF8Converter.h>
+#include <plog/Converters/NativeEOLConverter.h>
+#include <plog/Util.h>
+#include <algorithm>
+
+namespace plog
+{
+    template<class Formatter, class Converter = NativeEOLConverter<UTF8Converter> >
+    class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender
+    {
+    public:
+        RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+            : m_fileSize()
+            , m_maxFileSize()
+            , m_maxFiles(maxFiles)
+            , m_firstWrite(true)
+        {
+            setFileName(fileName);
+            setMaxFileSize(maxFileSize);
+        }
+
+#if defined(_WIN32) && !PLOG_CHAR_IS_UTF8
+        RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+            : m_fileSize()
+            , m_maxFileSize()
+            , m_maxFiles(maxFiles)
+            , m_firstWrite(true)
+        {
+            setFileName(fileName);
+            setMaxFileSize(maxFileSize);
+        }
+#endif
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            util::MutexLock lock(m_mutex);
+
+            if (m_firstWrite)
+            {
+                openLogFile();
+                m_firstWrite = false;
+            }
+            else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast<size_t>(-1) != m_fileSize)
+            {
+                rollLogFiles();
+            }
+
+            size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record)));
+
+            if (static_cast<size_t>(-1) != bytesWritten)
+            {
+                m_fileSize += bytesWritten;
+            }
+        }
+
+        void setFileName(const util::nchar* fileName)
+        {
+            util::MutexLock lock(m_mutex);
+
+            util::splitFileName(fileName, m_fileNameNoExt, m_fileExt);
+
+            m_file.close();
+            m_firstWrite = true;
+        }
+
+#if defined(_WIN32) && !PLOG_CHAR_IS_UTF8
+        void setFileName(const char* fileName)
+        {
+            setFileName(util::toWide(fileName).c_str());
+        }
+#endif
+
+        void setMaxFiles(int maxFiles)
+        {
+            m_maxFiles = maxFiles;
+        }
+
+        void setMaxFileSize(size_t maxFileSize)
+        {
+            m_maxFileSize = (std::max)(maxFileSize, static_cast<size_t>(1000)); // set a lower limit for the maxFileSize
+        }
+
+        void rollLogFiles()
+        {
+            m_file.close();
+
+            util::nstring lastFileName = buildFileName(m_maxFiles - 1);
+            util::File::unlink(lastFileName);
+
+            for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber)
+            {
+                util::nstring currentFileName = buildFileName(fileNumber);
+                util::nstring nextFileName = buildFileName(fileNumber + 1);
+
+                util::File::rename(currentFileName, nextFileName);
+            }
+
+            openLogFile();
+            m_firstWrite = false;
+        }
+
+    private:
+        void openLogFile()
+        {
+            m_fileSize = m_file.open(buildFileName());
+
+            if (0 == m_fileSize)
+            {
+                size_t bytesWritten = m_file.write(Converter::header(Formatter::header()));
+
+                if (static_cast<size_t>(-1) != bytesWritten)
+                {
+                    m_fileSize += bytesWritten;
+                }
+            }
+        }
+
+        util::nstring buildFileName(int fileNumber = 0)
+        {
+            util::nostringstream ss;
+            ss << m_fileNameNoExt;
+
+            if (fileNumber > 0)
+            {
+                ss << '.' << fileNumber;
+            }
+
+            if (!m_fileExt.empty())
+            {
+                ss << '.' << m_fileExt;
+            }
+
+            return ss.str();
+        }
+
+    private:
+        util::Mutex     m_mutex;
+        util::File      m_file;
+        size_t          m_fileSize;
+        size_t          m_maxFileSize;
+        int             m_maxFiles;
+        util::nstring   m_fileExt;
+        util::nstring   m_fileNameNoExt;
+        bool            m_firstWrite;
+    };
+}

+ 44 - 0
src/3rd/plog/plog/Converters/NativeEOLConverter.h

@@ -0,0 +1,44 @@
+#pragma once
+#include <plog/Converters/UTF8Converter.h>
+#include <plog/Util.h>
+
+namespace plog
+{
+    template<class NextConverter = UTF8Converter>
+    class NativeEOLConverter : public NextConverter
+    {
+#ifdef _WIN32
+    public:
+        static std::string header(const util::nstring& str)
+        {
+            return NextConverter::header(fixLineEndings(str));
+        }
+
+        static std::string convert(const util::nstring& str)
+        {
+            return NextConverter::convert(fixLineEndings(str));
+        }
+
+    private:
+        static util::nstring fixLineEndings(const util::nstring& str)
+        {
+            util::nstring output;
+            output.reserve(str.length() * 2); // the worst case requires 2x chars
+
+            for (size_t i = 0; i < str.size(); ++i)
+            {
+                util::nchar ch = str[i];
+
+                if (ch == PLOG_NSTR('\n'))
+                {
+                    output.push_back(PLOG_NSTR('\r'));
+                }
+
+                output.push_back(ch);
+            }
+
+            return output;
+        }
+#endif
+    };
+}

+ 28 - 0
src/3rd/plog/plog/Converters/UTF8Converter.h

@@ -0,0 +1,28 @@
+#pragma once
+#include <plog/Util.h>
+
+namespace plog
+{
+    class UTF8Converter
+    {
+    public:
+        static std::string header(const util::nstring& str)
+        {
+            const char kBOM[] = "\xEF\xBB\xBF";
+
+            return std::string(kBOM) + convert(str);
+        }
+
+#if PLOG_CHAR_IS_UTF8
+        static const std::string& convert(const util::nstring& str)
+        {
+            return str;
+        }
+#else
+        static std::string convert(const util::nstring& str)
+        {
+            return util::toNarrow(str, codePage::kUTF8);
+        }
+#endif
+    };
+}

+ 57 - 0
src/3rd/plog/plog/Formatters/CsvFormatter.h

@@ -0,0 +1,57 @@
+#pragma once
+#include <plog/Record.h>
+#include <plog/Util.h>
+#include <iomanip>
+
+namespace plog
+{
+    template<bool useUtcTime>
+    class CsvFormatterImpl
+    {
+    public:
+        static util::nstring header()
+        {
+            return PLOG_NSTR("Date;Time;Severity;TID;This;Function;Message\n");
+        }
+
+        static util::nstring format(const Record& record)
+        {
+            tm t;
+            useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time);
+
+            util::nostringstream ss;
+            ss << t.tm_year + 1900 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(";");
+            ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast<int> (record.getTime().millitm) << PLOG_NSTR(";");
+            ss << severityToString(record.getSeverity()) << PLOG_NSTR(";");
+            ss << record.getTid() << PLOG_NSTR(";");
+            ss << record.getObject() << PLOG_NSTR(";");
+            ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(";");
+
+            util::nstring message = record.getMessage();
+
+            if (message.size() > kMaxMessageSize)
+            {
+                message.resize(kMaxMessageSize);
+                message.append(PLOG_NSTR("..."));
+            }
+
+            util::nistringstream split(message);
+            util::nstring token;
+
+            while (!split.eof())
+            {
+                std::getline(split, token, PLOG_NSTR('"'));
+                ss << PLOG_NSTR("\"") << token << PLOG_NSTR("\"");
+            }
+
+            ss << PLOG_NSTR("\n");
+
+            return ss.str();
+        }
+
+        static const size_t kMaxMessageSize = 32000;
+    };
+
+    class CsvFormatter : public CsvFormatterImpl<false> {};
+    class CsvFormatterUtcTime : public CsvFormatterImpl<true> {};
+}

+ 23 - 0
src/3rd/plog/plog/Formatters/FuncMessageFormatter.h

@@ -0,0 +1,23 @@
+#pragma once
+#include <plog/Record.h>
+#include <plog/Util.h>
+
+namespace plog
+{
+    class FuncMessageFormatter
+    {
+    public:
+        static util::nstring header()
+        {
+            return util::nstring();
+        }
+
+        static util::nstring format(const Record& record)
+        {
+            util::nostringstream ss;
+            ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(": ") << record.getMessage() << PLOG_NSTR("\n");
+
+            return ss.str();
+        }
+    };
+}

+ 23 - 0
src/3rd/plog/plog/Formatters/MessageOnlyFormatter.h

@@ -0,0 +1,23 @@
+#pragma once
+#include <plog/Record.h>
+#include <plog/Util.h>
+
+namespace plog
+{
+    class MessageOnlyFormatter
+    {
+    public:
+        static util::nstring header()
+        {
+            return util::nstring();
+        }
+
+        static util::nstring format(const Record& record)
+        {
+            util::nostringstream ss;
+            ss << record.getMessage() << PLOG_NSTR("\n");
+
+            return ss.str();
+        }
+    };
+}

+ 36 - 0
src/3rd/plog/plog/Formatters/TxtFormatter.h

@@ -0,0 +1,36 @@
+#pragma once
+#include <plog/Record.h>
+#include <plog/Util.h>
+#include <iomanip>
+
+namespace plog
+{
+    template<bool useUtcTime>
+    class TxtFormatterImpl
+    {
+    public:
+        static util::nstring header()
+        {
+            return util::nstring();
+        }
+
+        static util::nstring format(const Record& record)
+        {
+            tm t;
+            useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time);
+
+            util::nostringstream ss;
+            ss << t.tm_year + 1900 << "-" << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("-") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(" ");
+            ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast<int> (record.getTime().millitm) << PLOG_NSTR(" ");
+            ss << std::setfill(PLOG_NSTR(' ')) << std::setw(5) << std::left << severityToString(record.getSeverity()) << PLOG_NSTR(" ");
+            ss << PLOG_NSTR("[") << record.getTid() << PLOG_NSTR("] ");
+            ss << PLOG_NSTR("[") << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR("] ");
+            ss << record.getMessage() << PLOG_NSTR("\n");
+
+            return ss.str();
+        }
+    };
+
+    class TxtFormatter : public TxtFormatterImpl<false> {};
+    class TxtFormatterUtcTime : public TxtFormatterImpl<true> {};
+}

+ 40 - 0
src/3rd/plog/plog/Helpers/AscDump.h

@@ -0,0 +1,40 @@
+#pragma once
+#include <plog/Util.h>
+#include <cctype>
+
+namespace plog
+{
+    class AscDump
+    {
+    public:
+        AscDump(const void* ptr, size_t size)
+            : m_ptr(static_cast<const char*>(ptr))
+            , m_size(size)
+        {
+        }
+
+        friend util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump);
+
+    private:
+        const char* m_ptr;
+        size_t m_size;
+    };
+
+    inline util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump)
+    {
+        for (size_t i = 0; i < ascDump.m_size; ++i)
+        {
+            stream << (std::isprint(ascDump.m_ptr[i]) ? ascDump.m_ptr[i] : '.');
+        }
+
+        return stream;
+    }
+
+    inline AscDump ascdump(const void* ptr, size_t size) { return AscDump(ptr, size); }
+
+    template<class Container>
+    inline AscDump ascdump(const Container& container) { return AscDump(container.data(), container.size() * sizeof(*container.data())); }
+
+    template<class T, size_t N>
+    inline AscDump ascdump(const T (&arr)[N]) { return AscDump(arr, N * sizeof(*arr)); }
+}

+ 79 - 0
src/3rd/plog/plog/Helpers/HexDump.h

@@ -0,0 +1,79 @@
+#pragma once
+#include <plog/Util.h>
+#include <iomanip>
+
+namespace plog
+{
+    class HexDump
+    {
+    public:
+        HexDump(const void* ptr, size_t size)
+            : m_ptr(static_cast<const unsigned char*>(ptr))
+            , m_size(size)
+            , m_group(8)
+            , m_digitSeparator(" ")
+            , m_groupSeparator("  ")
+        {
+        }
+
+        HexDump& group(size_t group)
+        {
+            m_group = group;
+            return *this;
+        }
+
+        HexDump& separator(const char* digitSeparator)
+        {
+            m_digitSeparator = digitSeparator;
+            return *this;
+        }
+
+        HexDump& separator(const char* digitSeparator, const char* groupSeparator)
+        {
+            m_digitSeparator = digitSeparator;
+            m_groupSeparator = groupSeparator;
+            return *this;
+        }
+
+        friend util::nostringstream& operator<<(util::nostringstream& stream, const HexDump&);
+
+    private:
+        const unsigned char* m_ptr;
+        size_t m_size;
+        size_t m_group;
+        const char* m_digitSeparator;
+        const char* m_groupSeparator;
+    };
+
+    inline util::nostringstream& operator<<(util::nostringstream& stream, const HexDump& hexDump)
+    {
+        stream << std::hex << std::setfill(PLOG_NSTR('0'));
+
+        for (size_t i = 0; i < hexDump.m_size;)
+        {
+            stream << std::setw(2) << static_cast<int>(hexDump.m_ptr[i]);
+
+            if (++i < hexDump.m_size)
+            {
+                if (hexDump.m_group > 0 && i % hexDump.m_group == 0)
+                {
+                    stream << hexDump.m_groupSeparator;
+                }
+                else
+                {
+                    stream << hexDump.m_digitSeparator;
+                }
+            }
+        }
+
+        return stream;
+    }
+
+    inline HexDump hexdump(const void* ptr, size_t size) { return HexDump(ptr, size); }
+
+    template<class Container>
+    inline HexDump hexdump(const Container& container) { return HexDump(container.data(), container.size() * sizeof(*container.data())); }
+
+    template<class T, size_t N>
+    inline HexDump hexdump(const T (&arr)[N]) { return HexDump(arr, N * sizeof(*arr)); }
+}

+ 24 - 0
src/3rd/plog/plog/Helpers/PrintVar.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#define PLOG_IMPL_PRINT_VAR_1(a1) #a1 ": " << a1
+#define PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_1(a1) PLOG_IMPL_PRINT_VAR_TAIL(a2)
+#define PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_TAIL(a3)
+#define PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_TAIL(a4)
+#define PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_TAIL(a5)
+#define PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_TAIL(a6)
+#define PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_TAIL(a7)
+#define PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_TAIL(a8)
+#define PLOG_IMPL_PRINT_VAR_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_TAIL(a9)
+#define PLOG_IMPL_PRINT_VAR_TAIL(a) << ", " PLOG_IMPL_PRINT_VAR_1(a)
+
+#define PLOG_IMPL_PRINT_VAR_EXPAND(x) x
+
+#ifdef __GNUC__
+#pragma GCC system_header // disable warning: variadic macros are a C99 feature
+#endif
+
+#define PLOG_IMPL_PRINT_VAR_GET_MACRO(x1, x2, x3, x4, x5, x6, x7, x8, x9, NAME, ...) NAME
+
+#define PLOG_PRINT_VAR(...) PLOG_IMPL_PRINT_VAR_EXPAND(PLOG_IMPL_PRINT_VAR_GET_MACRO(__VA_ARGS__,\
+    PLOG_IMPL_PRINT_VAR_9, PLOG_IMPL_PRINT_VAR_8, PLOG_IMPL_PRINT_VAR_7, PLOG_IMPL_PRINT_VAR_6, PLOG_IMPL_PRINT_VAR_5,\
+    PLOG_IMPL_PRINT_VAR_4, PLOG_IMPL_PRINT_VAR_3, PLOG_IMPL_PRINT_VAR_2, PLOG_IMPL_PRINT_VAR_1, UNUSED)(__VA_ARGS__))

+ 17 - 0
src/3rd/plog/plog/Init.h

@@ -0,0 +1,17 @@
+#pragma once
+#include <plog/Logger.h>
+
+namespace plog
+{
+    template<int instanceId>
+    PLOG_LINKAGE_HIDDEN inline Logger<instanceId>& init(Severity maxSeverity = none, IAppender* appender = NULL)
+    {
+        static Logger<instanceId> logger(maxSeverity);
+        return appender ? logger.addAppender(appender) : logger;
+    }
+
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity = none, IAppender* appender = NULL)
+    {
+        return init<PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, appender);
+    }
+}

+ 22 - 0
src/3rd/plog/plog/Initializers/ConsoleInitializer.h

@@ -0,0 +1,22 @@
+#pragma once
+#include <plog/Appenders/ColorConsoleAppender.h>
+#include <plog/Init.h>
+
+namespace plog
+{
+    //////////////////////////////////////////////////////////////////////////
+    // ColorConsoleAppender with any Formatter
+
+    template<class Formatter, int instanceId>
+    PLOG_LINKAGE_HIDDEN inline Logger<instanceId>& init(Severity maxSeverity, OutputStream outputStream)
+    {
+        static ColorConsoleAppender<Formatter> appender(outputStream);
+        return init<instanceId>(maxSeverity, &appender);
+    }
+
+    template<class Formatter>
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, OutputStream outputStream)
+    {
+        return init<Formatter, PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, outputStream);
+    }
+}

+ 80 - 0
src/3rd/plog/plog/Initializers/RollingFileInitializer.h

@@ -0,0 +1,80 @@
+#pragma once
+#include <plog/Appenders/RollingFileAppender.h>
+#include <plog/Formatters/TxtFormatter.h>
+#include <plog/Formatters/CsvFormatter.h>
+#include <plog/Init.h>
+#include <cstring>
+
+namespace plog
+{
+    //////////////////////////////////////////////////////////////////////////
+    // RollingFileAppender with any Formatter
+
+    template<class Formatter, int instanceId>
+    PLOG_LINKAGE_HIDDEN inline Logger<instanceId>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        static RollingFileAppender<Formatter> rollingFileAppender(fileName, maxFileSize, maxFiles);
+        return init<instanceId>(maxSeverity, &rollingFileAppender);
+    }
+
+    template<class Formatter>
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return init<Formatter, PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // RollingFileAppender with TXT/CSV chosen by file extension
+
+    namespace
+    {
+        inline bool isCsv(const util::nchar* fileName)
+        {
+            const util::nchar* dot = util::findExtensionDot(fileName);
+#if PLOG_CHAR_IS_UTF8
+            return dot && 0 == std::strcmp(dot, ".csv");
+#else
+            return dot && 0 == std::wcscmp(dot, L".csv");
+#endif
+        }
+    }
+
+    template<int instanceId>
+    inline Logger<instanceId>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return isCsv(fileName) ? init<CsvFormatter, instanceId>(maxSeverity, fileName, maxFileSize, maxFiles) : init<TxtFormatter, instanceId>(maxSeverity, fileName, maxFileSize, maxFiles);
+    }
+
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return init<PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // CHAR variants for Windows
+
+#if defined(_WIN32) && !PLOG_CHAR_IS_UTF8
+    template<class Formatter, int instanceId>
+    inline Logger<instanceId>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return init<Formatter, instanceId>(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles);
+    }
+
+    template<class Formatter>
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return init<Formatter, PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
+    }
+
+    template<int instanceId>
+    inline Logger<instanceId>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return init<instanceId>(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles);
+    }
+
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
+    {
+        return init<PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
+    }
+#endif
+}

+ 202 - 0
src/3rd/plog/plog/Log.h

@@ -0,0 +1,202 @@
+//////////////////////////////////////////////////////////////////////////
+//  Plog - portable and simple log for C++
+//  Documentation and sources: https://github.com/SergiusTheBest/plog
+//  License: MIT, https://choosealicense.com/licenses/mit
+
+#pragma once
+#include <plog/Logger.h>
+
+//////////////////////////////////////////////////////////////////////////
+// Helper macros that get context info
+
+#if defined(PLOG_ENABLE_GET_THIS) && defined(_MSC_VER) && _MSC_VER >= 1600 && !defined(__INTELLISENSE__) && !defined(__INTEL_COMPILER) && !defined(__llvm__) && !defined(__RESHARPER__) // >= Visual Studio 2010, skip IntelliSense, Intel Compiler, Clang Code Model and ReSharper
+#   define PLOG_GET_THIS()      __if_exists(this) { this } __if_not_exists(this) { 0 }
+#else
+#   define PLOG_GET_THIS()      reinterpret_cast<void*>(0)
+#endif
+
+#ifdef _MSC_VER
+#   define PLOG_GET_FUNC()      __FUNCTION__
+#elif defined(__BORLANDC__)
+#   define PLOG_GET_FUNC()      __FUNC__
+#else
+#   define PLOG_GET_FUNC()      __PRETTY_FUNCTION__
+#endif
+
+#ifdef PLOG_CAPTURE_FILE
+#   define PLOG_GET_FILE()      __FILE__
+#else
+#   define PLOG_GET_FILE()      ""
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// Log severity level checker
+
+#ifdef PLOG_DISABLE_LOGGING
+#   ifdef _MSC_VER
+#       define IF_PLOG_(instanceId, severity)  __pragma(warning(push)) __pragma(warning(disable:4127)) if (true) {;} else __pragma(warning(pop)) // conditional expression is constant
+#   else
+#       define IF_PLOG_(instanceId, severity)   if (true) {;} else
+#   endif
+#else
+#   define IF_PLOG_(instanceId, severity)   if (!plog::get<instanceId>() || !plog::get<instanceId>()->checkSeverity(severity)) {;} else
+#endif
+
+#define IF_PLOG(severity)                IF_PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity)
+
+//////////////////////////////////////////////////////////////////////////
+// Main logging macros
+
+#define PLOG_(instanceId, severity)      IF_PLOG_(instanceId, severity) (*plog::get<instanceId>()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref()
+#define PLOG(severity)                   PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity)
+
+#define PLOG_VERBOSE                     PLOG(plog::verbose)
+#define PLOG_DEBUG                       PLOG(plog::debug)
+#define PLOG_INFO                        PLOG(plog::info)
+#define PLOG_WARNING                     PLOG(plog::warning)
+#define PLOG_ERROR                       PLOG(plog::error)
+#define PLOG_FATAL                       PLOG(plog::fatal)
+#define PLOG_NONE                        PLOG(plog::none)
+
+#define PLOG_VERBOSE_(instanceId)        PLOG_(instanceId, plog::verbose)
+#define PLOG_DEBUG_(instanceId)          PLOG_(instanceId, plog::debug)
+#define PLOG_INFO_(instanceId)           PLOG_(instanceId, plog::info)
+#define PLOG_WARNING_(instanceId)        PLOG_(instanceId, plog::warning)
+#define PLOG_ERROR_(instanceId)          PLOG_(instanceId, plog::error)
+#define PLOG_FATAL_(instanceId)          PLOG_(instanceId, plog::fatal)
+#define PLOG_NONE_(instanceId)           PLOG_(instanceId, plog::none)
+
+#define PLOGV                            PLOG_VERBOSE
+#define PLOGD                            PLOG_DEBUG
+#define PLOGI                            PLOG_INFO
+#define PLOGW                            PLOG_WARNING
+#define PLOGE                            PLOG_ERROR
+#define PLOGF                            PLOG_FATAL
+#define PLOGN                            PLOG_NONE
+
+#define PLOGV_(instanceId)               PLOG_VERBOSE_(instanceId)
+#define PLOGD_(instanceId)               PLOG_DEBUG_(instanceId)
+#define PLOGI_(instanceId)               PLOG_INFO_(instanceId)
+#define PLOGW_(instanceId)               PLOG_WARNING_(instanceId)
+#define PLOGE_(instanceId)               PLOG_ERROR_(instanceId)
+#define PLOGF_(instanceId)               PLOG_FATAL_(instanceId)
+#define PLOGN_(instanceId)               PLOG_NONE_(instanceId)
+
+//////////////////////////////////////////////////////////////////////////
+// Conditional logging macros
+
+#define PLOG_IF_(instanceId, severity, condition)  if (!(condition)) {;} else PLOG_(instanceId, severity)
+#define PLOG_IF(severity, condition)               PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition)
+
+#define PLOG_VERBOSE_IF(condition)               PLOG_IF(plog::verbose, condition)
+#define PLOG_DEBUG_IF(condition)                 PLOG_IF(plog::debug, condition)
+#define PLOG_INFO_IF(condition)                  PLOG_IF(plog::info, condition)
+#define PLOG_WARNING_IF(condition)               PLOG_IF(plog::warning, condition)
+#define PLOG_ERROR_IF(condition)                 PLOG_IF(plog::error, condition)
+#define PLOG_FATAL_IF(condition)                 PLOG_IF(plog::fatal, condition)
+#define PLOG_NONE_IF(condition)                  PLOG_IF(plog::none, condition)
+
+#define PLOG_VERBOSE_IF_(instanceId, condition)  PLOG_IF_(instanceId, plog::verbose, condition)
+#define PLOG_DEBUG_IF_(instanceId, condition)    PLOG_IF_(instanceId, plog::debug, condition)
+#define PLOG_INFO_IF_(instanceId, condition)     PLOG_IF_(instanceId, plog::info, condition)
+#define PLOG_WARNING_IF_(instanceId, condition)  PLOG_IF_(instanceId, plog::warning, condition)
+#define PLOG_ERROR_IF_(instanceId, condition)    PLOG_IF_(instanceId, plog::error, condition)
+#define PLOG_FATAL_IF_(instanceId, condition)    PLOG_IF_(instanceId, plog::fatal, condition)
+#define PLOG_NONE_IF_(instanceId, condition)     PLOG_IF_(instanceId, plog::none, condition)
+
+#define PLOGV_IF(condition)                      PLOG_VERBOSE_IF(condition)
+#define PLOGD_IF(condition)                      PLOG_DEBUG_IF(condition)
+#define PLOGI_IF(condition)                      PLOG_INFO_IF(condition)
+#define PLOGW_IF(condition)                      PLOG_WARNING_IF(condition)
+#define PLOGE_IF(condition)                      PLOG_ERROR_IF(condition)
+#define PLOGF_IF(condition)                      PLOG_FATAL_IF(condition)
+#define PLOGN_IF(condition)                      PLOG_NONE_IF(condition)
+
+#define PLOGV_IF_(instanceId, condition)         PLOG_VERBOSE_IF_(instanceId, condition)
+#define PLOGD_IF_(instanceId, condition)         PLOG_DEBUG_IF_(instanceId, condition)
+#define PLOGI_IF_(instanceId, condition)         PLOG_INFO_IF_(instanceId, condition)
+#define PLOGW_IF_(instanceId, condition)         PLOG_WARNING_IF_(instanceId, condition)
+#define PLOGE_IF_(instanceId, condition)         PLOG_ERROR_IF_(instanceId, condition)
+#define PLOGF_IF_(instanceId, condition)         PLOG_FATAL_IF_(instanceId, condition)
+#define PLOGN_IF_(instanceId, condition)         PLOG_NONE_IF_(instanceId, condition)
+
+// Old macro names for downward compatibility. To bypass including these macro names, add
+// #define PLOG_OMIT_LOG_DEFINES before #include <plog/Log.h>
+#ifndef PLOG_OMIT_LOG_DEFINES
+
+//////////////////////////////////////////////////////////////////////////
+// Main logging macros - can be changed later to point at macros for a different logging package
+
+#define LOG_(instanceId, severity)      IF_PLOG_(instanceId, severity) (*plog::get<instanceId>()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref()
+#define LOG(severity)                   PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity)
+
+#define LOG_VERBOSE                     PLOG(plog::verbose)
+#define LOG_DEBUG                       PLOG(plog::debug)
+#define LOG_INFO                        PLOG(plog::info)
+#define LOG_WARNING                     PLOG(plog::warning)
+#define LOG_ERROR                       PLOG(plog::error)
+#define LOG_FATAL                       PLOG(plog::fatal)
+#define LOG_NONE                        PLOG(plog::none)
+
+#define LOG_VERBOSE_(instanceId)        PLOG_(instanceId, plog::verbose)
+#define LOG_DEBUG_(instanceId)          PLOG_(instanceId, plog::debug)
+#define LOG_INFO_(instanceId)           PLOG_(instanceId, plog::info)
+#define LOG_WARNING_(instanceId)        PLOG_(instanceId, plog::warning)
+#define LOG_ERROR_(instanceId)          PLOG_(instanceId, plog::error)
+#define LOG_FATAL_(instanceId)          PLOG_(instanceId, plog::fatal)
+#define LOG_NONE_(instanceId)           PLOG_(instanceId, plog::none)
+
+#define LOGV                            PLOG_VERBOSE
+#define LOGD                            PLOG_DEBUG
+#define LOGI                            PLOG_INFO
+#define LOGW                            PLOG_WARNING
+#define LOGE                            PLOG_ERROR
+#define LOGF                            PLOG_FATAL
+#define LOGN                            PLOG_NONE
+
+#define LOGV_(instanceId)               PLOG_VERBOSE_(instanceId)
+#define LOGD_(instanceId)               PLOG_DEBUG_(instanceId)
+#define LOGI_(instanceId)               PLOG_INFO_(instanceId)
+#define LOGW_(instanceId)               PLOG_WARNING_(instanceId)
+#define LOGE_(instanceId)               PLOG_ERROR_(instanceId)
+#define LOGF_(instanceId)               PLOG_FATAL_(instanceId)
+#define LOGN_(instanceId)               PLOG_NONE_(instanceId)
+
+//////////////////////////////////////////////////////////////////////////
+// Conditional logging macros
+
+#define LOG_IF_(instanceId, severity, condition)  if (!(condition)) {;} else PLOG_(instanceId, severity)
+#define LOG_IF(severity, condition)               PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition)
+
+#define LOG_VERBOSE_IF(condition)               PLOG_IF(plog::verbose, condition)
+#define LOG_DEBUG_IF(condition)                 PLOG_IF(plog::debug, condition)
+#define LOG_INFO_IF(condition)                  PLOG_IF(plog::info, condition)
+#define LOG_WARNING_IF(condition)               PLOG_IF(plog::warning, condition)
+#define LOG_ERROR_IF(condition)                 PLOG_IF(plog::error, condition)
+#define LOG_FATAL_IF(condition)                 PLOG_IF(plog::fatal, condition)
+#define LOG_NONE_IF(condition)                  PLOG_IF(plog::none, condition)
+
+#define LOG_VERBOSE_IF_(instanceId, condition)  PLOG_IF_(instanceId, plog::verbose, condition)
+#define LOG_DEBUG_IF_(instanceId, condition)    PLOG_IF_(instanceId, plog::debug, condition)
+#define LOG_INFO_IF_(instanceId, condition)     PLOG_IF_(instanceId, plog::info, condition)
+#define LOG_WARNING_IF_(instanceId, condition)  PLOG_IF_(instanceId, plog::warning, condition)
+#define LOG_ERROR_IF_(instanceId, condition)    PLOG_IF_(instanceId, plog::error, condition)
+#define LOG_FATAL_IF_(instanceId, condition)    PLOG_IF_(instanceId, plog::fatal, condition)
+#define LOG_NONE_IF_(instanceId, condition)     PLOG_IF_(instanceId, plog::none, condition)
+
+#define LOGV_IF(condition)                      PLOG_VERBOSE_IF(condition)
+#define LOGD_IF(condition)                      PLOG_DEBUG_IF(condition)
+#define LOGI_IF(condition)                      PLOG_INFO_IF(condition)
+#define LOGW_IF(condition)                      PLOG_WARNING_IF(condition)
+#define LOGE_IF(condition)                      PLOG_ERROR_IF(condition)
+#define LOGF_IF(condition)                      PLOG_FATAL_IF(condition)
+#define LOGN_IF(condition)                      PLOG_NONE_IF(condition)
+
+#define LOGV_IF_(instanceId, condition)         PLOG_VERBOSE_IF_(instanceId, condition)
+#define LOGD_IF_(instanceId, condition)         PLOG_DEBUG_IF_(instanceId, condition)
+#define LOGI_IF_(instanceId, condition)         PLOG_INFO_IF_(instanceId, condition)
+#define LOGW_IF_(instanceId, condition)         PLOG_WARNING_IF_(instanceId, condition)
+#define LOGE_IF_(instanceId, condition)         PLOG_ERROR_IF_(instanceId, condition)
+#define LOGF_IF_(instanceId, condition)         PLOG_FATAL_IF_(instanceId, condition)
+#define LOGN_IF_(instanceId, condition)         PLOG_NONE_IF_(instanceId, condition)
+#endif

+ 84 - 0
src/3rd/plog/plog/Logger.h

@@ -0,0 +1,84 @@
+#pragma once
+#include <plog/Appenders/IAppender.h>
+#include <plog/Util.h>
+#include <vector>
+
+#ifdef PLOG_DEFAULT_INSTANCE // for backward compatibility
+#   define PLOG_DEFAULT_INSTANCE_ID PLOG_DEFAULT_INSTANCE
+#endif
+
+#ifndef PLOG_DEFAULT_INSTANCE_ID
+#   define PLOG_DEFAULT_INSTANCE_ID 0
+#endif
+
+namespace plog
+{
+    template<int instanceId>
+    class PLOG_LINKAGE Logger : public util::Singleton<Logger<instanceId> >, public IAppender
+    {
+    public:
+        Logger(Severity maxSeverity = none) : m_maxSeverity(maxSeverity)
+        {
+        }
+
+        Logger& addAppender(IAppender* appender)
+        {
+            assert(appender != this);
+            m_appenders.push_back(appender);
+            return *this;
+        }
+
+        Severity getMaxSeverity() const
+        {
+            return m_maxSeverity;
+        }
+
+        void setMaxSeverity(Severity severity)
+        {
+            m_maxSeverity = severity;
+        }
+
+        bool checkSeverity(Severity severity) const
+        {
+            return severity <= m_maxSeverity;
+        }
+
+        virtual void write(const Record& record) PLOG_OVERRIDE
+        {
+            if (checkSeverity(record.getSeverity()))
+            {
+                *this += record;
+            }
+        }
+
+        void operator+=(const Record& record)
+        {
+            for (std::vector<IAppender*>::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it)
+            {
+                (*it)->write(record);
+            }
+        }
+
+    private:
+        Severity m_maxSeverity;
+#ifdef _MSC_VER
+#   pragma warning(push)
+#   pragma warning(disable:4251) // needs to have dll-interface to be used by clients of class
+#endif
+        std::vector<IAppender*> m_appenders;
+#ifdef _MSC_VER
+#   pragma warning(pop)
+#endif
+    };
+
+    template<int instanceId>
+    inline Logger<instanceId>* get()
+    {
+        return Logger<instanceId>::getInstance();
+    }
+
+    inline Logger<PLOG_DEFAULT_INSTANCE_ID>* get()
+    {
+        return Logger<PLOG_DEFAULT_INSTANCE_ID>::getInstance();
+    }
+}

+ 465 - 0
src/3rd/plog/plog/Record.h

@@ -0,0 +1,465 @@
+#pragma once
+#include <cstdarg>
+#include <plog/Severity.h>
+#include <plog/Util.h>
+
+#ifdef __cplusplus_cli
+#include <vcclr.h>  // For PtrToStringChars
+#endif
+
+namespace plog
+{
+    namespace detail
+    {
+#if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
+        namespace meta
+        {
+            template<class T>
+            inline T& declval()
+            {
+#ifdef __INTEL_COMPILER
+#    pragma warning(suppress: 327) // NULL reference is not allowed
+#endif
+                return *reinterpret_cast<T*>(0);
+            }
+
+            template<bool B, class T = void>
+            struct enableIf {};
+
+            template<class T>
+            struct enableIf<true, T> { typedef T type; };
+
+            struct No  { char a[1]; };
+            struct Yes { char a[2]; };
+
+            template <class From, class To>
+            struct isConvertible
+            {
+                // `+ sizeof(U*)` is required for GCC 4.5-4.7
+                template<class U>
+                static typename enableIf<!!(sizeof(static_cast<To>(meta::declval<U>())) + sizeof(U*)), Yes>::type test(int);
+
+                template<class U>
+                static No test(...);
+
+                enum { value = sizeof(test<From>(0)) == sizeof(Yes) };
+            };
+
+            template <class T>
+            struct isConvertibleToString : isConvertible<T, std::string> {};
+
+#if PLOG_ENABLE_WCHAR_INPUT
+            template <class T>
+            struct isConvertibleToWString : isConvertible<T, std::wstring> {};
+#endif
+
+            template <class T>
+            struct isContainer
+            {
+                template<class U>
+                static typename meta::enableIf<!!(sizeof(
+#if defined(_MSC_VER) && _MSC_VER < 1700 // MSVC 2010 doesn't understand `typename T::const_iterator`
+                    meta::declval<U>().begin()) + sizeof(meta::declval<U>().end()
+#else
+                    typename U::const_iterator
+#endif
+                    )), Yes>::type test(int);
+
+                template<class U>
+                static No test(...);
+
+                enum { value = sizeof(test<T>(0)) == sizeof(Yes) };
+            };
+
+            // Detects `std::filesystem::path` and `boost::filesystem::path`. They look like containers
+            // but we don't want to treat them as containers, so we use this detector to filter them out.
+            template <class T>
+            struct isFilesystemPath
+            {
+                template<class U>
+                static typename meta::enableIf<!!(sizeof(meta::declval<U>().preferred_separator)), Yes>::type test(int);
+
+                template<class U>
+                static No test(...);
+
+                enum { value = sizeof(test<T>(0)) == sizeof(Yes) };
+            };
+        }
+#endif
+
+        //////////////////////////////////////////////////////////////////////////
+        // Stream output operators as free functions
+
+#if PLOG_ENABLE_WCHAR_INPUT
+        inline void operator<<(util::nostringstream& stream, const wchar_t* data)
+        {
+            data = data ? data : L"(null)";
+
+#   ifdef _WIN32
+#       if PLOG_CHAR_IS_UTF8
+            std::operator<<(stream, util::toNarrow(data, codePage::kUTF8));
+#       else
+            std::operator<<(stream, data);
+#       endif
+#   else
+            std::operator<<(stream, util::toNarrow(data));
+#   endif
+        }
+
+        inline void operator<<(util::nostringstream& stream, wchar_t* data)
+        {
+            plog::detail::operator<<(stream, const_cast<const wchar_t*>(data));
+        }
+
+        inline void operator<<(util::nostringstream& stream, const std::wstring& data)
+        {
+            plog::detail::operator<<(stream, data.c_str());
+        }
+#endif
+
+        inline void operator<<(util::nostringstream& stream, const char* data)
+        {
+            data = data ? data : "(null)";
+
+#if defined(_WIN32) && defined(__BORLANDC__)
+#   if PLOG_CHAR_IS_UTF8
+            stream << data;
+#   else
+            stream << util::toWide(data);
+#   endif
+#elif defined(_WIN32)
+#   if PLOG_CHAR_IS_UTF8
+            std::operator<<(stream, data);
+#   else
+            std::operator<<(stream, util::toWide(data));
+#   endif
+#else
+            std::operator<<(stream, data);
+#endif
+        }
+
+        inline void operator<<(util::nostringstream& stream, char* data)
+        {
+            plog::detail::operator<<(stream, const_cast<const char*>(data));
+        }
+
+        inline void operator<<(util::nostringstream& stream, const std::string& data)
+        {
+            plog::detail::operator<<(stream, data.c_str());
+        }
+
+#ifdef __cpp_char8_t
+        inline void operator<<(util::nostringstream& stream, const char8_t* data)
+        {
+#   if PLOG_CHAR_IS_UTF8
+            plog::detail::operator<<(stream, reinterpret_cast<const char*>(data));
+#   else
+            plog::detail::operator<<(stream, util::toWide(reinterpret_cast<const char*>(data), codePage::kUTF8));
+#   endif
+        }
+#endif //__cpp_char8_t
+
+        // Print `std::pair`
+        template<class T1, class T2>
+        inline void operator<<(util::nostringstream& stream, const std::pair<T1, T2>& data)
+        {
+            stream << data.first;
+            stream << ":";
+            stream << data.second;
+        }
+
+#if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600
+#if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
+        // Print data that can be casted to `std::string`
+        template<class T>
+        inline typename meta::enableIf<meta::isConvertibleToString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
+        {
+            plog::detail::operator<<(stream, static_cast<std::string>(data));
+        }
+
+#if PLOG_ENABLE_WCHAR_INPUT
+        // Print data that can be casted to `std::wstring`
+        template<class T>
+        inline typename meta::enableIf<meta::isConvertibleToWString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
+        {
+            plog::detail::operator<<(stream, static_cast<std::wstring>(data));
+        }
+#endif
+
+        // Print std containers
+        template<class T>
+        inline typename meta::enableIf<meta::isContainer<T>::value &&
+            !meta::isConvertibleToString<T>::value &&
+#if PLOG_ENABLE_WCHAR_INPUT
+            !meta::isConvertibleToWString<T>::value &&
+#endif
+            !meta::isFilesystemPath<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
+        {
+            stream << "[";
+
+            for (typename T::const_iterator it = data.begin(); it != data.end();)
+            {
+                stream << *it;
+
+                if (++it == data.end())
+                {
+                    break;
+                }
+
+                stream << ", ";
+            }
+
+            stream << "]";
+        }
+#endif
+#endif
+
+#ifdef __cplusplus_cli
+        // Print managed C++ `System::String^`
+        inline void operator<<(util::nostringstream& stream, System::String^ data)
+        {
+            cli::pin_ptr<const System::Char> ptr = PtrToStringChars(data);
+            plog::detail::operator<<(stream, static_cast<const wchar_t*>(ptr));
+        }
+#endif
+
+#if PLOG_ENABLE_WCHAR_INPUT && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
+        namespace meta
+        {
+            template<bool Value>
+            struct valueType { enum { value = Value }; };
+
+            template<class T, class Stream>
+            inline No operator<<(Stream&, const T&);
+
+            template<class T, class Stream>
+            struct isStreamable : valueType<sizeof(operator<<(meta::declval<Stream>(), meta::declval<const T>())) != sizeof(No)> {};
+
+            template<class Stream>
+            struct isStreamable<std::ios_base& PLOG_CDECL (std::ios_base&), Stream> : valueType<true> {};
+
+            template<class Stream, size_t N>
+            struct isStreamable<wchar_t[N], Stream> : valueType<false> {};
+
+            template<class Stream, size_t N>
+            struct isStreamable<const wchar_t[N], Stream> : valueType<false> {};
+
+            // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const wchar_t*) = delete` so explicitly define it
+            template<>
+            struct isStreamable<const wchar_t*, std::ostream> : valueType<false> {};
+
+#   ifdef __cpp_char8_t
+            // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const char8_t*) = delete` so explicitly define it
+            template<class Stream, size_t N>
+            struct isStreamable<char8_t[N], Stream> : valueType<false> {};
+
+            template<class Stream>
+            struct isStreamable<const char8_t*, Stream> : valueType<false> {};
+#   endif //__cpp_char8_t
+        }
+
+#   if PLOG_CHAR_IS_UTF8
+        // Print types that can be streamed into `std::owstringstream` but not into `std::ostringstream` when we use UTF-8 on Windows
+        template<class T>
+        inline typename meta::enableIf<meta::isStreamable<T, std::wostream>::value &&
+            !meta::isStreamable<T, std::ostream>::value &&
+            !meta::isConvertibleToWString<T>::value, void>::type operator<<(std::ostringstream& stream, const T& data)
+        {
+            std::wostringstream ss;
+            ss << data;
+            stream << ss.str();
+        }
+#   else
+        // Print types that can be streamed into `std::ostringstream` but not into `std::owstringstream` when we use `wchar_t` on Windows
+        template<class T>
+        inline typename meta::enableIf<meta::isStreamable<T, std::ostream>::value &&
+            !meta::isStreamable<T, std::wostream>::value &&
+            !meta::isConvertibleToString<T>::value, void>::type operator<<(std::wostringstream& stream, const T& data)
+        {
+            std::ostringstream ss;
+            ss << data;
+            stream << ss.str();
+        }
+#   endif
+#endif
+    }
+
+    class Record
+    {
+    public:
+        Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId)
+            : m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file), m_instanceId(instanceId)
+        {
+            util::ftime(&m_time);
+        }
+
+        Record& ref()
+        {
+            return *this;
+        }
+
+        //////////////////////////////////////////////////////////////////////////
+        // Stream output operators
+
+        Record& operator<<(char data)
+        {
+            char str[] = { data, 0 };
+            return *this << str;
+        }
+
+#if PLOG_ENABLE_WCHAR_INPUT
+        Record& operator<<(wchar_t data)
+        {
+            wchar_t str[] = { data, 0 };
+            return *this << str;
+        }
+#endif
+
+        Record& operator<<(util::nostream& (PLOG_CDECL *data)(util::nostream&))
+        {
+            m_message << data;
+            return *this;
+        }
+
+#ifdef QT_VERSION
+        Record& operator<<(const QString& data)
+        {
+#   if PLOG_CHAR_IS_UTF8
+            return *this << data.toStdString();
+#   else
+            return *this << data.toStdWString();
+#   endif
+        }
+
+#   if QT_VERSION < 0x060000
+        Record& operator<<(const QStringRef& data)
+        {
+            return *this << data.toString();
+        }
+#   endif
+
+#   ifdef QSTRINGVIEW_H
+        Record& operator<<(QStringView data)
+        {
+            return *this << data.toString();
+        }
+#   endif
+#endif
+
+        template<typename T>
+        Record& operator<<(const T& data)
+        {
+            using namespace plog::detail;
+
+            m_message << data;
+            return *this;
+        }
+
+#ifndef __cplusplus_cli
+        Record& printf(const char* format, ...)
+        {
+            using namespace util;
+
+            char* str = NULL;
+            va_list ap;
+
+            va_start(ap, format);
+            int len = vasprintf(&str, format, ap);
+            static_cast<void>(len);
+            va_end(ap);
+
+            *this << str;
+            free(str);
+
+            return *this;
+        }
+
+#ifdef _WIN32
+        Record& printf(const wchar_t* format, ...)
+        {
+            using namespace util;
+
+            wchar_t* str = NULL;
+            va_list ap;
+
+            va_start(ap, format);
+            int len = vaswprintf(&str, format, ap);
+            static_cast<void>(len);
+            va_end(ap);
+
+            *this << str;
+            free(str);
+
+            return *this;
+        }
+#endif
+#endif //__cplusplus_cli
+
+        //////////////////////////////////////////////////////////////////////////
+        // Getters
+
+        virtual const util::Time& getTime() const
+        {
+            return m_time;
+        }
+
+        virtual Severity getSeverity() const
+        {
+            return m_severity;
+        }
+
+        virtual unsigned int getTid() const
+        {
+            return m_tid;
+        }
+
+        virtual const void* getObject() const
+        {
+            return m_object;
+        }
+
+        virtual size_t getLine() const
+        {
+            return m_line;
+        }
+
+        virtual const util::nchar* getMessage() const
+        {
+            m_messageStr = m_message.str();
+            return m_messageStr.c_str();
+        }
+
+        virtual const char* getFunc() const
+        {
+            m_funcStr = util::processFuncName(m_func);
+            return m_funcStr.c_str();
+        }
+
+        virtual const char* getFile() const
+        {
+            return m_file;
+        }
+
+        virtual ~Record() // virtual destructor to satisfy -Wnon-virtual-dtor warning
+        {
+        }
+
+        virtual int getInstanceId() const
+        {
+            return m_instanceId;
+        }
+
+    private:
+        util::Time              m_time;
+        const Severity          m_severity;
+        const unsigned int      m_tid;
+        const void* const       m_object;
+        const size_t            m_line;
+        util::nostringstream    m_message;
+        const char* const       m_func;
+        const char* const       m_file;
+        const int               m_instanceId;
+        mutable std::string     m_funcStr;
+        mutable util::nstring   m_messageStr;
+    };
+}

+ 61 - 0
src/3rd/plog/plog/Severity.h

@@ -0,0 +1,61 @@
+#pragma once
+#include <cctype>
+
+namespace plog
+{
+    enum Severity
+    {
+        none = 0,
+        fatal = 1,
+        error = 2,
+        warning = 3,
+        info = 4,
+        debug = 5,
+        verbose = 6
+    };
+
+#ifdef _MSC_VER
+#   pragma warning(suppress: 26812) //  Prefer 'enum class' over 'enum'
+#endif
+    inline const char* severityToString(Severity severity)
+    {
+        switch (severity)
+        {
+        case fatal:
+            return "FATAL";
+        case error:
+            return "ERROR";
+        case warning:
+            return "WARN";
+        case info:
+            return "INFO";
+        case debug:
+            return "DEBUG";
+        case verbose:
+            return "VERB";
+        default:
+            return "NONE";
+        }
+    }
+
+    inline Severity severityFromString(const char* str)
+    {
+        switch (std::toupper(str[0]))
+        {
+        case 'F':
+            return fatal;
+        case 'E':
+            return error;
+        case 'W':
+            return warning;
+        case 'I':
+            return info;
+        case 'D':
+            return debug;
+        case 'V':
+            return verbose;
+        default:
+            return none;
+        }
+    }
+}

+ 635 - 0
src/3rd/plog/plog/Util.h

@@ -0,0 +1,635 @@
+#pragma once
+#include <cassert>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <sstream>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifndef PLOG_ENABLE_WCHAR_INPUT
+#   ifdef _WIN32
+#       define PLOG_ENABLE_WCHAR_INPUT 1
+#   else
+#       define PLOG_ENABLE_WCHAR_INPUT 0
+#   endif
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// PLOG_CHAR_IS_UTF8 specifies character encoding of `char` type. On *nix
+// systems it's set to UTF-8 while on Windows in can be ANSI or UTF-8. It
+// automatically detects `/utf-8` command line option in MSVC. Also it can
+// be set manually if required.
+// This option allows to support http://utf8everywhere.org approach.
+
+#ifndef PLOG_CHAR_IS_UTF8
+#   if defined(_WIN32) && !defined(_UTF8)
+#       define PLOG_CHAR_IS_UTF8 0
+#   else
+#       define PLOG_CHAR_IS_UTF8 1
+#   endif
+#endif
+
+#ifdef _WIN32
+#   if defined(PLOG_EXPORT)
+#       define PLOG_LINKAGE __declspec(dllexport)
+#   elif defined(PLOG_IMPORT)
+#       define PLOG_LINKAGE __declspec(dllimport)
+#   endif
+#   if defined(PLOG_GLOBAL)
+#       error "PLOG_GLOBAL isn't supported on Windows"
+#   endif
+#else
+#   if defined(PLOG_GLOBAL)
+#       define PLOG_LINKAGE __attribute__ ((visibility ("default")))
+#   elif defined(PLOG_LOCAL)
+#       define PLOG_LINKAGE __attribute__ ((visibility ("hidden")))
+#       define PLOG_LINKAGE_HIDDEN PLOG_LINKAGE
+#   endif
+#   if defined(PLOG_EXPORT) || defined(PLOG_IMPORT)
+#       error "PLOG_EXPORT/PLOG_IMPORT is supported only on Windows"
+#   endif
+#endif
+
+#ifndef PLOG_LINKAGE
+#   define PLOG_LINKAGE
+#endif
+
+#ifndef PLOG_LINKAGE_HIDDEN
+#   define PLOG_LINKAGE_HIDDEN
+#endif
+
+#ifdef _WIN32
+#   include <plog/WinApi.h>
+#   include <time.h>
+#   include <sys/timeb.h>
+#   include <io.h>
+#   include <share.h>
+#else
+#   include <unistd.h>
+#   include <sys/time.h>
+#   if defined(__linux__) || defined(__FreeBSD__)
+#       include <sys/syscall.h>
+#   elif defined(__rtems__)
+#       include <rtems.h>
+#   endif
+#   if defined(_POSIX_THREADS)
+#       include <pthread.h>
+#   endif
+#   if PLOG_ENABLE_WCHAR_INPUT
+#       include <iconv.h>
+#   endif
+#endif
+
+#ifdef __FREERTOS__ // There is no standard way to know if the code is compiled for FreeRTOS. We expect __FREERTOS__ macro to be defined in such case.
+#   include <FreeRTOS.h>
+#   include <semphr.h>
+#   include <task.h>
+#endif
+
+#if PLOG_CHAR_IS_UTF8
+#   define PLOG_NSTR(x)    x
+#else
+#   define _PLOG_NSTR(x)   L##x
+#   define PLOG_NSTR(x)    _PLOG_NSTR(x)
+#endif
+
+#ifdef _WIN32
+#   define PLOG_CDECL      __cdecl
+#else
+#   define PLOG_CDECL
+#endif
+
+#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700
+#   define PLOG_OVERRIDE override
+#else
+#   define PLOG_OVERRIDE
+#endif
+
+namespace plog
+{
+    namespace util
+    {
+#if PLOG_CHAR_IS_UTF8
+        typedef std::string nstring;
+        typedef std::ostringstream nostringstream;
+        typedef std::istringstream nistringstream;
+        typedef std::ostream nostream;
+        typedef char nchar;
+#else
+        typedef std::wstring nstring;
+        typedef std::wostringstream nostringstream;
+        typedef std::wistringstream nistringstream;
+        typedef std::wostream nostream;
+        typedef wchar_t nchar;
+#endif
+
+        inline void localtime_s(struct tm* t, const time_t* time)
+        {
+#if defined(_WIN32) && defined(__BORLANDC__)
+            ::localtime_s(time, t);
+#elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+            *t = *::localtime(time);
+#elif defined(_WIN32)
+            ::localtime_s(t, time);
+#else
+            ::localtime_r(time, t);
+#endif
+        }
+
+        inline void gmtime_s(struct tm* t, const time_t* time)
+        {
+#if defined(_WIN32) && defined(__BORLANDC__)
+            ::gmtime_s(time, t);
+#elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+            *t = *::gmtime(time);
+#elif defined(_WIN32)
+            ::gmtime_s(t, time);
+#else
+            ::gmtime_r(time, t);
+#endif
+        }
+
+#ifdef _WIN32
+        typedef timeb Time;
+
+        inline void ftime(Time* t)
+        {
+            ::ftime(t);
+        }
+#else
+        struct Time
+        {
+            time_t time;
+            unsigned short millitm;
+        };
+
+        inline void ftime(Time* t)
+        {
+            timeval tv;
+            ::gettimeofday(&tv, NULL);
+
+            t->time = tv.tv_sec;
+            t->millitm = static_cast<unsigned short>(tv.tv_usec / 1000);
+        }
+#endif
+
+        inline unsigned int gettid()
+        {
+#if defined(__FREERTOS__) && defined(INCLUDE_xTaskGetCurrentTaskHandle)
+            return static_cast<unsigned int>(reinterpret_cast<uintptr_t>(xTaskGetCurrentTaskHandle()));
+#elif defined(_WIN32)
+            return GetCurrentThreadId();
+#elif defined(__linux__)
+            return static_cast<unsigned int>(::syscall(__NR_gettid));
+#elif defined(__FreeBSD__)
+            long tid;
+            syscall(SYS_thr_self, &tid);
+            return static_cast<unsigned int>(tid);
+#elif defined(__rtems__)
+            return rtems_task_self();
+#elif defined(__APPLE__)
+            uint64_t tid64;
+            pthread_threadid_np(NULL, &tid64);
+            return static_cast<unsigned int>(tid64);
+#else
+            return 0;
+#endif
+        }
+
+#ifndef _GNU_SOURCE
+    inline int vasprintf(char** strp, const char* format, va_list ap)
+    {
+        va_list ap_copy;
+#if defined(_MSC_VER) && _MSC_VER <= 1600
+        ap_copy = ap; // there is no va_copy on Visual Studio 2010
+#else
+        va_copy(ap_copy, ap);
+#endif
+#ifndef __STDC_SECURE_LIB__
+        int charCount = vsnprintf(NULL, 0, format, ap_copy);
+#else
+        int charCount = _vscprintf(format, ap_copy);
+#endif
+        va_end(ap_copy);
+        if (charCount < 0)
+        {
+            return -1;
+        }
+
+        size_t bufferCharCount = static_cast<size_t>(charCount) + 1;
+
+        char* str = static_cast<char*>(malloc(bufferCharCount));
+        if (!str)
+        {
+            return -1;
+        }
+
+#ifndef __STDC_SECURE_LIB__
+        int retval = vsnprintf(str, bufferCharCount, format, ap);
+#else
+        int retval = vsnprintf_s(str, bufferCharCount, static_cast<size_t>(-1), format, ap);
+#endif
+        if (retval < 0)
+        {
+            free(str);
+            return -1;
+        }
+
+        *strp = str;
+        return retval;
+    }
+#endif
+
+#ifdef _WIN32
+    inline int vaswprintf(wchar_t** strp, const wchar_t* format, va_list ap)
+    {
+#if defined(__BORLANDC__)
+        int charCount = 0x1000; // there is no _vscwprintf on Borland/Embarcadero
+#else
+        int charCount = _vscwprintf(format, ap);
+        if (charCount < 0)
+        {
+            return -1;
+        }
+#endif
+
+        size_t bufferCharCount = static_cast<size_t>(charCount) + 1;
+
+        wchar_t* str = static_cast<wchar_t*>(malloc(bufferCharCount * sizeof(wchar_t)));
+        if (!str)
+        {
+            return -1;
+        }
+
+#if defined(__BORLANDC__)
+        int retval = vsnwprintf_s(str, bufferCharCount, format, ap);
+#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+        int retval = _vsnwprintf(str, bufferCharCount, format, ap);
+#else
+        int retval = _vsnwprintf_s(str, bufferCharCount, charCount, format, ap);
+#endif
+        if (retval < 0)
+        {
+            free(str);
+            return -1;
+        }
+
+        *strp = str;
+        return retval;
+    }
+#endif
+
+#ifdef _WIN32
+        inline std::wstring toWide(const char* str, UINT cp = codePage::kChar)
+        {
+            size_t len = ::strlen(str);
+            std::wstring wstr(len, 0);
+
+            if (!wstr.empty())
+            {
+                int wlen = MultiByteToWideChar(cp, 0, str, static_cast<int>(len), &wstr[0], static_cast<int>(wstr.size()));
+                wstr.resize(wlen);
+            }
+
+            return wstr;
+        }
+
+        inline std::wstring toWide(const std::string& str, UINT cp = codePage::kChar)
+        {
+            return toWide(str.c_str(), cp);
+        }
+
+        inline const std::wstring& toWide(const std::wstring& str) // do nothing for already wide string
+        {
+            return str;
+        }
+
+        inline std::string toNarrow(const std::wstring& wstr, long page)
+        {
+            int len = WideCharToMultiByte(page, 0, wstr.c_str(), static_cast<int>(wstr.size()), 0, 0, 0, 0);
+            std::string str(len, 0);
+
+            if (!str.empty())
+            {
+                WideCharToMultiByte(page, 0, wstr.c_str(), static_cast<int>(wstr.size()), &str[0], len, 0, 0);
+            }
+
+            return str;
+        }
+#elif PLOG_ENABLE_WCHAR_INPUT
+        inline std::string toNarrow(const wchar_t* wstr)
+        {
+            size_t wlen = ::wcslen(wstr);
+            std::string str(wlen * sizeof(wchar_t), 0);
+
+            if (!str.empty())
+            {
+                const char* in = reinterpret_cast<const char*>(&wstr[0]);
+                char* out = &str[0];
+                size_t inBytes = wlen * sizeof(wchar_t);
+                size_t outBytes = str.size();
+
+                iconv_t cd = ::iconv_open("UTF-8", "WCHAR_T");
+                ::iconv(cd, const_cast<char**>(&in), &inBytes, &out, &outBytes);
+                ::iconv_close(cd);
+
+                str.resize(str.size() - outBytes);
+            }
+
+            return str;
+        }
+#endif
+
+        inline std::string processFuncName(const char* func)
+        {
+#if (defined(_WIN32) && !defined(__MINGW32__)) || defined(__OBJC__)
+            return std::string(func);
+#else
+            const char* funcBegin = func;
+            const char* funcEnd = ::strchr(funcBegin, '(');
+            int foundTemplate = 0;
+
+            if (!funcEnd)
+            {
+                return std::string(func);
+            }
+
+            for (const char* i = funcEnd - 1; i >= funcBegin; --i) // search backwards for the first space char
+            {
+                if (*i == '>')
+                {
+                    foundTemplate++;
+                }
+                else if (*i == '<')
+                {
+                    foundTemplate--;
+                }
+                else if (*i == ' ' && foundTemplate == 0)
+                {
+                    funcBegin = i + 1;
+                    break;
+                }
+            }
+
+            return std::string(funcBegin, funcEnd);
+#endif
+        }
+
+        inline const nchar* findExtensionDot(const nchar* fileName)
+        {
+#if PLOG_CHAR_IS_UTF8
+            return std::strrchr(fileName, '.');
+#else
+            return std::wcsrchr(fileName, L'.');
+#endif
+        }
+
+        inline void splitFileName(const nchar* fileName, nstring& fileNameNoExt, nstring& fileExt)
+        {
+            const nchar* dot = findExtensionDot(fileName);
+
+            if (dot)
+            {
+                fileNameNoExt.assign(fileName, dot);
+                fileExt.assign(dot + 1);
+            }
+            else
+            {
+                fileNameNoExt.assign(fileName);
+                fileExt.clear();
+            }
+        }
+
+        class PLOG_LINKAGE NonCopyable
+        {
+        protected:
+            NonCopyable()
+            {
+            }
+
+        private:
+            NonCopyable(const NonCopyable&);
+            NonCopyable& operator=(const NonCopyable&);
+        };
+
+        class PLOG_LINKAGE_HIDDEN File : NonCopyable
+        {
+        public:
+            File() : m_file(-1)
+            {
+            }
+
+            ~File()
+            {
+                close();
+            }
+
+            size_t open(const nstring& fileName)
+            {
+#if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__))
+                m_file = ::_wsopen(toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, SH_DENYWR, _S_IREAD | _S_IWRITE);
+#elif defined(_WIN32)
+                ::_wsopen_s(&m_file, toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, _SH_DENYWR, _S_IREAD | _S_IWRITE);
+#elif defined(O_CLOEXEC)
+                m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+#else
+                m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+#endif
+                return seek(0, SEEK_END);
+            }
+
+            size_t write(const void* buf, size_t count)
+            {
+                return m_file != -1 ? static_cast<size_t>(
+#ifdef _WIN32
+                    ::_write(m_file, buf, static_cast<unsigned int>(count))
+#else
+                    ::write(m_file, buf, count)
+#endif
+                    ) : static_cast<size_t>(-1);
+            }
+
+            template<class CharType>
+            size_t write(const std::basic_string<CharType>& str)
+            {
+                return write(str.data(), str.size() * sizeof(CharType));
+            }
+
+            size_t seek(size_t offset, int whence)
+            {
+                return m_file != -1 ? static_cast<size_t>(
+#if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__))
+                    ::_lseek(m_file, static_cast<off_t>(offset), whence)
+#elif defined(_WIN32)
+                    ::_lseeki64(m_file, static_cast<off_t>(offset), whence)
+#else
+                    ::lseek(m_file, static_cast<off_t>(offset), whence)
+#endif
+                    ) : static_cast<size_t>(-1);
+            }
+
+            void close()
+            {
+                if (m_file != -1)
+                {
+#ifdef _WIN32
+                    ::_close(m_file);
+#else
+                    ::close(m_file);
+#endif
+                    m_file = -1;
+                }
+            }
+
+            static int unlink(const nstring& fileName)
+            {
+#ifdef _WIN32
+                return ::_wunlink(toWide(fileName).c_str());
+#else
+                return ::unlink(fileName.c_str());
+#endif
+            }
+
+            static int rename(const nstring& oldFilename, const nstring& newFilename)
+            {
+#ifdef _WIN32
+                return MoveFileW(toWide(oldFilename).c_str(), toWide(newFilename).c_str());
+#else
+                return ::rename(oldFilename.c_str(), newFilename.c_str());
+#endif
+            }
+
+        private:
+            int m_file;
+        };
+
+        class PLOG_LINKAGE_HIDDEN Mutex : NonCopyable
+        {
+        public:
+            Mutex()
+            {
+#ifdef __FREERTOS__
+                m_sync = xSemaphoreCreateBinary();
+                xSemaphoreGive(m_sync);
+#elif defined(_WIN32)
+                InitializeCriticalSection(&m_sync);
+#elif defined(__rtems__)
+                rtems_semaphore_create(0, 1,
+                            RTEMS_PRIORITY |
+                            RTEMS_BINARY_SEMAPHORE |
+                            RTEMS_INHERIT_PRIORITY, 1, &m_sync);
+#elif defined(_POSIX_THREADS)
+                ::pthread_mutex_init(&m_sync, 0);
+#endif
+            }
+
+            ~Mutex()
+            {
+#ifdef __FREERTOS__
+                vSemaphoreDelete(m_sync);
+#elif defined(_WIN32)
+                DeleteCriticalSection(&m_sync);
+#elif defined(__rtems__)
+                rtems_semaphore_delete(m_sync);
+#elif defined(_POSIX_THREADS)
+                ::pthread_mutex_destroy(&m_sync);
+#endif
+            }
+
+            friend class MutexLock;
+
+        private:
+            void lock()
+            {
+#ifdef __FREERTOS__
+                xSemaphoreTake(m_sync, portMAX_DELAY);
+#elif defined(_WIN32)
+                EnterCriticalSection(&m_sync);
+#elif defined(__rtems__)
+                rtems_semaphore_obtain(m_sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+#elif defined(_POSIX_THREADS)
+                ::pthread_mutex_lock(&m_sync);
+#endif
+            }
+
+            void unlock()
+            {
+#ifdef __FREERTOS__
+                xSemaphoreGive(m_sync);
+#elif defined(_WIN32)
+                LeaveCriticalSection(&m_sync);
+#elif defined(__rtems__)
+                rtems_semaphore_release(m_sync);
+#elif defined(_POSIX_THREADS)
+                ::pthread_mutex_unlock(&m_sync);
+#endif
+            }
+
+        private:
+#ifdef __FREERTOS__
+            SemaphoreHandle_t m_sync;
+#elif defined(_WIN32)
+            CRITICAL_SECTION m_sync;
+#elif defined(__rtems__)
+            rtems_id m_sync;
+#elif defined(_POSIX_THREADS)
+            pthread_mutex_t m_sync;
+#endif
+        };
+
+        class PLOG_LINKAGE_HIDDEN MutexLock : NonCopyable
+        {
+        public:
+            MutexLock(Mutex& mutex) : m_mutex(mutex)
+            {
+                m_mutex.lock();
+            }
+
+            ~MutexLock()
+            {
+                m_mutex.unlock();
+            }
+
+        private:
+            Mutex& m_mutex;
+        };
+
+        template<class T>
+#ifdef _WIN32
+        class Singleton : NonCopyable
+#else
+        class PLOG_LINKAGE Singleton : NonCopyable
+#endif
+        {
+        public:
+#if (defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 8) && !defined(__BORLANDC__)
+            // This constructor is called before the `T` object is fully constructed, and
+            // pointers are not dereferenced anyway, so UBSan shouldn't check vptrs.
+            __attribute__((no_sanitize("vptr")))
+#endif
+            Singleton()
+            {
+                assert(!m_instance);
+                m_instance = static_cast<T*>(this);
+            }
+
+            ~Singleton()
+            {
+                assert(m_instance);
+                m_instance = 0;
+            }
+
+            static T* getInstance()
+            {
+                return m_instance;
+            }
+
+        private:
+            static T* m_instance;
+        };
+
+        template<class T>
+        T* Singleton<T>::m_instance = NULL;
+    }
+}

+ 175 - 0
src/3rd/plog/plog/WinApi.h

@@ -0,0 +1,175 @@
+#pragma once
+
+#ifdef _WIN32
+
+// These windows structs must be in a global namespace
+struct HKEY__;
+struct _SECURITY_ATTRIBUTES;
+struct _CONSOLE_SCREEN_BUFFER_INFO;
+struct _RTL_CRITICAL_SECTION;
+
+namespace plog
+{
+    typedef unsigned long DWORD;
+    typedef unsigned short WORD;
+    typedef unsigned char BYTE;
+    typedef unsigned int UINT;
+    typedef int BOOL;
+    typedef long LSTATUS;
+    typedef char* LPSTR;
+    typedef wchar_t* LPWSTR;
+    typedef const char* LPCSTR;
+    typedef const wchar_t* LPCWSTR;
+    typedef void* HANDLE;
+    typedef HKEY__* HKEY;
+    typedef size_t ULONG_PTR;
+
+    struct CRITICAL_SECTION
+    {
+        void* DebugInfo;
+        long LockCount;
+        long RecursionCount;
+        HANDLE OwningThread;
+        HANDLE LockSemaphore;
+        ULONG_PTR SpinCount;
+    };
+
+    struct COORD
+    {
+        short X;
+        short Y;
+    };
+
+    struct SMALL_RECT
+    {
+        short Left;
+        short Top;
+        short Right;
+        short Bottom;
+    };
+
+    struct CONSOLE_SCREEN_BUFFER_INFO
+    {
+        COORD dwSize;
+        COORD dwCursorPosition;
+        WORD  wAttributes;
+        SMALL_RECT srWindow;
+        COORD dwMaximumWindowSize;
+    };
+
+    namespace codePage
+    {
+        const UINT kActive = 0;
+        const UINT kUTF8 = 65001;
+#if PLOG_CHAR_IS_UTF8
+        const UINT kChar = kUTF8;
+#else
+        const UINT kChar = kActive;
+#endif
+    }
+
+    namespace eventLog
+    {
+        const WORD kErrorType = 0x0001;
+        const WORD kWarningType = 0x0002;
+        const WORD kInformationType = 0x0004;
+    }
+
+    namespace hkey
+    {
+        const HKEY kLocalMachine = reinterpret_cast<HKEY>(static_cast<ULONG_PTR>(0x80000002));
+    }
+
+    namespace regSam
+    {
+        const DWORD kQueryValue = 0x0001;
+        const DWORD kSetValue = 0x0002;
+    }
+
+    namespace regType
+    {
+        const DWORD kExpandSz = 2;
+        const DWORD kDword = 4;
+    }
+
+     namespace stdHandle
+    {
+        const DWORD kOutput = static_cast<DWORD>(-11);
+        const DWORD kErrorOutput = static_cast<DWORD>(-12);
+    }
+
+    namespace foreground
+    {
+        const WORD kBlue = 0x0001;
+        const WORD kGreen = 0x0002;
+        const WORD kRed = 0x0004;
+        const WORD kIntensity = 0x0008;
+    }
+
+    namespace background
+    {
+        const WORD kBlue = 0x0010;
+        const WORD kGreen = 0x0020;
+        const WORD kRed = 0x0040;
+        const WORD kIntensity = 0x0080;
+    }
+
+    extern "C"
+    {
+        __declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
+        __declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, BOOL* lpUsedDefaultChar);
+
+        __declspec(dllimport) DWORD __stdcall GetCurrentThreadId();
+
+        __declspec(dllimport) BOOL __stdcall MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName);
+
+        __declspec(dllimport) void __stdcall InitializeCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
+        __declspec(dllimport) void __stdcall EnterCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
+        __declspec(dllimport) void __stdcall LeaveCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
+        __declspec(dllimport) void __stdcall DeleteCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
+
+        __declspec(dllimport) HANDLE __stdcall RegisterEventSourceW(LPCWSTR lpUNCServerName, LPCWSTR lpSourceName);
+        __declspec(dllimport) BOOL __stdcall DeregisterEventSource(HANDLE hEventLog);
+        __declspec(dllimport) BOOL __stdcall ReportEventW(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, void* lpUserSid, WORD wNumStrings, DWORD dwDataSize, LPCWSTR* lpStrings, void* lpRawData);
+
+        __declspec(dllimport) LSTATUS __stdcall RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, DWORD samDesired, _SECURITY_ATTRIBUTES* lpSecurityAttributes, HKEY* phkResult, DWORD* lpdwDisposition);
+        __declspec(dllimport) LSTATUS __stdcall RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData);
+        __declspec(dllimport) LSTATUS __stdcall RegCloseKey(HKEY hKey);
+        __declspec(dllimport) LSTATUS __stdcall RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult);
+        __declspec(dllimport) LSTATUS __stdcall RegDeleteKeyW(HKEY hKey, LPCWSTR lpSubKey);
+
+        __declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle);
+
+        __declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const void* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten, void* lpReserved);
+        __declspec(dllimport) BOOL __stdcall GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, _CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo);
+        __declspec(dllimport) BOOL __stdcall SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
+
+        __declspec(dllimport) void __stdcall OutputDebugStringW(LPCWSTR lpOutputString);
+    }
+
+    inline void InitializeCriticalSection(CRITICAL_SECTION* criticalSection)
+    {
+        plog::InitializeCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
+    }
+
+    inline void EnterCriticalSection(CRITICAL_SECTION* criticalSection)
+    {
+        plog::EnterCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
+    }
+
+    inline void LeaveCriticalSection(CRITICAL_SECTION* criticalSection)
+    {
+        plog::LeaveCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
+    }
+
+    inline void DeleteCriticalSection(CRITICAL_SECTION* criticalSection)
+    {
+        plog::DeleteCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
+    }
+
+    inline BOOL GetConsoleScreenBufferInfo(HANDLE consoleOutput, CONSOLE_SCREEN_BUFFER_INFO* consoleScreenBufferInfo)
+    {
+        return plog::GetConsoleScreenBufferInfo(consoleOutput, reinterpret_cast<_CONSOLE_SCREEN_BUFFER_INFO*>(consoleScreenBufferInfo));
+    }
+}
+#endif // _WIN32

+ 474 - 0
src/graph/graph.hpp

@@ -0,0 +1,474 @@
+#ifndef GRAPH_HPP__
+#define GRAPH_HPP__
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include "common/json.hpp"
+#include "nodes/base/base.hpp"
+#include "nodes/stream/streamNode.hpp"
+#include "nodes/infer/inferNode.hpp"
+#include "nodes/analyze/analyzeNode.hpp"
+#include "nodes/draw/drawNode.hpp"
+#include "nodes/record/recordNode.hpp"
+#include "nodes/httpPush/httpPush.hpp"
+#include "nodes/track/trackNode.hpp"
+
+
+namespace Graph
+{
+
+// json format
+/*
+json 设计原则
+1. 同一条pipeline下的infer节点公用一个模型 
+2. 不同条pipeline下的infer节点不公用一个模型
+3. 相当于每条pipeline都是平行的,只是在infer节点上共用了同一个模型指针
+
+
+1. 每个pipeline都是平行的,没有交叉
+2. 每个pipeline的节点按顺序连接
+3. 每个pipeline可以公用一个模型指针,infer节点通过设置模型指针来共用模型
+4. 下面是我的设计思路,可以参考。请帮我修改完善,我觉得有些地方不太可以,但是我想不出更好的办法
+
+{
+  // ---------------------------------------------------------------------------
+  // 全局资源定义区:定义可被多个Pipeline共享的资源,例如模型
+  // ---------------------------------------------------------------------------
+  "shared_resources": {
+    "models": {
+      // 定义一个模型,给它一个唯一的ID,例如 "yolo_model_main"
+      "yolo_model_main": {
+        "model_path": "model/yolo11s.engine", // 模型文件路径
+        "model_type": "YOLO11",             // 模型类型
+        "names": ["person", "car", "close", "open"], // 类别名称
+        // 注意:GPU ID通常与推理实例相关,而不是模型本身。
+        // 如果模型加载强绑定GPU,可以放在这里,但更常见的是在推理节点实例中指定。
+        "gpu_id": 0,
+        "confidence_threshold": 0.25,
+        "nms_threshold": 0.45
+      }
+      // 如果有其他模型,可以在这里继续添加
+      // "another_model": { ... }
+    }
+    // 可以扩展添加其他共享资源,如特定的追踪器配置模板等
+    // "tracker_configs": { ... }
+  },
+
+  // ---------------------------------------------------------------------------
+  // Pipeline 定义区:一个包含所有独立Pipeline的数组
+  // ---------------------------------------------------------------------------
+  "pipelines": [
+    // --- Pipeline 0 ---
+    {
+      "pipeline_id": "pipeline_0", // 给Pipeline一个唯一标识符
+      "description": "处理摄像头0的视频流", // 可选的描述信息
+      // 使用一个节点数组来明确定义执行顺序
+      "nodes": [
+        {
+          "node_id": "src_0",          // 节点实例的唯一ID
+          "node_type": "Source",       // 节点类型(更通用的名称)
+          "params": {                  // 节点的具体参数
+            "stream_url": "rtsp://xxxx_cam0", // 使用具体的URL
+            "gpu_id": 0,               // 指定解码使用的GPU
+            "decode_type": "GPU",
+            "skip_frame": 10
+          }
+        },
+        {
+          "node_id": "infer_0",        // 节点实例的唯一ID
+          "node_type": "Inference",    // 节点类型
+          "params": {
+            // 通过 model_id 引用上面定义的共享模型
+            "model_id": "yolo_model_main",
+          }
+        },
+        {
+          "node_id": "track_0",        // 节点实例的唯一ID
+          "node_type": "Tracker",      // 节点类型
+          "params": {
+            "track_name": "person",    // 需要追踪的目标类别
+            "track_frame": 30,         // 追踪相关的帧数参数(具体含义需根据实现确定,可能是最大丢失帧数)
+            "track_distance": 30       // 追踪相关的距离参数(具体含义和单位需根据实现确定)
+          }
+        },
+        {
+          "node_id": "analyze_0",      // 节点实例的唯一ID
+          "node_type": "Analyzer",     // 节点类型
+          "params": {
+            // 分析节点的特定参数(如果需要)
+          }
+        },
+        {
+          "node_id": "draw_0",         // 节点实例的唯一ID
+          "node_type": "Drawer",       // 节点类型
+          "params": {
+            // 绘制节点的特定参数(如果需要)
+          }
+        },
+        {
+          "node_id": "record_0",       // 节点实例的唯一ID
+          "node_type": "Recorder",     // 节点类型
+          "params": {
+            // 建议为每个pipeline使用不同的输出文件路径
+            "record_path": "result/result_pipeline0.mp4"
+          }
+        }
+      ] // end of nodes for pipeline_0
+    }, // end of pipeline_0
+
+    // --- Pipeline 1 ---
+    {
+      "pipeline_id": "pipeline_1", // 唯一标识符
+      "description": "处理摄像头1的视频流",
+      "nodes": [
+        {
+          "node_id": "src_1",          // 唯一节点ID
+          "node_type": "Source",
+          "params": {
+            "stream_url": "rtsp://xxxx_cam1", // 不同的URL
+            "gpu_id": 0,               // 可以是同一个GPU,也可以是不同的
+            "decode_type": "GPU",
+            "skip_frame": 10
+          }
+        },
+        {
+          "node_id": "infer_1",        // 唯一节点ID
+          "node_type": "Inference",
+          "params": {
+            // 引用同一个共享模型
+            "model_id": "yolo_model_main",
+            
+          }
+        },
+        {
+          "node_id": "track_1",        // 唯一节点ID
+          "node_type": "Tracker",
+          // 即使参数相同,这也是一个独立的追踪器实例,处理来自 src_1 的数据
+          "params": {
+            "track_name": "person",
+            "track_frame": 30,
+            "track_distance": 30
+          }
+        },
+        {
+          "node_id": "analyze_1",      // 唯一节点ID
+          "node_type": "Analyzer",
+          "params": {}
+        },
+        {
+          "node_id": "draw_1",         // 唯一节点ID
+          "node_type": "Drawer",
+          "params": {}
+        },
+        {
+          "node_id": "record_1",       // 唯一节点ID
+          "node_type": "Recorder",
+          "params": {
+            "record_path": "result/result_pipeline1.mp4" // 不同的输出路径
+          }
+        }
+      ] // end of nodes for pipeline_1
+    } // end of pipeline_1
+
+    // 可以根据需要添加更多的 Pipeline 定义 ...
+
+  ] // end of pipelines
+}
+
+void load_from_json(const std::string& json_path)
+    {
+        auto json_data = nlohmann::json::parse(json_path);
+        auto shared_resources = json_data["shared_resources"];
+        auto models = shared_resources["models"];
+        for (auto& model : models.items())
+        {
+            std::string model_id = model.key();
+            std::string model_path = model.value()["model_path"];
+            std::string model_type = model.value()["model_type"];
+            std::vector<std::string> names = model.value()["names"];
+            int gpu_id = model.value()["gpu_id"];
+            float confidence_threshold = model.value()["confidence_threshold"];
+            float nms_threshold = model.value()["nms_threshold"];
+            std::shared_ptr<Infer> infer = load(model_path, model_type, names, gpu_id, confidence_threshold, nms_threshold);
+            shared_models_[model_id] = infer;
+        }
+
+        auto pipelines = json_data["pipelines"];
+        
+    }
+
+*/
+
+namespace Graph
+{
+    // --- 内部结构,用于存储单个配置好的 Pipeline ---
+    struct PipelineInstance {
+        std::string pipeline_id;
+        std::string description;
+        std::vector<std::shared_ptr<GNode::BaseNode>> nodes; // 只包含此 pipeline 的节点,按顺序
+    };
+
+class Graph
+{
+public:
+    Graph() = default;
+    ~Graph() {
+        // 如果需要,在这里添加清理逻辑,比如停止所有节点
+        std::cout << "Graph destructor called. Consider adding node stopping logic here." << std::endl;
+    }
+
+
+    // 获取配置好的 pipelines (只读)
+    const std::vector<PipelineInstance>& getPipelines() const {
+        return configured_pipelines_;
+    }
+
+    // 获取加载的共享模型 (只读)
+    const std::map<std::string, std::shared_ptr<GNode::Infer>>& getSharedModels() const {
+        return shared_models_;
+    }
+
+
+    void load_from_json(const std::string& json_path)
+    {
+        std::cout << "Loading graph configuration from: " << json_path << std::endl;
+        std::ifstream ifs(json_path);
+        if (!ifs.is_open()) {
+            throw std::runtime_error("Failed to open JSON file: " + json_path);
+        }
+
+        nlohmann::json json_data;
+        try {
+            json_data = nlohmann::json::parse(ifs);
+        } catch (const nlohmann::json::parse_error& e) {
+            throw std::runtime_error("Failed to parse JSON: " + std::string(e.what()));
+        }
+
+        // --- 1. 加载共享模型 ---
+        std::cout << "\n--- Loading Shared Models ---" << std::endl;
+        shared_models_.clear(); // 清空旧模型
+        try {
+            // 安全地访问 JSON 结构
+            const auto models_json = json_data.value("shared_resources", nlohmann::json::object()).value("models", nlohmann::json::object());
+
+            for (auto& [model_id, model_config] : models_json.items())
+            {
+                std::cout << "Processing shared model definition: " << model_id << std::endl;
+                std::string model_path = model_config.value("model_path", "");
+                std::string model_type_str = model_config.value("model_type", ""); // JSON中的模型类型字符串
+                std::vector<std::string> names;
+                 if(model_config.contains("names") && model_config["names"].is_array()){
+                     names = model_config["names"].get<std::vector<std::string>>();
+                 } else {
+                      std::cerr << "Warning: Model '" << model_id << "' missing 'names' array." << std::endl;
+                 }
+                int gpu_id = model_config.value("gpu_id", 0);
+                float confidence_threshold = model_config.value("confidence_threshold", 0.25f);
+                float nms_threshold = model_config.value("nms_threshold", 0.45f);
+
+                if (model_path.empty() || model_type_str.empty()) {
+                    std::cerr << "Warning: Skipping model '" << model_id << "' due to missing 'model_path' or 'model_type'." << std::endl;
+                    shared_models_[model_id] = nullptr;
+                    continue;
+                }
+
+                // 调用模型加载函数 (全局或静态/成员)
+                std::shared_ptr<GNode::Infer> infer_ptr = ::load(model_path, model_type_str, names, gpu_id, confidence_threshold, nms_threshold);
+                shared_models_[model_id] = infer_ptr;
+
+                if (!infer_ptr) {
+                    std::cerr << "ERROR: Model '" << model_id << "' failed to load." << std::endl;
+                    // 根据需要决定是否抛出异常终止
+                    // throw std::runtime_error("Failed to load critical model: " + model_id);
+                } else {
+                    std::cout << "Successfully loaded shared model: " << model_id << std::endl;
+                }
+            }
+        } catch (const std::exception& e) {
+             std::cerr << "ERROR during shared model loading: " << e.what() << std::endl;
+             throw; // 重新抛出
+        }
+
+        // --- 2. 加载并构建 Pipelines ---
+        std::cout << "\n--- Configuring Pipelines ---" << std::endl;
+        configured_pipelines_.clear(); // 清空旧的 pipelines
+
+        try {
+            const auto pipelines_json = json_data.value("pipelines", nlohmann::json::array());
+
+            for (const auto& pipeline_config : pipelines_json) {
+                std::string pipeline_id = pipeline_config.value("pipeline_id", "unknown_pipeline");
+                std::cout << "\nConfiguring pipeline: " << pipeline_id << std::endl;
+
+                PipelineInstance current_pipeline; // 创建当前 pipeline 的实例结构
+                current_pipeline.pipeline_id = pipeline_id;
+                current_pipeline.description = pipeline_config.value("description", "");
+                // current_pipeline.nodes 会在下面填充
+
+                const auto nodes_json = pipeline_config.value("nodes", nlohmann::json::array());
+                if (nodes_json.empty()) {
+                     std::cerr << "Warning: Pipeline '" << pipeline_id << "' has no nodes defined." << std::endl;
+                     continue;
+                }
+
+                // --- 2.1 创建当前 pipeline 的所有节点实例 ---
+                std::vector<std::shared_ptr<GNode::BaseNode>> temp_nodes_for_this_pipeline; // 临时存储当前pipeline的节点
+                for (const auto& node_config : nodes_json) {
+                    std::string node_id = node_config.value("node_id", "unknown_node");
+                    std::string node_type = node_config.value("node_type", "");
+                    const auto params = node_config.value("params", nlohmann::json::object());
+
+                    std::shared_ptr<GNode::BaseNode> node_ptr = nullptr;
+
+                    if (node_type.empty()) {
+                         std::cerr << "ERROR: Node in pipeline '" << pipeline_id << "' is missing 'node_type'. Skipping node." << std::endl;
+                         continue;
+                    }
+
+                    std::cout << "  Creating node: " << node_id << " (Type: " << node_type << ")" << std::endl;
+
+                    // 根据类型创建节点,并添加到 *临时* 列表
+                    if (node_type == "Source") {
+                        std::string stream_url = params.value("stream_url", "");
+                        int gpu_id = params.value("gpu_id", 0);
+                        std::string decode_type = params.value("decode_type", "GPU");
+                        int skip_frame = params.value("skip_frame", 0);
+                        if (stream_url.empty()) throw std::runtime_error("Source node '" + node_id + "' missing 'stream_url'.");
+
+                        auto src_node = std::make_shared<GNode::StreamNode>(node_id, stream_url, gpu_id, decode_type);
+                        src_node->set_skip_frame(skip_frame);
+                        node_ptr = src_node;
+                    }
+                    else if (node_type == "Inference") {
+                        std::string model_id = params.value("model_id", "");
+                        if (model_id.empty()) throw std::runtime_error("Inference node '" + node_id + "' missing 'model_id'.");
+
+                        auto it = shared_models_.find(model_id);
+                        if (it == shared_models_.end()) throw std::runtime_error("Node '" + node_id + "' references unknown model_id '" + model_id + "'.");
+                        if (!it->second) throw std::runtime_error("Node '" + node_id + "' references model '" + model_id + "' which failed to load or is null.");
+
+                        auto infer_node = std::make_shared<GNode::InferNode>(node_id);
+
+                        // --- 模型类型转换 ---
+                        // 从 shared_resources 中获取模型类型字符串
+                        std::string model_type_str_from_shared = json_data["shared_resources"]["models"][model_id].value("model_type", "UNKNOWN");
+                        GNode::ModelType model_enum_type = GNode::ModelType::YOLO11; // 默认值
+                        if (model_type_str_from_shared == "YOLO11") { // 这里进行字符串到枚举的映射
+                            model_enum_type = GNode::ModelType::YOLO11;
+                        } // else if (model_type_str == "ANOTHER_TYPE") { model_enum_type = GNode::ModelType::ANOTHER_ENUM; }
+                        else {
+                             std::cerr << "Warning: Unknown or unhandled model_type string '" << model_type_str_from_shared
+                                       << "' for model_id '" << model_id << "'. Using default." << std::endl;
+                        }
+                        // --- 结束模型类型转换 ---
+
+                        infer_node->set_model_instance(it->second, model_enum_type);
+                        node_ptr = infer_node;
+                    }
+                    else if (node_type == "Tracker") {
+                        std::string track_name = params.value("track_name", "person");
+                        int track_frame = params.value("track_frame", 30);
+                        int track_distance = params.value("track_distance", 30);
+                        node_ptr = std::make_shared<GNode::TrackNode>(node_id, track_name, track_frame, track_distance);
+                    }
+                    else if (node_type == "Analyzer") {
+                        node_ptr = std::make_shared<GNode::AnalyzeNode>(node_id);
+                        // 解析 Analyzer 特有参数 (如果 params 中有定义)
+                        // example: int threshold = params.value("threshold", 10);
+                    }
+                    else if (node_type == "Drawer") {
+                        node_ptr = std::make_shared<GNode::DrawNode>(node_id);
+                        // 解析 Drawer 特有参数
+                    }
+                    else if (node_type == "Recorder") {
+                        std::string record_path = params.value("record_path", "");
+                        if (record_path.empty()) throw std::runtime_error("Recorder node '" + node_id + "' missing 'record_path'.");
+                        auto record_node = std::make_shared<GNode::RecordNode>(node_id);
+                        record_node->set_record_path(record_path);
+                        node_ptr = record_node;
+                    }
+                    // else if (node_type == "HttpPush") { // 如果需要 HttpPush 节点
+                    //     std::string url = params.value("url", "");
+                    //     if (url.empty()) throw std::runtime_error("HttpPush node '" + node_id + "' missing 'url'.");
+                    //     node_ptr = std::make_shared<GNode::HttpPushNode>(node_id, url); // 假设构造函数是这样
+                    // }
+                     else {
+                         std::cerr << "ERROR: Unknown node_type '" << node_type << "' for node '" << node_id << "'. Skipping." << std::endl;
+                         continue; // 跳过未知类型
+                     }
+
+                    // 将成功创建的节点添加到当前 pipeline 的临时列表中
+                    temp_nodes_for_this_pipeline.push_back(node_ptr);
+                    std::cout << "    Added node '" << node_id << "' to temporary list for pipeline '" << pipeline_id << "'" << std::endl;
+
+                } // --- 结束当前 pipeline 的节点创建循环 ---
+
+
+                // --- 2.2 连接当前 pipeline 内部的节点 ---
+                std::cout << "  Linking nodes for pipeline: " << pipeline_id << std::endl;
+                if (temp_nodes_for_this_pipeline.size() > 1) {
+                    for (size_t i = 0; i < temp_nodes_for_this_pipeline.size() - 1; ++i) {
+                        // 使用 GNode::LinkNode 连接临时列表中的节点
+                        GNode::LinkNode(temp_nodes_for_this_pipeline[i], temp_nodes_for_this_pipeline[i + 1]);
+                    }
+                } else {
+                     std::cout << "  Pipeline '" << pipeline_id << "' has " << temp_nodes_for_this_pipeline.size() << " node(s), linking skipped/not needed." << std::endl;
+                 }
+
+                // --- 2.3 将节点列表存入 PipelineInstance 并保存 ---
+                if (!temp_nodes_for_this_pipeline.empty()) {
+                    current_pipeline.nodes = std::move(temp_nodes_for_this_pipeline); // 移动节点列表
+                    configured_pipelines_.push_back(std::move(current_pipeline)); // 保存配置好的 pipeline 实例
+                    std::cout << "Successfully configured and stored pipeline: " << pipeline_id << std::endl;
+                } else {
+                     std::cerr << "Warning: Pipeline '" << pipeline_id << "' ended up with no valid nodes. Skipping storage." << std::endl;
+                }
+
+            } // --- 结束所有 pipeline 的配置循环 ---
+
+        } catch (const std::exception& e) {
+             std::cerr << "ERROR during pipeline configuration: " << e.what() << std::endl;
+             // 清理状态
+             configured_pipelines_.clear();
+             shared_models_.clear();
+             throw; // 重新抛出
+        }
+
+        // --- 3. 启动所有 Pipeline 的节点 ---
+        std::cout << "\n--- Starting All Configured Pipelines ---" << std::endl;
+        if (configured_pipelines_.empty()) {
+            std::cout << "No pipelines were configured to start." << std::endl;
+            return;
+        }
+
+        for (auto& pipeline : configured_pipelines_) {
+            std::cout << "Starting nodes for pipeline: " << pipeline.pipeline_id << std::endl;
+            // 从后往前启动当前 pipeline 的节点
+            for (int i = static_cast<int>(pipeline.nodes.size()) - 1; i >= 0; --i) {
+                if (pipeline.nodes[i]) {
+                    // 假设 GNode::BaseNode 有 start() 方法
+                    pipeline.nodes[i]->start();
+                }
+            }
+        }
+
+        std::cout << "\nGraph configuration loading and pipeline starting finished." << std::endl;
+        std::cout << "Total shared models loaded (or attempted): " << shared_models_.size() << std::endl;
+        std::cout << "Total pipelines configured and started: " << configured_pipelines_.size() << std::endl;
+    }
+
+private:
+    // 存储共享模型指针 <model_id, model_instance_ptr>
+    std::map<std::string, std::shared_ptr<GNode::Infer>> shared_models_;
+
+    // 存储所有配置好的 Pipeline 实例
+    std::vector<PipelineInstance> configured_pipelines_;
+
+    // 这个全局 nodes_ 列表不再符合多 pipeline 的设计,应移除或注释掉
+    // std::vector<std::shared_ptr<GNode::BaseNode>> nodes_;
+};
+
+}   // namespace Graph
+
+#endif // GRAPH_HPP__

+ 2 - 0
src/infer/trt/yolo/yolo.hpp

@@ -47,6 +47,8 @@ namespace yolo
     
         int num_classes_ = 0;
 
+        int device_id_ = 0;
+
         YoloModelImpl() = default;
     
         virtual ~YoloModelImpl() = default;

+ 1 - 1
src/nodes/analyze/analyzeNode.hpp

@@ -11,7 +11,7 @@ class AnalyzeNode : public BaseNode
 {
 public:
     AnalyzeNode() = delete;
-    AnalyzeNode(const std::string& name) : BaseNode(name, NODE_TYPE::MID_NODE) {}
+    AnalyzeNode(const std::string& name) : BaseNode(name, NODE_TYPE::MID_NODE) { }
     virtual ~AnalyzeNode() { stop(); };
 
     void work() override;

+ 3 - 0
src/nodes/analyze/function/cross.cpp

@@ -1,6 +1,9 @@
 #include "nodes/analyze/function/cross.hpp"
 #include "opencv2/opencv.hpp"
 #include <unordered_map>
+
+
+
 data::BoxArray person_cross_line(const data::BoxArray& boxes, const cv::Point& line_start, const cv::Point& line_end)
 {
     // 记录之前人在线的那一侧

+ 10 - 0
src/nodes/analyze/function/cross.hpp

@@ -12,6 +12,16 @@ enum class Side
     RIGHT = 1
 };
 
+class PersonCross{
+public:
+    PersonCress() = default;
+    ~PersonCress() = default;
+    data::BoxArray person_cross_line(const data::BoxArray& boxes, const cv::Point& line_start, const cv::Point& line_end);
+
+private:
+    std::unordered_map<int, Side> person_side;
+};
+
 data::BoxArray person_cross_line(const data::BoxArray& boxes, const cv::Point& line_start, const cv::Point& line_end);
 
 #endif // CROSS_HPP__

+ 3 - 0
src/nodes/infer/inferNode.cpp

@@ -30,6 +30,9 @@ void InferNode::work()
         printf("model is nullptr\n");
         return;
     }
+    // 不同线程都需要指定显卡id,默认为0号显卡
+    // 在主线程中创建的模型,创建时指定了显卡id
+    // 推理的时候创建了另一个线程,需要指定同样的显卡id
     checkRuntime(cudaSetDevice(device_id_));
     while (running_)
     {