From 32cf1e681fc04f5e5d61009fdecbc6a13fa51fd9 Mon Sep 17 00:00:00 2001
From: Denis Sumin <denis.smolov@demlabs.net>
Date: Thu, 17 Aug 2023 09:37:41 +0000
Subject: [PATCH] Features 9364

---
 .gitignore                      |   1 +
 .gitlab-ci.yml                  |   6 +-
 CMakeLists.txt                  |  15 +-
 diagtool/AbstractDiagnostic.cpp | 223 +++++++++++++++++++++++++-
 diagtool/AbstractDiagnostic.h   |  14 ++
 diagtool/CMakeLists.txt         |  41 ++++-
 diagtool/DiagnosticWorker.cpp   |  10 +-
 diagtool/DiagnosticWorker.h     |   9 +-
 diagtool/LinuxDiagnostic.cpp    | 208 +-----------------------
 diagtool/LinuxDiagnostic.h      |  15 +-
 diagtool/MacDiagnostic.cpp      | 243 ++++++++++++++++++++++++++++
 diagtool/MacDiagnostic.h        |  36 +++++
 diagtool/NodeDiagnostic.pro     |  30 +++-
 diagtool/WinDiagnostic.cpp      | 274 ++++++++++++++++++++++++++++++++
 diagtool/WinDiagnostic.h        |  38 +++++
 diagtool/main.cpp               |   1 +
 version.mk                      |   2 +-
 17 files changed, 920 insertions(+), 246 deletions(-)
 create mode 100644 diagtool/MacDiagnostic.cpp
 create mode 100644 diagtool/MacDiagnostic.h
 create mode 100644 diagtool/WinDiagnostic.cpp
 create mode 100644 diagtool/WinDiagnostic.h

diff --git a/.gitignore b/.gitignore
index 6a60feef0..5223da1bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ test/build
 *.txt.user
 *.txt.user.*
 *.autosave
+*.user
 .vscode/
 # Object files
 *.o
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b05a1e422..d5efcdf77 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -42,17 +42,17 @@ stages:
 
 windows-amd64:
     extends: .build
-    image: demlabs/amd64/debian-bullseye:windowsbuilder
+    image: demlabs/amd64/debian-bullseye:windowsbuilder 
     before_script: /opt/buildtools/prepare_environment.sh amd64-linux
     script:
-      - ./prod_build/build.sh --target windows release
+      - ./prod_build/build.sh --target windows release -DBUILD_DIAGTOOL=ON
 
 macos-amd64:
     extends: .build
     image: demlabs/amd64/debian-bullseye:osxbuilder
     before_script: /opt/buildtools/prepare_environment.sh amd64-osx
     script:
-      - ./prod_build/build.sh --target osx release
+      - ./prod_build/build.sh --target osx release -DBUILD_DIAGTOOL=ON
       - ./prod_build/pack.sh --target osx release --sign /opt/buildtools/sign/macos/demlabs.sh
     artifacts:
       paths:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8998a0a3..24e844e0a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -228,6 +228,10 @@ endif()
 target_link_libraries(${NODE_TARGET}      ${NODE_LIBRARIES} crc32c_adler )
 target_link_libraries(${NODE_TOOL_TARGET} ${NODE_TOOL_LIBRARIES} crc32c_adler )
 
+if(BUILD_DIAGTOOL)
+    message("[*] Diagtool build on")
+    add_subdirectory(diagtool)
+endif()
 
 if(UNIX AND NOT WIN32)
     message ("[*] Unix library set")
@@ -237,11 +241,6 @@ if(UNIX AND NOT WIN32)
     set(NODE_CLI_LIBRARIES m cellframe-sdk)
 
     set(NODE_TOOL_LIBRARIES m cellframe-sdk)
-    
-    if(BUILD_DIAGTOOL)
-        message("[*] Diagtool build on")
-        add_subdirectory(diagtool)
-    endif()
 
     if (SUPPORT_PYTHON_PLUGINS)
         message("[+] Build with python plugins support")
@@ -369,7 +368,11 @@ else()
     INSTALL(TARGETS ${NODE_TOOL_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin )
 
     if (BUILD_DIAGTOOL)
-        INSTALL(TARGETS cellframe-diagtool DESTINATION ${CMAKE_INSTALL_PREFIX}/bin )
+        if(WIN32)
+            INSTALL(TARGETS cellframe-diagtool.exe DESTINATION ${CMAKE_INSTALL_PREFIX}/bin )
+        else()
+            INSTALL(TARGETS cellframe-diagtool DESTINATION ${CMAKE_INSTALL_PREFIX}/bin )
+        endif()
     endif()
 
     if(NOT ANDROID)
diff --git a/diagtool/AbstractDiagnostic.cpp b/diagtool/AbstractDiagnostic.cpp
index b175b29ed..fdb51df4a 100644
--- a/diagtool/AbstractDiagnostic.cpp
+++ b/diagtool/AbstractDiagnostic.cpp
@@ -1,8 +1,23 @@
 #include "AbstractDiagnostic.h"
 
+#ifdef Q_OS_LINUX
+
+#elif defined(Q_OS_WIN)
+#include "registry.h"
+#elif defined(Q_OS_MACOS)
+#include "dap_common.h"
+#endif
+
 AbstractDiagnostic::AbstractDiagnostic(QObject *parent)
     :QObject(parent)
 {
+#ifdef Q_OS_LINUX
+    s_nodeDataPath = "/opt/cellframe-node";
+#elif defined(Q_OS_WIN)
+    s_nodeDataPath = QString("%1/cellframe-node").arg(regGetUsrPath());
+#elif defined(Q_OS_MACOS)
+    s_nodeDataPath = QString("/Users/%1/Applications/Cellframe.app/Contents/Resources/").arg(getenv("USER"));
+#endif
     s_timer_update = new QTimer();
     s_mac = get_mac();
 }
@@ -127,7 +142,7 @@ QJsonObject AbstractDiagnostic::roles_processing()
 {
     QJsonObject rolesObject;
 
-    QDir currentFolder("/opt/cellframe-node/etc/network");
+    QDir currentFolder(s_nodeDataPath + "/etc/network");
 
     currentFolder.setFilter( QDir::Dirs | QDir::Files | QDir::NoSymLinks );
     currentFolder.setSorting( QDir::Name );
@@ -148,3 +163,209 @@ QJsonObject AbstractDiagnostic::roles_processing()
 
     return rolesObject;
 }
+
+/// ---------------------------------------------------------------
+///        Cli info
+/// ---------------------------------------------------------------
+QJsonObject AbstractDiagnostic::get_cli_info()
+{
+    QStringList networks = get_networks();
+
+    QJsonObject netObj;
+
+    for(QString net : networks)
+    {
+        QJsonObject dataObj;
+
+        dataObj.insert("net_info", get_net_info(net));
+        dataObj.insert("mempool" , get_mempool_count(net));
+//        dataObj.insert("ledger"  , get_ledger_count(net));
+        dataObj.insert("blocks"  , get_blocks_count(net));
+        dataObj.insert("events"  , get_events_count(net));
+        dataObj.insert("nodelist"  , get_nodelist(net));
+
+        netObj.insert(net, dataObj);
+    }
+
+    return netObj;
+}
+
+QStringList AbstractDiagnostic::get_networks()
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH), QStringList()<<"net"<<"list");
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    QStringList listNetworks;
+    result.remove(' ');
+    result.remove("\r");
+    result.remove("\n");
+    result.remove("Networks:");
+    if(!(result.isEmpty() || result.isNull() || result.contains('\'') || result.contains("error") || result.contains("Error") || result.contains("err")))
+    {
+        listNetworks = result.split("\t", QString::SkipEmptyParts);
+    }
+
+    return listNetworks;
+}
+
+QJsonObject AbstractDiagnostic::get_net_info(QString net)
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH),
+               QStringList()<<"net"<<"-net"<<QString(net)<<"get"<<"status");
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    // ---------- State & TargetState ----------------
+
+    QRegularExpression rx(R"***(^Network "(\S+)" has state (\S+) \(target state (\S*)\), .*cur node address ([A-F0-9]{4}::[A-F0-9]{4}::[A-F0-9]{4}::[A-F0-9]{4}))***");
+    QRegularExpressionMatch match = rx.match(result);
+    if (!match.hasMatch()) {
+        return {};
+    }
+
+    QJsonObject resultObj({
+                                {"state"              , match.captured(2)},
+                                {"target_state"       , match.captured(3)},
+                                {"node_address"       , match.captured(4)}
+                            });
+
+    // ---------- Links count ----------------
+    QRegularExpression rxLinks(R"(\), active links (\d+) from (\d+),)");
+    match = rxLinks.match(result);
+    if (!match.hasMatch()) {
+        return resultObj;
+    }
+
+    resultObj.insert("active_links_count", match.captured(1));
+    resultObj.insert("links_count"       , match.captured(2));
+    resultObj.insert("balancer", get_balancer_links(net));
+
+    return resultObj;
+}
+
+QJsonObject AbstractDiagnostic::get_mempool_count(QString net)
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH),
+               QStringList()<<"mempool_list"<<"-net"<<QString(net));
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    QRegularExpression rx(R"(\.(.+): Total (.+) records)");
+
+    ///TODO: bug in requests. Always returns both chains
+//    QRegularExpressionMatch match = rx.match(result);
+//    if (!match.hasMatch()) {
+//        return {};
+//    }
+
+//    proc.start(QString(CLI_PATH),
+//               QStringList()<<"mempool_list"<<"-net"<<QString(net)<<"-chain"<<"zero");
+//    proc.waitForFinished(5000);
+//    result = proc.readAll();
+
+    QJsonObject resultObj;
+
+    QRegularExpressionMatchIterator matchItr = rx.globalMatch(result);
+
+    while (matchItr.hasNext())
+    {
+        QRegularExpressionMatch match = matchItr.next();
+        resultObj.insert(match.captured(1), match.captured(2));
+    }
+
+    return resultObj;
+}
+
+QJsonObject AbstractDiagnostic::get_ledger_count(QString net)
+{
+    //TODO: legder tx -all -net   NOT WORKING
+    return {};
+}
+
+QJsonObject AbstractDiagnostic::get_blocks_count(QString net)
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH),
+               QStringList()<<"block"<<"list"<<"-net"<<QString(net) <<"-chain" << "main");
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    QRegularExpression rx(R"(\.(.+): Have (.+) blocks)");
+    QRegularExpressionMatch match = rx.match(result);
+    if (!match.hasMatch()) {
+        return {};
+    }
+
+    QJsonObject resultObj;
+    resultObj.insert(match.captured(1), match.captured(2));
+
+    return resultObj;
+
+}
+
+QJsonObject AbstractDiagnostic::get_events_count(QString net)
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH),
+               QStringList()<<"dag"<<"event"<<"list"<<"-net"<<QString(net) <<"-chain" << "zerochain");
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    QRegularExpression rx(R"(\.(.+) have total (.+) events)");
+    QRegularExpressionMatch match = rx.match(result);
+    if (!match.hasMatch()) {
+        return {};
+    }
+
+    QJsonObject resultObj;
+    resultObj.insert(match.captured(1), match.captured(2));
+
+    return resultObj;
+}
+
+
+QJsonArray AbstractDiagnostic::get_nodelist(QString net)
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH),
+               QStringList()<<"node"<<"dump"<<"-net"<<QString(net));
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    QJsonArray res;
+    for (auto l : result.split("\n"))
+        res.push_back(QJsonValue(l));
+
+    return res;
+}
+
+QJsonObject AbstractDiagnostic::get_balancer_links(QString net)
+{
+    QProcess proc;
+    proc.start(QString(CLI_PATH),
+                QStringList()<<"node"<<"connections"<<"-net"<<QString(net));
+
+
+    proc.waitForFinished(5000);
+    QString result = proc.readAll();
+
+    QJsonObject resultObj;
+    for (auto line : result.split("\n"))
+    {
+        if (line.split(":").length() < 2)
+            continue;
+
+        if(line.startsWith("Uplinks:"))
+            resultObj.insert("uplinks", line.split(":")[1].trimmed());
+        if(line.startsWith("Downlinks:"))
+            resultObj.insert("downlinks", line.split(":")[1].trimmed());
+    }
+
+    qDebug() << resultObj;
+
+    return resultObj;
+}
diff --git a/diagtool/AbstractDiagnostic.h b/diagtool/AbstractDiagnostic.h
index 197b44613..b00068f51 100644
--- a/diagtool/AbstractDiagnostic.h
+++ b/diagtool/AbstractDiagnostic.h
@@ -39,11 +39,25 @@ public:
 
     QJsonObject roles_processing();
 
+    //CLI
+    QJsonObject get_cli_info();
+
+    QStringList get_networks();
+    QJsonObject get_net_info(QString net);
+    QJsonObject get_mempool_count(QString net);
+    QJsonObject get_ledger_count(QString net);
+    QJsonObject get_blocks_count(QString net);
+    QJsonObject get_events_count(QString net);
+    QJsonArray get_nodelist(QString net);
+    QJsonObject get_balancer_links(QString net);
+
 public:
     QTimer * s_timer_update;
     int s_timeout{1000};
     QJsonDocument s_full_info;
     QJsonValue s_mac;
+    bool s_node_status{false};
+    QString s_nodeDataPath{""};
 
 signals:
     void data_updated(QJsonDocument);
diff --git a/diagtool/CMakeLists.txt b/diagtool/CMakeLists.txt
index 76aea6192..ad329d3a5 100644
--- a/diagtool/CMakeLists.txt
+++ b/diagtool/CMakeLists.txt
@@ -9,12 +9,41 @@ find_package(Qt5 5.15 REQUIRED COMPONENTS
 
 set(CMAKE_AUTOMOC ON)
 
-add_executable(${PROJECT_NAME}
-    main.cpp
-    DiagnosticWorker.cpp
-    AbstractDiagnostic.cpp
-    LinuxDiagnostic.cpp
-)
+
+
+if(UNIX)
+    if(DARWIN)
+        add_definitions(-DCLI_PATH="./cellframe-node-cli")
+        add_executable(${PROJECT_NAME}
+            main.cpp
+            DiagnosticWorker.cpp
+            AbstractDiagnostic.cpp
+            MacDiagnostic.cpp
+        )
+    endif()
+
+    if(LINUX)
+        add_definitions(-DCLI_PATH="/opt/cellframe-node/bin/cellframe-node-cli")
+        add_executable(${PROJECT_NAME}
+            main.cpp
+            DiagnosticWorker.cpp
+            AbstractDiagnostic.cpp
+            LinuxDiagnostic.cpp
+        )
+    endif()
+
+endif()
+
+if(WIN32)
+    add_definitions(-DCLI_PATH="cellframe-node-cli.exe")
+    add_executable(${PROJECT_NAME}
+        main.cpp
+        DiagnosticWorker.cpp
+        AbstractDiagnostic.cpp
+        WinDiagnostic.cpp
+    )
+endif()
+
 
 target_link_libraries(${PROJECT_NAME}
     Qt5::Core Qt5::Network
diff --git a/diagtool/DiagnosticWorker.cpp b/diagtool/DiagnosticWorker.cpp
index be0c71a3c..0768437f2 100644
--- a/diagtool/DiagnosticWorker.cpp
+++ b/diagtool/DiagnosticWorker.cpp
@@ -17,14 +17,20 @@ DiagnosticWorker::DiagnosticWorker(QObject * parent)
     s_elapsed_timer = new QElapsedTimer();
     s_elapsed_timer->start();
 
+#ifdef Q_OS_LINUX
     m_diagnostic = new LinuxDiagnostic();
+#elif defined Q_OS_WIN
+    m_diagnostic = new WinDiagnostic();
+#elif defined Q_OS_MAC
+    m_diagnostic = new MacDiagnostic();
+#endif
 
     connect(m_diagnostic, &AbstractDiagnostic::data_updated,
             this, &DiagnosticWorker::slot_diagnostic_data,
             Qt::QueuedConnection);
 
     m_diagnostic->start_diagnostic();
-    m_diagnostic->set_timeout(30000); //3minutes
+    m_diagnostic->set_timeout(30000); //30 sec
 }
 DiagnosticWorker::~DiagnosticWorker()
 {
@@ -52,7 +58,7 @@ void DiagnosticWorker::slot_diagnostic_data(QJsonDocument data)
     if(m_node_version.isEmpty())
     {
         QProcess proc;
-        proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), QStringList()<<"version");
+        proc.start(QString(CLI_PATH), QStringList()<<"version");
         proc.waitForFinished(5000);
         QString result = proc.readAll();
         if(result.contains("version"))
diff --git a/diagtool/DiagnosticWorker.h b/diagtool/DiagnosticWorker.h
index 01341f29c..2216d1cfe 100644
--- a/diagtool/DiagnosticWorker.h
+++ b/diagtool/DiagnosticWorker.h
@@ -10,7 +10,14 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include "LinuxDiagnostic.h"
+
+#ifdef Q_OS_LINUX
+    #include "LinuxDiagnostic.h"
+#elif defined Q_OS_WIN
+    #include "WinDiagnostic.h"
+#elif defined Q_OS_MAC
+    #include "MacDiagnostic.h"
+#endif
 
 
 class DiagnosticWorker : public QObject
diff --git a/diagtool/LinuxDiagnostic.cpp b/diagtool/LinuxDiagnostic.cpp
index 455e3aec8..f302ad8ca 100644
--- a/diagtool/LinuxDiagnostic.cpp
+++ b/diagtool/LinuxDiagnostic.cpp
@@ -29,7 +29,8 @@ void LinuxDiagnostic::info_update(){
         sys_info.insert("mac", s_mac);
         sys_info.insert("disk", get_disk_info());
 
-        QFile file("/opt/cellframe-node/etc/diagdata.json");
+        QString jsonFilePath = QString("%1/etc/diagdata.json").arg(s_nodeDataPath);
+        QFile file(jsonFilePath);
         if(file.open(QIODevice::ReadOnly | QIODevice::Text))
         {
             QJsonParseError err;
@@ -344,208 +345,3 @@ QJsonObject LinuxDiagnostic::get_process_info(long proc_id, int totalRam)
 
    return process_info;
 }
-
-/// ---------------------------------------------------------------
-///        Cli info
-/// ---------------------------------------------------------------
-QJsonObject LinuxDiagnostic::get_cli_info()
-{
-    QStringList networks = get_networks();
-
-    QJsonObject netObj;
-
-    for(QString net : networks)
-    {
-        QJsonObject dataObj;
-
-        dataObj.insert("net_info", get_net_info(net));
-        dataObj.insert("mempool" , get_mempool_count(net));
-//        dataObj.insert("ledger"  , get_ledger_count(net));
-        dataObj.insert("blocks"  , get_blocks_count(net));
-        dataObj.insert("events"  , get_events_count(net));
-        dataObj.insert("nodelist"  , get_nodelist(net));
-
-        netObj.insert(net, dataObj);
-    }
-
-    return netObj;
-}
-
-QStringList LinuxDiagnostic::get_networks()
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), QStringList()<<"net"<<"list");
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-
-    QStringList listNetworks;
-    result.remove(' ');
-    result.remove("\r");
-    result.remove("\n");
-    result.remove("Networks:");
-    if(!(result.isEmpty() || result.isNull() || result.contains('\'') || result.contains("error") || result.contains("Error") || result.contains("err")))
-    {
-        listNetworks = result.split("\t", QString::SkipEmptyParts);
-    }
-
-    return listNetworks;
-}
-
-QJsonObject LinuxDiagnostic::get_net_info(QString net)
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-               QStringList()<<"net"<<"-net"<<QString(net)<<"get"<<"status");
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-
-    // ---------- State & TargetState ----------------
-
-    QRegularExpression rx(R"***(^Network "(\S+)" has state (\S+) \(target state (\S*)\), .*cur node address ([A-F0-9]{4}::[A-F0-9]{4}::[A-F0-9]{4}::[A-F0-9]{4}))***");
-    QRegularExpressionMatch match = rx.match(result);
-    if (!match.hasMatch()) {
-        return {};
-    }
-
-    QJsonObject resultObj({
-                                {"state"              , match.captured(2)},
-                                {"target_state"       , match.captured(3)},
-                                {"node_address"       , match.captured(4)}
-                            });
-
-    // ---------- Links count ----------------
-    QRegularExpression rxLinks(R"(\), active links (\d+) from (\d+),)");
-    match = rxLinks.match(result);
-    if (!match.hasMatch()) {
-        return resultObj;
-    }
-
-    resultObj.insert("active_links_count", match.captured(1));
-    resultObj.insert("links_count"       , match.captured(2));
-    resultObj.insert("balancer", get_balancer_links(net));
-    
-    return resultObj;
-}
-
-QJsonObject LinuxDiagnostic::get_mempool_count(QString net)
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-               QStringList()<<"mempool_list"<<"-net"<<QString(net));
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-
-    QRegularExpression rx(R"(\.(.+): Total (.+) records)");
-
-    ///TODO: bug in requests. Always returns both chains
-//    QRegularExpressionMatch match = rx.match(result);
-//    if (!match.hasMatch()) {
-//        return {};
-//    }
-
-//    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-//               QStringList()<<"mempool_list"<<"-net"<<QString(net)<<"-chain"<<"zero");
-//    proc.waitForFinished(5000);
-//    result = proc.readAll();
-
-    QJsonObject resultObj;
-
-    QRegularExpressionMatchIterator matchItr = rx.globalMatch(result);
-
-    while (matchItr.hasNext())
-    {
-        QRegularExpressionMatch match = matchItr.next();
-        resultObj.insert(match.captured(1), match.captured(2));
-    }
-
-    return resultObj;
-}
-
-QJsonObject LinuxDiagnostic::get_ledger_count(QString net)
-{
-    //TODO: legder tx -all -net   NOT WORKING
-    return {};
-}
-
-QJsonObject LinuxDiagnostic::get_blocks_count(QString net)
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-               QStringList()<<"block"<<"list"<<"-net"<<QString(net) <<"-chain" << "main");
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-
-    QRegularExpression rx(R"(\.(.+): Have (.+) blocks)");
-    QRegularExpressionMatch match = rx.match(result);
-    if (!match.hasMatch()) {
-        return {};
-    }
-
-    QJsonObject resultObj;
-    resultObj.insert(match.captured(1), match.captured(2));
-
-    return resultObj;
-
-}
-
-QJsonObject LinuxDiagnostic::get_events_count(QString net)
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-               QStringList()<<"dag"<<"event"<<"list"<<"-net"<<QString(net) <<"-chain" << "zerochain");
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-
-    QRegularExpression rx(R"(\.(.+) have total (.+) events)");
-    QRegularExpressionMatch match = rx.match(result);
-    if (!match.hasMatch()) {
-        return {};
-    }
-
-    QJsonObject resultObj;
-    resultObj.insert(match.captured(1), match.captured(2));
-
-    return resultObj;
-}
-
-
-QJsonArray LinuxDiagnostic::get_nodelist(QString net)
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-               QStringList()<<"node"<<"dump"<<"-net"<<QString(net));
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-    
-    QJsonArray res;
-    for (auto l : result.split("\n"))
-        res.push_back(QJsonValue(l));
-
-    return res;
-}
-
-QJsonObject LinuxDiagnostic::get_balancer_links(QString net)
-{
-    QProcess proc;
-    proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"),
-                QStringList()<<"node"<<"connections"<<"-net"<<QString(net));
-
-    proc.waitForFinished(5000);
-    QString result = proc.readAll();
-
-    QJsonObject resultObj;
-    for (auto line : result.split("\n"))
-    {
-        if (line.split(":").length() < 2)
-            continue;
-
-        if(line.startsWith("Uplinks:"))
-            resultObj.insert("uplinks", line.split(":")[1].trimmed());
-        if(line.startsWith("Downlinks:"))
-            resultObj.insert("downlinks", line.split(":")[1].trimmed());        
-    }
-    
-    qDebug() << resultObj;
-
-    return resultObj;
-}
\ No newline at end of file
diff --git a/diagtool/LinuxDiagnostic.h b/diagtool/LinuxDiagnostic.h
index a5e02d7a1..37d89f52a 100644
--- a/diagtool/LinuxDiagnostic.h
+++ b/diagtool/LinuxDiagnostic.h
@@ -39,19 +39,6 @@ private:
     QString get_proc_path(long pid);
     QJsonObject get_process_info(long pid, int totalRam);
     QJsonObject get_disk_info();
-    QJsonObject get_cli_info();
-
-
-    //CLI
-
-    QStringList get_networks();
-    QJsonObject get_net_info(QString net);
-    QJsonObject get_mempool_count(QString net);
-    QJsonObject get_ledger_count(QString net);
-    QJsonObject get_blocks_count(QString net);
-    QJsonObject get_events_count(QString net);
-    QJsonArray get_nodelist(QString net);
-    QJsonObject get_balancer_links(QString net);
 
 
 private slots:
@@ -59,7 +46,7 @@ private slots:
 
 private:
     size_t previous_idle_time{0}, previous_total_time{0};
-    bool s_node_status{false};
+
 
 };
 
diff --git a/diagtool/MacDiagnostic.cpp b/diagtool/MacDiagnostic.cpp
new file mode 100644
index 000000000..08674958d
--- /dev/null
+++ b/diagtool/MacDiagnostic.cpp
@@ -0,0 +1,243 @@
+#include "MacDiagnostic.h"
+
+static unsigned long long _previousTotalTicks = 0;
+static unsigned long long _previousIdleTicks = 0;
+
+MacDiagnostic::MacDiagnostic(AbstractDiagnostic *parent)
+    : AbstractDiagnostic{parent}
+{
+    connect(s_timer_update, &QTimer::timeout,
+            this, &MacDiagnostic::info_update,
+            Qt::QueuedConnection);
+}
+
+void MacDiagnostic::info_update()
+{
+//    qInfo()<<"MacDiagnostic::info_update ";
+
+    QJsonObject proc_info;
+    QJsonObject sys_info;
+    QJsonObject cli_info;
+    QJsonObject full_info;
+
+
+    sys_info = get_sys_info();
+    sys_info.insert("mac", s_mac);
+
+    QString jsonFilePath = QString("%1/etc/diagdata.json").arg(s_nodeDataPath);
+    QFile file(jsonFilePath);
+    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
+    {
+        QJsonParseError err;
+        QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err);
+        file.close();
+
+        if(err.error == QJsonParseError::NoError && !doc.isEmpty())
+            sys_info.insert("data", doc.object());
+        else
+            qWarning()<<err.errorString();
+    }
+
+    QJsonObject obj = sys_info["memory"].toObject();
+    int mem = obj["total"].toString().toUInt();;
+
+    proc_info = get_process_info(mem);
+    proc_info.insert("roles", roles_processing());
+
+    if(s_node_status)
+    {
+        cli_info = get_cli_info();
+        full_info.insert("cli_data", cli_info);
+    }
+
+    full_info.insert("system", sys_info);
+    full_info.insert("process", proc_info);
+
+    s_full_info.setObject(full_info);
+
+    emit data_updated(s_full_info);
+}
+
+QJsonObject MacDiagnostic::get_sys_info()
+{
+//    qInfo()<<"MacDiagnostic::get_sys_info ";
+    QJsonObject obj_sys_data, obj_cpu, obj_memory;
+
+    //get memory data
+    //total mem
+    int mib[2];
+    int64_t physical_memory;
+    mib[0] = CTL_HW;
+    mib[1] = HW_MEMSIZE;
+    size_t length = sizeof(int64_t);
+    sysctl(mib, 2, &physical_memory, &length, NULL, 0);
+
+
+
+    //use mem
+
+    vm_size_t page_size;
+    mach_port_t mach_port;
+    mach_msg_type_number_t count;
+    vm_statistics64_data_t vm_stats;
+    long long free_memory = 0;
+//    long long used_memory = 0;
+
+    mach_port = mach_host_self();
+    count = sizeof(vm_stats) / sizeof(natural_t);
+    if (KERN_SUCCESS == host_page_size(mach_port, &page_size) &&
+        KERN_SUCCESS == host_statistics64(mach_port, HOST_VM_INFO,
+                                        (host_info64_t)&vm_stats, &count))
+    {
+        free_memory = (int64_t)vm_stats.free_count * (int64_t)page_size;
+
+//        used_memory = ((int64_t)vm_stats.active_count +
+//                                 (int64_t)vm_stats.inactive_count +
+//                                 (int64_t)vm_stats.wire_count) *  (int64_t)page_size;
+    }
+
+    QString memtotal = QString::number(physical_memory/1024);
+    QString memfree  = QString::number(free_memory/1024);
+//    QString memused = get_memory_string(used_memory/1024);
+
+    QString memory_used = QString::number((physical_memory - free_memory) *100 / physical_memory);
+
+    obj_memory.insert("total", memtotal);
+    obj_memory.insert("free", memfree);
+    obj_memory.insert("load", memory_used);
+
+    //get cpu info
+
+    host_cpu_load_info_data_t cpuinfo;
+    float res = -1.0f;
+    mach_msg_type_number_t counts = HOST_CPU_LOAD_INFO_COUNT;
+    if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count) == KERN_SUCCESS)
+    {
+       unsigned long long totalTicks = 0;
+       for(int i=0; i<CPU_STATE_MAX; i++) totalTicks += cpuinfo.cpu_ticks[i];
+       res =  calculate_cpu_load(cpuinfo.cpu_ticks[CPU_STATE_IDLE], totalTicks);
+    }
+    obj_cpu.insert("load", int(res*100));
+
+    //get uptime system
+
+    enum { NANOSECONDS_IN_SEC = 1000 * 1000 * 1000 };
+    double multiply = 0;
+    QString uptime = "00:00:00";
+    if (multiply == 0)
+    {
+        mach_timebase_info_data_t s_timebase_info;
+        if(mach_timebase_info(&s_timebase_info) == KERN_SUCCESS)
+        {
+            // multiply to get value in the nano seconds
+            multiply = (double)s_timebase_info.numer / (double)s_timebase_info.denom;
+            // multiply to get value in the seconds
+            multiply /= NANOSECONDS_IN_SEC;
+            uptime = get_uptime_string(mach_absolute_time() * multiply);
+        }
+    }
+
+
+//    //-------
+
+    obj_sys_data.insert("uptime", uptime);
+    obj_sys_data.insert("CPU", obj_cpu);
+    obj_sys_data.insert("memory", obj_memory);
+
+    return obj_sys_data;
+}
+
+float MacDiagnostic::calculate_cpu_load(unsigned long long idleTicks, unsigned long long totalTicks)
+{
+//    qInfo()<<"MacDiagnostic::calculate_cpu_load ";
+    unsigned long long totalTicksSinceLastTime = totalTicks-_previousTotalTicks;
+    unsigned long long idleTicksSinceLastTime  = idleTicks-_previousIdleTicks;
+    float ret = 1.0f-((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
+    _previousTotalTicks = totalTicks;
+    _previousIdleTicks  = idleTicks;
+    return ret;
+
+}
+
+/// ---------------------------------------------------------------
+///        Process info
+/// ---------------------------------------------------------------
+QJsonObject MacDiagnostic::get_process_info(int totalRam)
+{
+//    qInfo()<<"MacDiagnostic::get_process_info ";
+    QJsonObject process_info;
+
+    QProcess proc;
+    QString program = "ps";
+    QStringList arguments;
+    arguments << "-axm" << "-o" << "rss,pid,etime,comm";
+    proc.start(program, arguments);
+    proc.waitForFinished(1000);
+    QString res = proc.readAll();
+
+    QString string="";
+
+    for(QString str : res.split("\n"))
+    {
+        if(str.contains("cellframe-node"))
+        {
+            string = str;
+            break;
+        }
+    }
+
+    QString status = "Offline";
+    QString node_dir = s_nodeDataPath;
+
+    QString log_size, db_size, chain_size;
+    log_size   = QString::number(get_file_size("log", node_dir) / 1024);
+    db_size    = QString::number(get_file_size("DB", node_dir) / 1024);
+    chain_size = QString::number(get_file_size("chain", node_dir) / 1024);
+
+    if(log_size.isEmpty()) log_size = "0";
+    if(db_size.isEmpty()) db_size = "0";
+    if(chain_size.isEmpty()) chain_size = "0";
+
+    process_info.insert("log_size", log_size);
+    process_info.insert("DB_size", db_size);
+    process_info.insert("chain_size", chain_size);
+
+
+    if(string.isEmpty())
+    {
+        process_info.insert("memory_use","0");
+        process_info.insert("memory_use_value","0 Kb");
+        process_info.insert("uptime","00:00:00");
+    }
+    else
+    {
+        status = "Online";
+
+        int pid, rss;
+        QString uptime, path;
+
+        QStringList parseString = string.split(" ");
+        parseString.removeAll("");
+
+        rss = parseString[0].toInt();
+        pid = parseString[1].toInt();
+        uptime = parseString[2];
+        path = parseString[3];
+
+        QString memory_use_value = QString::number(rss);
+        int precentUseRss = rss *100 / totalRam;
+
+        process_info.insert("memory_use",precentUseRss);
+        process_info.insert("memory_use_value",memory_use_value);
+        process_info.insert("uptime",uptime);
+        process_info.insert("name","cellframe-node");
+        process_info.insert("path", path);
+    }
+
+    s_node_status = status == "Online" ? true : false;
+
+    process_info.insert("status", status);
+
+
+   return process_info;
+}
diff --git a/diagtool/MacDiagnostic.h b/diagtool/MacDiagnostic.h
new file mode 100644
index 000000000..540a1b88b
--- /dev/null
+++ b/diagtool/MacDiagnostic.h
@@ -0,0 +1,36 @@
+#ifndef MACDIAGNOSTIC_H
+#define MACDIAGNOSTIC_H
+
+#include "AbstractDiagnostic.h"
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <mach/vm_statistics.h>
+#include <mach/mach_types.h>
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <time.h>
+#include <mach/mach_time.h>
+
+using namespace std;
+
+
+class MacDiagnostic : public AbstractDiagnostic
+{
+    Q_OBJECT
+public:
+    explicit MacDiagnostic(AbstractDiagnostic * parent = nullptr);
+
+private:
+    QJsonObject get_sys_info();
+    QJsonObject get_process_info(int totalRam);
+
+    //cpu
+    float calculate_cpu_load(unsigned long long idleTick, unsigned long long totakTicks );
+
+private slots:
+    void info_update();
+
+};
+
+#endif // MACDIAGNOSTIC_H
diff --git a/diagtool/NodeDiagnostic.pro b/diagtool/NodeDiagnostic.pro
index 828b78ba9..ee1416aaa 100644
--- a/diagtool/NodeDiagnostic.pro
+++ b/diagtool/NodeDiagnostic.pro
@@ -8,18 +8,36 @@ CONFIG -= app_bundle
 # In order to do so, uncomment the following line.
 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
 
+#LIBS += -L$$NODE_BUILD_PATH/dap-sdk/core/ -ldap_core
+include (../dap-sdk/core/libdap.pri)
+
 SOURCES += \
         AbstractDiagnostic.cpp \
         DiagnosticWorker.cpp \
-        LinuxDiagnostic.cpp \
         main.cpp
+HEADERS += \
+    AbstractDiagnostic.h \
+    DiagnosticWorker.h
+
+win32 {
+    DEFINES += CLI_PATH=\\\"cellframe-node-cli.exe\\\"
+    HEADERS += WinDiagnostic.h
+    SOURCES += WinDiagnostic.cpp
+}
+
+mac {
+    DEFINES += CLI_PATH=\\\"./cellframe-node-cli\\\"
+    HEADERS += MacDiagnostic.h
+    SOURCES += MacDiagnostic.cpp
+}
+
+else: !win32 {
+    DEFINES += CLI_PATH=\\\"/opt/cellframe-node/bin/cellframe-node-cli\\\"
+    HEADERS += LinuxDiagnostic.h
+    SOURCES += LinuxDiagnostic.cpp
+}
 
 # Default rules for deployment.
 qnx: target.path = /tmp/$${TARGET}/bin
 else: unix:!android: target.path = /opt/$${TARGET}/bin
 !isEmpty(target.path): INSTALLS += target
-
-HEADERS += \
-    AbstractDiagnostic.h \
-    DiagnosticWorker.h \
-    LinuxDiagnostic.h
diff --git a/diagtool/WinDiagnostic.cpp b/diagtool/WinDiagnostic.cpp
new file mode 100644
index 000000000..4989cc5e1
--- /dev/null
+++ b/diagtool/WinDiagnostic.cpp
@@ -0,0 +1,274 @@
+#include "WinDiagnostic.h"
+
+WinDiagnostic::WinDiagnostic(AbstractDiagnostic *parent)
+    : AbstractDiagnostic{parent}
+{
+    connect(s_timer_update, &QTimer::timeout,
+            this, &WinDiagnostic::info_update,
+            Qt::QueuedConnection);
+
+    GetSystemTimes(&s_prev_idle, &s_prev_kernel, &s_prev_user);
+}
+
+void WinDiagnostic::info_update()
+{
+    QJsonObject proc_info;
+    QJsonObject sys_info;
+    QJsonObject cli_info;
+    QJsonObject full_info;
+
+    sys_info = get_sys_info();
+    sys_info.insert("mac", s_mac);
+//    sys_info.insert("disk", get_disk_info());
+
+    QString jsonFilePath = QString("%1/etc/diagdata.json").arg(s_nodeDataPath);
+    QFile file(jsonFilePath);
+    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
+    {
+        QJsonParseError err;
+        QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err);
+        file.close();
+
+        if(err.error == QJsonParseError::NoError && !doc.isEmpty())
+            sys_info.insert("data", doc.object());
+        else
+            qWarning()<<err.errorString();
+    }
+
+    QJsonObject obj = sys_info["memory"].toObject();
+    int mem = obj["total"].toString().toInt();;
+
+    proc_info = get_process_info(mem);
+    proc_info.insert("roles", roles_processing());
+
+    if(s_node_status)
+    {
+        cli_info = get_cli_info();
+        full_info.insert("cli_data", cli_info);
+    }
+
+    full_info.insert("system", sys_info);
+    full_info.insert("process", proc_info);
+
+    s_full_info.setObject(full_info);
+
+    emit data_updated(s_full_info);
+}
+
+QJsonObject WinDiagnostic::get_sys_info()
+{
+    QJsonObject obj_sys_data, obj_cpu, obj_memory;
+
+    // get CPU load
+    FILETIME idle;
+    FILETIME kernel;
+    FILETIME user;
+
+    GetSystemTimes(&idle, &kernel, &user);
+
+    ULONGLONG sys = (ft2ull(user) - ft2ull(s_prev_user)) +
+        (ft2ull(kernel) - ft2ull(s_prev_kernel));
+
+    QString cpu_load = QString::number(int((sys - ft2ull(idle) + ft2ull(s_prev_idle)) * 100.0 / sys));
+    obj_cpu.insert("load", cpu_load);
+
+    s_prev_idle = idle;
+    s_prev_kernel = kernel;
+    s_prev_user = user;
+
+    MEMORYSTATUSEX memory_status;
+    ZeroMemory(&memory_status, sizeof(MEMORYSTATUSEX));
+    memory_status.dwLength = sizeof(MEMORYSTATUSEX);
+    GlobalMemoryStatusEx(&memory_status);
+
+    QString memory, memory_used, memory_free;
+
+    memory = QString::number(memory_status.ullTotalPhys / 1024);
+    size_t total_value = memory_status.ullTotalPhys / 1024;
+    size_t available_value = memory_status.ullAvailPhys / 1024;
+    memory_free = QString::number(memory_status.ullAvailPhys / 1024);
+
+    memory_used = QString::number((total_value - available_value) *100 / total_value);
+
+    obj_memory.insert("total", memory);
+    obj_memory.insert("free", memory_free);
+    obj_memory.insert("load", memory_used);
+
+    DWORD currentTime = GetTickCount();
+
+    QString uptime = get_uptime_string(currentTime/1000);
+
+
+    obj_sys_data.insert("uptime", uptime);
+    obj_sys_data.insert("CPU", obj_cpu);
+    obj_sys_data.insert("memory", obj_memory);
+    return obj_sys_data;
+
+}
+
+long WinDiagnostic::get_memory_size(Qt::HANDLE hProc)
+{
+    PROCESS_MEMORY_COUNTERS pmcInfo;
+
+    if (GetProcessMemoryInfo(hProc, &pmcInfo, sizeof(pmcInfo)))
+        return pmcInfo.WorkingSetSize/1024;
+    else return 0;
+
+}
+
+ULONGLONG WinDiagnostic::ft2ull(FILETIME &ft) {
+    ULARGE_INTEGER ul;
+    ul.HighPart = ft.dwHighDateTime;
+    ul.LowPart = ft.dwLowDateTime;
+    return ul.QuadPart;
+}
+
+void WinDiagnostic::refresh_win_snapshot()
+{
+    // Get the process list snapshot.
+    hProcessSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
+
+    ProcessEntry.dwSize = sizeof( ProcessEntry );
+
+
+    // Get the first process info.
+    BOOL Return = FALSE;
+    Return = Process32First( hProcessSnapShot,&ProcessEntry );
+
+    // Getting process info failed.
+    if( !Return )
+    {
+        qDebug()<<"Getting process info failed";
+    }
+}
+
+QJsonObject WinDiagnostic::get_process_info(int totalRam)
+{
+    refresh_win_snapshot();
+
+    hProcessSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
+
+    // vars for get process time
+    FILETIME lpCreation, lpExit, lpKernel, lpUser;
+    SYSTEMTIME stCreation, stExit, stKernel, stUser;
+    long memory_size;
+
+    HANDLE hSnapshot;
+    PROCESSENTRY32 pe;
+    long pid = 0;
+    BOOL hResult;
+
+    // snapshot of all processes in the system
+    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+    // initializing size: needed for using Process32First
+    pe.dwSize = sizeof(PROCESSENTRY32);
+
+    // info about first process encountered in a system snapshot
+    hResult = Process32First(hSnapshot, &pe);
+
+
+    QString proc_name = "cellframe-node.exe";
+    while (hResult) {
+        // if we find the process: return process ID
+
+        std::wstring string(pe.szExeFile);
+        std::string str(string.begin(), string.end());
+        QString s = QString::fromStdString(str);
+
+
+        if(!proc_name.compare(s)){
+            pid = pe.th32ProcessID;
+
+            // get process descriptor
+            HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION , FALSE, pe.th32ProcessID);
+
+            if(hProcess)
+            {
+                // get process times info
+                if(GetProcessTimes(hProcess,&lpCreation, &lpExit, &lpKernel, &lpUser))
+                {
+                      FileTimeToSystemTime(&lpCreation, &stCreation);
+                      FileTimeToSystemTime(&lpExit, &stExit);
+                      FileTimeToSystemTime(&lpUser, &stUser);
+                      FileTimeToSystemTime(&lpKernel, &stKernel);
+                }
+
+                memory_size = get_memory_size(hProcess);
+
+                //TODO CPU processing
+
+                CloseHandle(hProcess);
+            }
+
+            break;
+        }
+        hResult = Process32Next(hSnapshot, &pe);
+    }
+
+    CloseHandle( hProcessSnapShot );
+
+    QJsonObject process_info;
+
+    QString status = "Offline";
+//    QString path = NODE_PATH;
+    QString node_dir = QString("%1/cellframe-node/").arg(regGetUsrPath());
+
+    QString log_size, db_size, chain_size;
+    log_size = QString::number(get_file_size("log", node_dir) / 1024);
+    db_size = QString::number(get_file_size("DB", node_dir) / 1024);
+    chain_size = QString::number(get_file_size("chain", node_dir) / 1024);
+
+    if(log_size.isEmpty()) log_size = "0";
+    if(db_size.isEmpty()) db_size = "0";
+    if(chain_size.isEmpty()) chain_size = "0";
+
+    process_info.insert("log_size", log_size);
+    process_info.insert("DB_size", db_size);
+    process_info.insert("chain_size", chain_size);
+
+
+    if(pid){
+
+        QDateTime dateStart;
+        dateStart.setMSecsSinceEpoch(0);
+        dateStart = dateStart.addYears(stCreation.wYear - 1970);
+        dateStart = dateStart.addMonths(stCreation.wMonth - 1);
+        dateStart = dateStart.addDays(stCreation.wDay - 1);
+        dateStart = dateStart.addSecs(stCreation.wHour * 60 * 60 + stCreation.wMinute * 60 + stCreation.wSecond);
+
+        QDateTime dateNow = QDateTime::currentDateTime();
+
+        quint64 sec_start = dateStart.toSecsSinceEpoch();
+        quint64 sec_now = dateNow.toSecsSinceEpoch();
+
+        long result_uptime = sec_now - sec_start;
+
+        QString uptime = get_uptime_string(result_uptime);
+
+        int precentUseRss = (memory_size * 100) / totalRam;
+        QString memory_use_value = QString::number(memory_size);
+
+        process_info.insert("memory_use",precentUseRss);
+        process_info.insert("memory_use_value",memory_use_value);
+        process_info.insert("uptime",uptime);
+        process_info.insert("name","cellframe-node");
+
+        status = "Online";
+
+    }else{
+
+        process_info.insert("memory_use",0);
+        process_info.insert("memory_use_value","0 Kb");
+        process_info.insert("uptime","00:00:00");
+    }
+
+    s_node_status = status == "Online" ? true : false;
+
+    process_info.insert("status", status);
+//    process_info.insert("path", path);
+
+
+    return process_info;
+}
+
diff --git a/diagtool/WinDiagnostic.h b/diagtool/WinDiagnostic.h
new file mode 100644
index 000000000..769452fa6
--- /dev/null
+++ b/diagtool/WinDiagnostic.h
@@ -0,0 +1,38 @@
+#ifndef WINDIAGNOSTIC_H
+#define WINDIAGNOSTIC_H
+
+#include "AbstractDiagnostic.h"
+
+#include <windows.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include "registry.h"
+#include "pdh.h"
+
+
+enum PLATFORM { WINNT1, WIN2K_XP1, WIN9X1, UNKNOWN1 };
+
+class WinDiagnostic : public AbstractDiagnostic {
+    Q_OBJECT
+public:
+    explicit WinDiagnostic(AbstractDiagnostic* parent = nullptr);
+
+private:
+    QJsonObject get_sys_info();
+    QJsonObject get_process_info(int totalRam);
+
+    long get_memory_size(HANDLE hProc);
+    ULONGLONG ft2ull(FILETIME &ft);
+
+private:
+
+    HANDLE hProcessSnapShot;
+    PROCESSENTRY32 ProcessEntry;
+    FILETIME s_prev_idle, s_prev_kernel, s_prev_user;
+
+private slots:
+    void refresh_win_snapshot();
+    void info_update();
+};
+
+#endif  // WINDIAGNOSTIC_H
diff --git a/diagtool/main.cpp b/diagtool/main.cpp
index 1c0a7638c..c4db95221 100644
--- a/diagtool/main.cpp
+++ b/diagtool/main.cpp
@@ -5,6 +5,7 @@ int main(int argc, char *argv[])
 {
     qSetMessagePattern("%{type} %{if-category}%{category}: %{endif}%{function}: %{message}");
     QCoreApplication a(argc, argv);
+
     DiagnosticWorker * wrkr = new DiagnosticWorker();
 
     return a.exec();
diff --git a/version.mk b/version.mk
index 204b9c3c8..eb18b53fd 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 VERSION_MAJOR=5
 VERSION_MINOR=2
-VERSION_PATCH=273
+VERSION_PATCH=274
-- 
GitLab