diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..77c099c7e67185aa3d244160e7b7e53866105f8a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cellframe-sdk"] + path = cellframe-sdk + url = https://gitlab.demlabs.net/cellframe/cellframe-sdk.git diff --git a/CellframeNodeDiagtool/AbstractDiagnostic.cpp b/CellframeNodeDiagtool/AbstractDiagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f0c8fb932a67c75c8c3b8c98fe0039ecfb5614a --- /dev/null +++ b/CellframeNodeDiagtool/AbstractDiagnostic.cpp @@ -0,0 +1,422 @@ +#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"; + s_sendedDataFilePath = "/tmp/lastSendedData.json"; +#elif defined(Q_OS_WIN) + s_nodeDataPath = QString("%1/cellframe-node").arg(regGetUsrPath()); + s_sendedDataFilePath = QString("%1/cellframe-node/lastSendedDiagData.json").arg(regGetUsrPath()); +#elif defined(Q_OS_MACOS) + s_nodeDataPath = QString("Applications/CellframeNode.app/Contents/Resources/"); + s_sendedDataFilePath = "/tmp/lastSendedData.json"; +#endif + + s_jsonFilePath = QString("%1/etc/diagdata.json").arg(s_nodeDataPath); + + QFile config_file(s_jsonFilePath); + if(!config_file.exists()) + { + config_file.open(QIODevice::WriteOnly); + QJsonObject obj{{"category","other"}}; + config_file.write(QJsonDocument(obj).toJson()); + config_file.close(); + } + + s_timer_update = new QTimer(); + s_mac = get_mac(); +} + +AbstractDiagnostic::~AbstractDiagnostic() +{ + delete s_timer_update; +} + +void AbstractDiagnostic::set_timeout(int timeout){ + s_timer_update->stop(); + s_timeout = timeout; + s_timer_update->start(s_timeout); +} + +void AbstractDiagnostic::start_diagnostic() +{ + s_timer_update->start(s_timeout); +} + +void AbstractDiagnostic::stop_diagnostic() +{ + s_timer_update->stop(); +} + +QJsonValue AbstractDiagnostic::get_mac() +{ + QString MAC{"unknown"}; + foreach(QNetworkInterface netInterface, QNetworkInterface::allInterfaces()) + { + // Return only the first non-loopback MAC Address + if (!(netInterface.flags() & QNetworkInterface::IsLoopBack)) + { + qDebug()<<netInterface.hardwareAddress(); + if(!netInterface.hardwareAddress().isEmpty()) + { + MAC = netInterface.hardwareAddress(); + break; + } + } + } + + return MAC; +} + +QString AbstractDiagnostic::get_uptime_string(long sec) +{ + QTime time(0, 0); + time = time.addSecs(sec); + int fullHours = sec/3600; + + QString uptime = QString("%1:").arg(fullHours) + time.toString("mm:ss"); + + return uptime; +} + +quint64 AbstractDiagnostic::get_file_size (QString flag, QString path ) { + + if(flag == "log") + path += "/var/log"; + else + if (flag == "DB") + path += "/var/lib/global_db"; + else + if (flag == "chain") + path += "/var/lib/network"; + else + path += ""; + + QDir currentFolder( path ); + + quint64 totalsize = 0; + + currentFolder.setFilter( QDir::Dirs | QDir::Files | QDir::NoSymLinks ); + currentFolder.setSorting( QDir::Name ); + + QFileInfoList folderitems( currentFolder.entryInfoList() ); + + foreach ( QFileInfo i, folderitems ) { + QString iname( i.fileName() ); + if ( iname == "." || iname == ".." || iname.isEmpty() ) + continue; + if(flag == "log" && i.suffix() != "log" && !i.isDir()) + continue; + else + if(flag == "DB" && (i.suffix() != "dat" && !i.suffix().isEmpty()) && !i.isDir()) + continue; + else + if(flag == "chain" && i.suffix() != "dchaincell" && !i.isDir()) + continue; + + if ( i.isDir() ) + totalsize += get_file_size("", path+"/"+iname); + else + totalsize += i.size(); + } + + return totalsize; +} + +QString AbstractDiagnostic::get_memory_string(long num) +{ + QString result = QString::number(num); + return result; + + int gb = (num / 1024) / 1024; + int mb = (num-gb*1024*1024) /1024; + int kb = (num - (gb*1024*1024+mb*1024)); + if (gb > 0) + result = QString::number(gb) + QString(" Gb "); + else + result = QString(""); + if (mb > 0) + result += QString::number(mb) + QString(" Mb "); + if (kb > 0) + result += QString::number(kb) + QString(" Kb "); + + return result; +} + +QJsonObject AbstractDiagnostic::roles_processing() +{ + QJsonObject rolesObject; + + QDir currentFolder(s_nodeDataPath + "/etc/network"); + + currentFolder.setFilter( QDir::Dirs | QDir::Files | QDir::NoSymLinks ); + currentFolder.setSorting( QDir::Name ); + + QFileInfoList folderitems( currentFolder.entryInfoList() ); + + foreach ( QFileInfo i, folderitems ) { + QString iname( i.fileName() ); + if ( iname == "." || iname == ".." || iname.isEmpty() ) + continue; + if(i.suffix() == "cfg" && !i.isDir()) + { + QSettings config(i.absoluteFilePath(), QSettings::IniFormat); + + rolesObject.insert(i.completeBaseName(), config.value("node-role", "unknown").toString()); + } + } + + 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(",", 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(); + + QRegularExpression rx_state(R"(.*current: ([A-Z,_]+).*)"); + QRegularExpression rx_addr(R"(.*current_addr: (.+))"); + + QJsonObject resultObj; + + QRegularExpressionMatch match_addr = rx_addr.match(result); + if (match_addr.hasMatch()) { + resultObj.insert("node_address", match_addr.captured(1)); + } + + QRegularExpressionMatch match_state = rx_state.match(result); + if (match_state.hasMatch()) { + resultObj.insert("state", match_state.captured(1)); + } + + // ---------- Links count ---------------- + QRegularExpression rxLinksActive(R"(.*active: (.*))"); + QRegularExpression rxLinksRequired(R"(.*required: (.*))"); + + QRegularExpressionMatch match_active = rxLinksActive.match(result); + if (match_active.hasMatch()) { + resultObj.insert("active_links_count", match_active.captured(1)); + } + + QRegularExpressionMatch match_req = rxLinksRequired.match(result); + if (match_req.hasMatch()) { + resultObj.insert("links_count", match_req.captured(1)); + } + + + 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: .*\.(.*): (\d+))"); + + 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"<<"last"<<"-net"<<QString(net) <<"-chain" << "main"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + QRegularExpression rx_block_count(R"(\.(.+) has blocks: (.+))"); + QRegularExpression rx_creation(R"(.*ts_created: (.*))"); + QRegularExpression rx_hash(R"(.*Last block hash: (.*))"); + + + QRegularExpressionMatch block_count_match = rx_block_count.match(result); + QRegularExpressionMatch timestamp_match = rx_creation.match(result); + QRegularExpressionMatch hash_match = rx_hash.match(result); + + if (!block_count_match.hasMatch()) { + return {}; + } + + QJsonObject resultObj; + resultObj.insert(block_count_match.captured(1), block_count_match.captured(2)); + + QJsonObject last_block; + last_block.insert("hash", hash_match.captured(1)); + + qDebug() << "TS: "<<timestamp_match.captured(1); + QDateTime dt = QDateTime::fromString(timestamp_match.captured(1), Qt::RFC2822Date); + qint64 timestamp = dt.toSecsSinceEpoch(); + last_block.insert("timestamp", QString::number(timestamp)); + + resultObj.insert("last_block", last_block); + + qDebug() << "Block last "<<net<<":"; + qDebug() << resultObj; + return resultObj; + +} + +QJsonObject AbstractDiagnostic::get_events_count(QString net) +{ + QProcess proc; + proc.start(QString(CLI_PATH), + QStringList()<<"dag"<<"event"<<"last"<<"-net"<<QString(net) <<"-chain" << "zerochain"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + QRegularExpression rx_event_count(R"(\.(.+) has events: (.+))"); + QRegularExpression rx_creation(R"(.*ts_created: (.*))"); + QRegularExpression rx_hash(R"(.*Last event hash: (.*))"); + + + QRegularExpressionMatch event_count_match = rx_event_count.match(result); + QRegularExpressionMatch timestamp_match = rx_creation.match(result); + QRegularExpressionMatch hash_match = rx_hash.match(result); + + if (!event_count_match.hasMatch()) { + return {}; + } + + QJsonObject resultObj; + resultObj.insert(event_count_match.captured(1), event_count_match.captured(2)); + + QJsonObject last_event; + last_event.insert("hash", hash_match.captured(1)); + + qDebug() << "TS: "<<timestamp_match.captured(1); + QDateTime dt = QDateTime::fromString(timestamp_match.captured(1), Qt::RFC2822Date); + qint64 timestamp = dt.toSecsSinceEpoch(); + last_event.insert("timestamp", QString::number(timestamp)); + + resultObj.insert("last_event", last_event); + + last_event.insert("timestamp", QString::number(timestamp)); + + qDebug() << "Event last "<<net<<":"; + qDebug() << resultObj; + + 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; + + QRegularExpression rx(R"(Total links: \d+ \| Uplinks: (\d+) \| Downlinks: (\d+))"); + QRegularExpressionMatch match = rx.match(result); + + if (!match.hasMatch()) { + return {}; + } + + resultObj.insert("uplinks", match.captured(1)); + resultObj.insert("downlinks", match.captured(2)); + + + return resultObj; +} diff --git a/CellframeNodeDiagtool/AbstractDiagnostic.h b/CellframeNodeDiagtool/AbstractDiagnostic.h new file mode 100644 index 0000000000000000000000000000000000000000..3440e76cf5d14aa1d2138632463a85912b6ac4e2 --- /dev/null +++ b/CellframeNodeDiagtool/AbstractDiagnostic.h @@ -0,0 +1,70 @@ +#ifndef ABSTRACTDIAGNOSTIC_H +#define ABSTRACTDIAGNOSTIC_H + +#include <QObject> +#include "qtimer.h" +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QDebug> +#include <QUrl> +#include <QFileInfo> +#include <QCoreApplication> +#include <QProcess> +#include <QString> +#include <QRegularExpression> +#include <QSettings> +#include <QTime> +#include <QFile> + +#include <QDir> +#include <QNetworkInterface> + +class AbstractDiagnostic : public QObject +{ + Q_OBJECT +public: + explicit AbstractDiagnostic(QObject * parent = nullptr); + ~AbstractDiagnostic(); + +public: + void start_diagnostic(); + void stop_diagnostic(); + void set_timeout(int timeout); + quint64 get_file_size(QString flag, QString path); + QString get_uptime_string(long sec); + QString get_memory_string(long num); + QJsonValue get_mac(); + + QJsonDocument get_full_info(){return s_full_info;} + + 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{""}; + QString s_jsonFilePath{""}; + QString s_sendedDataFilePath{""}; + +signals: + void data_updated(QJsonDocument); + +}; + +#endif // ABSTRACTDIAGNOSTIC_H diff --git a/CellframeNodeDiagtool/CMakeLists.txt b/CellframeNodeDiagtool/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ad329d3a55847fc077ba92baa22fbf812870d2f7 --- /dev/null +++ b/CellframeNodeDiagtool/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.10) + +project(cellframe-diagtool) + +find_package(Qt5 5.15 REQUIRED COMPONENTS + Core + Network +) + +set(CMAKE_AUTOMOC ON) + + + +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/CellframeNodeDiagtool/DiagnosticWorker.cpp b/CellframeNodeDiagtool/DiagnosticWorker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72ca0b0f0ead8ad9eba054d7c8e54d2548bd3ec1 --- /dev/null +++ b/CellframeNodeDiagtool/DiagnosticWorker.cpp @@ -0,0 +1,151 @@ +#include "DiagnosticWorker.h" +#include <QNetworkAccessManager> +#include <QHttpPart> +#include <QHttpMultiPart> +#include <QNetworkReply> +static QString group = "global.users.statistic"; + +DiagnosticWorker::DiagnosticWorker(QObject * parent) + : QObject{parent} +{ + +} + +DiagnosticWorker::~DiagnosticWorker() +{ + delete s_uptime_timer; + delete s_elapsed_timer; + delete m_diagnostic; +} + +void DiagnosticWorker::init() +{ + s_uptime_timer = new QTimer(this); + connect(s_uptime_timer, &QTimer::timeout, + this, &DiagnosticWorker::slot_uptime, + Qt::QueuedConnection); + s_uptime_timer->start(1000); + + 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(3000); //30 sec + + m_isSendData = QSettings().value("diag_send_data", "1").toInt(); + + m_initStatus = true; + + emit sigInitStatusChanged(m_initStatus); +} + +void DiagnosticWorker::switchSendData() +{ + m_isSendData = !m_isSendData; + + if(!m_isSendData) + m_diagnostic->stop_diagnostic(); + else + m_diagnostic->start_diagnostic(); + + QSettings().setValue("diag_send_data", m_isSendData); + + emit sigSendDataStatusChanged(m_isSendData); +} + +void DiagnosticWorker::openConfigFile() +{ + QDesktopServices::openUrl(m_diagnostic->s_jsonFilePath); +} + +void DiagnosticWorker::getSendedData() +{ + QFile sndData(m_diagnostic->s_sendedDataFilePath); + sndData.open(QIODevice::WriteOnly); + sndData.write(m_lastSendedPack.toJson()); + + QDesktopServices::openUrl(m_diagnostic->s_sendedDataFilePath); +} + +DiagnosticWorker &DiagnosticWorker::getInstance() +{ + static DiagnosticWorker instance; + return instance; +} + +void DiagnosticWorker::slot_diagnostic_data(QJsonDocument data) +{ + //insert uptime dashboard and more into system info + QJsonObject obj = data.object(); + QJsonObject system = data["system"].toObject(); + QJsonObject proc = data["process"].toObject(); + + system.insert("uptime_diagtool", s_uptime); + system.insert("time_update_unix", QDateTime::currentSecsSinceEpoch()); + +// system.insert("time_update", QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm")); + obj.insert("system",system); + + if(proc["status"].toString() == "Offline") //if node offline - clear version + m_node_version = ""; + else + if(m_node_version.isEmpty()) + { + QProcess proc; + proc.start(QString(CLI_PATH), QStringList()<<"version"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + if(result.contains("version")) + { + result = result.split("version")[1]; + m_node_version = result.split('\n', QString::SkipEmptyParts).first().trimmed(); + } + } + + proc.insert("version", m_node_version); + obj.insert("process",proc); + data.setObject(obj); + m_lastSendedPack = data; + + write_data(data); +} + +void DiagnosticWorker::slot_uptime() +{ + s_uptime = m_diagnostic->get_uptime_string(s_elapsed_timer->elapsed()/1000); +} + +void DiagnosticWorker::write_data(QJsonDocument data) +{ + QStringList urls = {"https://telemetry.cellframe.net/diag_report", "https://engine-minkowski.kelvpn.com/diag_report"}; + for (auto url_s : urls) + { + QUrl url = QUrl(url_s); + + QNetworkAccessManager * mgr = new QNetworkAccessManager(this); + + connect(mgr, &QNetworkAccessManager::finished, this, [=](QNetworkReply*r) {qDebug() << "data sent " << urls << " " << r->error();}); + connect(mgr,SIGNAL(finished(QNetworkReply*)),mgr, SLOT(deleteLater())); + + auto req = QNetworkRequest(url); + req.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded"); + QString key = m_diagnostic->s_mac.toString(); + QJsonObject obj = data.object(); + obj.insert("mac",key); + data.setObject(obj); + mgr->post(req, data.toJson()); + } + +} diff --git a/CellframeNodeDiagtool/DiagnosticWorker.h b/CellframeNodeDiagtool/DiagnosticWorker.h new file mode 100644 index 0000000000000000000000000000000000000000..51c1d3b79a416168152090ef7b36a1387ac2cfcb --- /dev/null +++ b/CellframeNodeDiagtool/DiagnosticWorker.h @@ -0,0 +1,72 @@ +#ifndef DIAGNOSTICWORKER_H +#define DIAGNOSTICWORKER_H + +#include <QObject> +#include <QTimer> +#include <QElapsedTimer> +#include <QSettings> +#include <QThread> +#include <QDesktopServices> +#include <QSettings> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.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 +{ + Q_OBJECT + Q_DISABLE_COPY(DiagnosticWorker) + + explicit DiagnosticWorker(QObject *parent = nullptr); + ~DiagnosticWorker(); + +public: + static DiagnosticWorker &getInstance(); + + void init(); + + Q_INVOKABLE bool getInitStatus(){return m_initStatus;} + Q_INVOKABLE bool getSendDataStatus(){return m_isSendData;} + Q_INVOKABLE void switchSendData(); + Q_INVOKABLE void openConfigFile(); + Q_INVOKABLE void getSendedData(); + +private: + QString m_node_version{""}; + QSettings m_settings; + AbstractDiagnostic* m_diagnostic; + + QTimer *s_uptime_timer; + QElapsedTimer *s_elapsed_timer; + QString s_uptime{"00:00:00"}; + + QJsonDocument m_lastSendedPack; + + bool m_initStatus{false}; + bool m_isSendData{true}; + +private slots: + void slot_diagnostic_data(QJsonDocument); + void slot_uptime(); + +private: + void write_data(QJsonDocument); + +signals: + Q_INVOKABLE void sigSendDataStatusChanged(bool status); + Q_INVOKABLE void sigInitStatusChanged(bool status); + +}; + +#endif // DIAGNOSTICWORKER_H diff --git a/CellframeNodeDiagtool/LinuxDiagnostic.cpp b/CellframeNodeDiagtool/LinuxDiagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3c031c8061eef1c0441b9a8753056d43f319ff8 --- /dev/null +++ b/CellframeNodeDiagtool/LinuxDiagnostic.cpp @@ -0,0 +1,346 @@ +#include "LinuxDiagnostic.h" +#include <sys/vfs.h> +#include <sys/stat.h> + +LinuxDiagnostic::LinuxDiagnostic(AbstractDiagnostic *parent) + : AbstractDiagnostic{parent} +{ + connect(s_timer_update, &QTimer::timeout, + this, &LinuxDiagnostic::info_update, + Qt::QueuedConnection); +} + + +void LinuxDiagnostic::info_update(){ + + // QSettings config("/opt/cellframe-node/etc/cellframe-node.cfg", + // QSettings::IniFormat); + + // bool isEnabled = config.value("Diagnostic/enabled",false).toBool(); + + if(true) //isEnabled + { + 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()); + + QFile file(s_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"].toInt(); + + proc_info = get_process_info(get_pid(), 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 LinuxDiagnostic::get_disk_info() +{ + QString path = "/opt/cellframe-node"; + + QString apath = QDir(path).absolutePath(); + + struct statfs stfs; + + if (::statfs(apath.toLocal8Bit(),&stfs) == -1) + { + QJsonObject diskObj; + diskObj.insert("total", -1); + diskObj.insert("free", -1); + diskObj.insert("available", -1); + diskObj.insert("used", -1); + + return diskObj; + } + + quint64 total = stfs.f_blocks * stfs.f_bsize; + quint64 free = stfs.f_bfree * stfs.f_bsize; + quint64 available = stfs.f_bavail * stfs.f_bsize; + quint64 used = total - free; + + QJsonObject diskObj; + diskObj.insert("total", QString::number(total)); + diskObj.insert("free", QString::number(free)); + diskObj.insert("available", QString::number(available)); + diskObj.insert("used", QString::number(used)); + + return diskObj; +} + + +long LinuxDiagnostic::get_pid() +{ + long pid; + QString path = QString("%1/var/run/cellframe-node.pid").arg("/opt/cellframe-node/"); + + QFile file(path); + QByteArray data; + if (!file.open(QIODevice::ReadOnly)) + return 0; + data = file.readAll(); + pid = data.toLong(); + + return pid; +} + +/// --------------------------------------------------------------- +/// Sys info +/// --------------------------------------------------------------- +QJsonObject LinuxDiagnostic::get_sys_info() +{ + QJsonObject obj_sys_data, obj_cpu, obj_memory; + + ifstream stat_stream; + string buff; + + //------- + + //get cpu info + size_t idle_time, total_time; + get_cpu_times(idle_time, total_time); + + const float idle_time_delta = idle_time - previous_idle_time; + const float total_time_delta = total_time - previous_total_time; + const float utilization = 100.0 * (1.0 - idle_time_delta / total_time_delta); + + previous_idle_time = idle_time; + previous_total_time = total_time; + + stat_stream.open("/proc/cpuinfo"); + for(int i = 0; i < 16;i++) stat_stream >> buff; + getline(stat_stream,buff); + + obj_cpu.insert("load", (int)utilization); + obj_cpu.insert("model", QString::fromStdString(buff)); + stat_stream.close(); + + //get uptime system + stat_stream.open("/proc/uptime"); + stat_stream >> buff; + QString uptime = get_uptime_string(atoi(buff.c_str())); + stat_stream.close(); + + //get memory data + stat_stream.open("/proc/meminfo"); + QString memory, memory_used, memory_free; + string total,free,available; + stat_stream >> buff >> total >> buff >> buff >> free >> buff >> buff >> available; + stat_stream.close(); + + int total_value = atoi(total.c_str()); + int available_value = atoi(available.c_str()); + + memory = get_memory_string(total_value); + memory_used = QString::number((total_value - available_value) *100 / total_value); + memory_free = get_memory_string(available_value); + +// obj_memory.insert("total", memory); + obj_memory.insert("total", total_value); +// obj_memory.insert("total_value", total_value); + obj_memory.insert("free", memory_free); + obj_memory.insert("load", memory_used); + + //------- + + obj_sys_data.insert("uptime", uptime); + obj_sys_data.insert("CPU", obj_cpu); + obj_sys_data.insert("memory", obj_memory); + + return obj_sys_data; +} + +QString LinuxDiagnostic::get_running(char* pid) +{ + char tbuf[32]; + char *cp; + int fd; + char c; + + sprintf(tbuf, "/proc/%s/stat", pid); + fd = open(tbuf, O_RDONLY, 0); + if (fd == -1) return QString("no open"); + + memset(tbuf, '\0', sizeof tbuf); // didn't feel like checking read() + read(fd, tbuf, sizeof tbuf - 1); + + cp = strrchr(tbuf, ')'); + if(!cp) return QString("no read"); + + c = cp[2]; + close(fd); + qDebug()<<c; + + if (c=='R') { + return "running"; + }else + if (c=='D') { + return "blocked"; + } + return QString("blocked"); +} + + +QString LinuxDiagnostic::get_proc_path(long pid) // work with root +{ + char exePath[PATH_MAX]; + char arg1[20]; + sprintf( arg1, "/proc/%ld/exe", pid ); + ssize_t len = ::readlink(arg1, exePath, sizeof(exePath)); + if (len == -1 || len == sizeof(exePath)) + len = 0; + exePath[len] = '\0'; + return QString::fromUtf8(exePath); +} + +std::vector<size_t> LinuxDiagnostic::get_cpu_times() { + std::ifstream proc_stat("/proc/stat"); + proc_stat.ignore(5, ' '); // Skip the 'cpu' prefix. + std::vector<size_t> times; + for (size_t time; proc_stat >> time; times.push_back(time)); + return times; +} + +bool LinuxDiagnostic::get_cpu_times(size_t &idle_time, size_t &total_time) { + const std::vector<size_t> cpu_times = get_cpu_times(); + if (cpu_times.size() < 4) + return false; + idle_time = cpu_times[3]; + total_time = std::accumulate(cpu_times.begin(), cpu_times.end(), 0); + return true; +} + + +/// --------------------------------------------------------------- +/// Process info +/// --------------------------------------------------------------- +QJsonObject LinuxDiagnostic::get_process_info(long proc_id, int totalRam) +{ + using std::ios_base; + using std::ifstream; + using std::string; + +// vm_usage = 0.0; +// resident_set = 0.0; + + // 'file' stat seems to give the most reliable results + char arg1[20]; + sprintf( arg1, "/proc/%ld/stat", proc_id ); + ifstream stat_stream(arg1,ios_base::in); + + // dummy vars for leading entries in stat that we don't care about + // + string pid, comm, state, ppid, pgrp, session, tty_nr; + string tpgid, flags, minflt, cminflt, majflt, cmajflt; + string utime, stime, cutime, cstime, priority, nice; + string O, itrealvalue; + + // the two fields we want + // + unsigned long vsize; + long rss; + double starttime; + + stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr + >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt + >> utime >> stime >> cutime >> cstime >> priority >> nice + >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest + + stat_stream.close(); + + long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages +// vm_usage = (vsize/1024.0); + long resident_set = rss * page_size_kb; + + int precentUseRss = (resident_set * 100) / totalRam; + + QProcess proc; + QString program = "ps"; + QStringList arguments; + arguments << "-p" << pid.c_str() << "-o" << "etimes"; + proc.start(program, arguments); + proc.waitForFinished(1000); + QString res = proc.readAll(); + + static QRegularExpression rx(R"([0-9]+)"); + + QJsonObject process_info; + QString status = "Offline"; + process_info.insert("status", "Offline"); + + QString node_dir = "/opt/cellframe-node/"; + + QString log_size, db_size, chain_size; + log_size = get_memory_string(get_file_size("log", node_dir) / 1024); + db_size = get_memory_string(get_file_size("DB", node_dir) / 1024); + chain_size = get_memory_string(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(QString::fromLocal8Bit(comm.c_str()).contains("cellframe-node")) + { + int uptime_sec = rx.match(res).captured(0).toInt(); + + QString uptime= get_uptime_string(uptime_sec); + + QString memory_use_value = get_memory_string(resident_set); + +// process_info.insert("PPID",QString::fromLocal8Bit(ppid.c_str())); +// process_info.insert("priory",QString::fromLocal8Bit(priority.c_str())); +// process_info.insert("start_time",QString::number(((starttime/100)/60))); + 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"; + } + + if(status == "Offline") + { + 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); + + return process_info; +} diff --git a/CellframeNodeDiagtool/LinuxDiagnostic.h b/CellframeNodeDiagtool/LinuxDiagnostic.h new file mode 100644 index 0000000000000000000000000000000000000000..37d89f52a0a691d57c8b942145071590f22cdd34 --- /dev/null +++ b/CellframeNodeDiagtool/LinuxDiagnostic.h @@ -0,0 +1,53 @@ +#ifndef LINUXDIAGNOSTIC_H +#define LINUXDIAGNOSTIC_H + + +#include "AbstractDiagnostic.h" +#include <unistd.h> +#include <fcntl.h> +#include <vector> + +#include <fstream> +#include <numeric> +#include <vector> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <locale.h> +#include <signal.h> +#include <dirent.h> + +#include <sys/sysinfo.h> + +using namespace std; + + +class LinuxDiagnostic : public AbstractDiagnostic +{ + Q_OBJECT +public: + explicit LinuxDiagnostic(AbstractDiagnostic * parent = nullptr); + +private: + QJsonObject get_sys_info(); + bool get_cpu_times(size_t &idle_time, size_t &total_time); + std::vector<size_t> get_cpu_times(); + long get_pid(); + QString get_running(char* pid); + QString get_proc_path(long pid); + QJsonObject get_process_info(long pid, int totalRam); + QJsonObject get_disk_info(); + + +private slots: + void info_update(); + +private: + size_t previous_idle_time{0}, previous_total_time{0}; + + +}; + +#endif // LINUXDIAGNOSTIC_H diff --git a/CellframeNodeDiagtool/MacDiagnostic.cpp b/CellframeNodeDiagtool/MacDiagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..761addce75d8bcb23102412475e2c479ec216c39 --- /dev/null +++ b/CellframeNodeDiagtool/MacDiagnostic.cpp @@ -0,0 +1,242 @@ +#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); + + QFile file(s_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/CellframeNodeDiagtool/MacDiagnostic.h b/CellframeNodeDiagtool/MacDiagnostic.h new file mode 100644 index 0000000000000000000000000000000000000000..540a1b88b540cce5833b70e0efd1e6645626cebb --- /dev/null +++ b/CellframeNodeDiagtool/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/CellframeNodeDiagtool/NodeDiagnostic.pro b/CellframeNodeDiagtool/NodeDiagnostic.pro new file mode 100644 index 0000000000000000000000000000000000000000..ee1416aaa153aed147c51acd0df25f9c324116a7 --- /dev/null +++ b/CellframeNodeDiagtool/NodeDiagnostic.pro @@ -0,0 +1,43 @@ +QT -= gui +QT += core network + +CONFIG += c++17 console +CONFIG -= app_bundle + +# You can make your code fail to compile if it uses deprecated APIs. +# 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 \ + 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 diff --git a/CellframeNodeDiagtool/WinDiagnostic.cpp b/CellframeNodeDiagtool/WinDiagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1ddd7dcee9744f17bde983860698c76ee64c800 --- /dev/null +++ b/CellframeNodeDiagtool/WinDiagnostic.cpp @@ -0,0 +1,273 @@ +#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()); + + QFile file(s_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/CellframeNodeDiagtool/WinDiagnostic.h b/CellframeNodeDiagtool/WinDiagnostic.h new file mode 100644 index 0000000000000000000000000000000000000000..769452fa6065392943825f504a9ad27c4e54e58a --- /dev/null +++ b/CellframeNodeDiagtool/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/CellframeNodeDiagtool/main.cpp b/CellframeNodeDiagtool/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4db95221cd35ace629ce42b6333dba533428f57 --- /dev/null +++ b/CellframeNodeDiagtool/main.cpp @@ -0,0 +1,12 @@ +#include <QCoreApplication> +#include "DiagnosticWorker.h" + +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/CellframeNodeTray/NodeTrayCommandController.cpp b/CellframeNodeTray/NodeTrayCommandController.cpp index 56964d73f2bffda4167387ee4517dd7e4cd427c3..631ec09567bfbe1398d23b61a75bb083302d8cd1 100644 --- a/CellframeNodeTray/NodeTrayCommandController.cpp +++ b/CellframeNodeTray/NodeTrayCommandController.cpp @@ -1,11 +1,38 @@ #include "NodeTrayCommandController.h" NodeTrayCommandController::NodeTrayCommandController(QObject *parent) - : QObject{parent} + : QObject(parent) { m_configDir = getConfigPath(); m_statusInitConftool = initConfTool(); + initEngine(); +} + +void NodeTrayCommandController::initEngine() +{ + QQmlContext * context = m_engine.rootContext(); + context->setContextProperty("trayCommandController", this); + context->setContextProperty("diagnosticWorker", &diagnosticWorker); + + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&m_engine, &QQmlApplicationEngine::objectCreated, + this, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + + m_engine.load(url); +} + +NodeTrayCommandController::~NodeTrayCommandController() +{ + +} + +QQmlApplicationEngine *NodeTrayCommandController::qmlEngine() +{ + return &m_engine; } bool NodeTrayCommandController::initConfTool() diff --git a/CellframeNodeTray/NodeTrayCommandController.h b/CellframeNodeTray/NodeTrayCommandController.h index bff1ed9193423c94c00dc879ad907293a73317e3..8daf9be583438e81ff9bbea51c2935139afddd57 100644 --- a/CellframeNodeTray/NodeTrayCommandController.h +++ b/CellframeNodeTray/NodeTrayCommandController.h @@ -2,6 +2,9 @@ #define NODETRAYCOMMANDCONTROLLER_H #include <QObject> +#include <QApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> #include <QDebug> #include <QProcess> #include <QDir> @@ -11,6 +14,9 @@ #include <QQmlEngine> + +#include "DiagnosticWorker.h" + #ifdef WIN32 #include <windows.h> #endif @@ -20,7 +26,9 @@ class NodeTrayCommandController : public QObject Q_OBJECT public: explicit NodeTrayCommandController(QObject *parent = nullptr); + ~NodeTrayCommandController(); + QQmlApplicationEngine *qmlEngine(); enum TypeServiceCommands{ Status = 0, @@ -39,16 +47,21 @@ public: void showMessage(const QString msg = "Could not find cellframe-node-config", const QMessageBox::Icon icn = QMessageBox::Warning); + DiagnosticWorker &diagnosticWorker = DiagnosticWorker::getInstance(); + private: bool m_statusInitConftool{false}; QString m_confToolPath{""}; QString m_configDir{""}; + QQmlApplicationEngine m_engine; + private: bool initConfTool(); QString getConfigPath(); QString sendRequest(QString req); + void initEngine(); signals: Q_INVOKABLE void sigResultStatus(bool status); diff --git a/CellframeNodeTray/main.cpp b/CellframeNodeTray/main.cpp deleted file mode 100644 index 930c93ad68b566531a6512610decb59d999f92da..0000000000000000000000000000000000000000 --- a/CellframeNodeTray/main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include <QApplication> -#include <QQmlApplicationEngine> -#include <QIcon> -#include <QQuickWidget> -#include <QSystemTrayIcon> -#include <QQmlContext> -#include <QDebug> - -#include <QProcess> - -#include "NodeTrayCommandController.h" - -int main(int argc, char *argv[]) -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif - QApplication app(argc, argv); - - - - QQmlApplicationEngine engine; - const QUrl url(QStringLiteral("qrc:/main.qml")); - QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, - &app, [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, Qt::QueuedConnection); - - NodeTrayCommandController *trayCommandController = new NodeTrayCommandController(); - QQmlContext * context = engine.rootContext(); - - context->setContextProperty("trayCommandController",trayCommandController); - - engine.load(url); - - return app.exec(); -} diff --git a/CellframeNodeTray/main.qml b/CellframeNodeTray/main.qml index fd77405ee9c81ac7f7c3626eac417574ba7cab20..7061d0f137a11dadd3bc153f617c54c5146bcbb3 100644 --- a/CellframeNodeTray/main.qml +++ b/CellframeNodeTray/main.qml @@ -8,7 +8,8 @@ ApplicationWindow { id: systemTrayApplicationWindow visible: false - property bool flagAutoCheck:false + property bool diagStatusInit:false + property bool diagStatusSendData:true Menu { id: menu @@ -24,7 +25,6 @@ ApplicationWindow { // } Menu{ - id: dirsMenu title: qsTr("Open dir") MenuItem { @@ -44,17 +44,16 @@ ApplicationWindow { Menu{ title: qsTr("Service") MenuItem { + property bool status: false id: autostartItemMenu - text: qsTr("Set autostart cellframe-node") + text: status ? qsTr("Autostart cellframe-node: enabled"): + qsTr("Autostart cellframe-node: disabled") - checkable: true - - onCheckedChanged: - { - if(checked && !flagAutoCheck) - trayCommandController.serviceCommand(1) - else if(!checked && !flagAutoCheck) + onTriggered: { + if(status) trayCommandController.serviceCommand(2) + else + trayCommandController.serviceCommand(1) } } MenuItem { @@ -70,6 +69,27 @@ ApplicationWindow { onTriggered: trayCommandController.serviceCommand(5) } } + + Menu{ + title: qsTr("Diagnostic") + visible: diagStatusInit + + MenuItem { + + text: diagStatusSendData? qsTr("Send diagnostic data: enabled") + : qsTr("Send diagnostic data: disabled") + onTriggered: diagnosticWorker.switchSendData() + } + MenuItem { + text: qsTr("Sended data list") + onTriggered: diagnosticWorker.getSendedData() + } + MenuItem { + text: qsTr("Open diagnostic config") + onTriggered: diagnosticWorker.openConfigFile() + } + + } } SystemTrayIcon { @@ -83,12 +103,11 @@ ApplicationWindow { onActivated: { - flagAutoCheck = true trayCommandController.serviceCommand(0) } Component.onCompleted: { - flagAutoCheck = true + diagStatusInit = diagnosticWorker.getInitStatus(); trayCommandController.serviceCommand(0) } } @@ -98,11 +117,7 @@ ApplicationWindow { function onSigResultStatus(status) { // console.log(status) - autostartItemMenu.checked = status - - if(flagAutoCheck) - flagAutoCheck = false - + autostartItemMenu.status = status } function onSigShowMessage(title, message) @@ -110,5 +125,17 @@ ApplicationWindow { systemTrayIcon.showMessage(title, message, "qrc:/cellframe-node-logo.svg", 5000) } } + + Connections{ + target: diagnosticWorker + function onSigInitStatusChanged(status) + { + diagStatusInit = status + } + function onSigSendDataStatusChanged(status) + { + diagStatusSendData = status + } + } } diff --git a/cellfram-node-diagtool.pro b/cellfram-node-diagtool.pro new file mode 100644 index 0000000000000000000000000000000000000000..e97454f8268b4be624bd99d39a8e4ecba2a2cb8f --- /dev/null +++ b/cellfram-node-diagtool.pro @@ -0,0 +1,59 @@ +QT += qml quick widgets quickwidgets + +CONFIG += c++17 + +# You can make your code fail to compile if it uses deprecated APIs. +# 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 + +INCLUDEPATH += CellframeNodeTray \ + CellframeNodeDiagtool + +include (cellframe-sdk/dap-sdk/core/libdap.pri) + +SOURCES += \ + CellframeNodeTray/NodeTrayCommandController.cpp \ + main.cpp \ + CellframeNodeDiagtool/AbstractDiagnostic.cpp \ + CellframeNodeDiagtool/DiagnosticWorker.cpp + +HEADERS += \ + CellframeNodeTray/NodeTrayCommandController.h \ + CellframeNodeDiagtool/AbstractDiagnostic.h \ + CellframeNodeDiagtool/DiagnosticWorker.h + +win32 { + DEFINES += CLI_PATH=\\\"cellframe-node-cli.exe\\\" + HEADERS += CellframeNodeDiagtool/WinDiagnostic.h + SOURCES += CellframeNodeDiagtool/WinDiagnostic.cpp +} + +mac { + DEFINES += CLI_PATH=\\\"./cellframe-node-cli\\\" + HEADERS += CellframeNodeDiagtool/MacDiagnostic.h + SOURCES += CellframeNodeDiagtool/MacDiagnostic.cpp +} + +else: !win32 { + DEFINES += CLI_PATH=\\\"/opt/cellframe-node/bin/cellframe-node-cli\\\" + HEADERS += CellframeNodeDiagtool/LinuxDiagnostic.h + SOURCES += CellframeNodeDiagtool/LinuxDiagnostic.cpp +} + +RESOURCES += CellframeNodeTray/qml.qrc \ + CellframeNodeTray/Resources.qrc + + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + + +DISTFILES += diff --git a/cellfram-node-tray.pro b/cellfram-node-tray.pro deleted file mode 100644 index 8443aab533c01474d9995667031dcfbb17245adc..0000000000000000000000000000000000000000 --- a/cellfram-node-tray.pro +++ /dev/null @@ -1,30 +0,0 @@ -QT += qml quick widgets quickwidgets - -# You can make your code fail to compile if it uses deprecated APIs. -# 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 - -INCLUDEPATH += CellframeNodeTray - -SOURCES += \ - CellframeNodeTray/NodeTrayCommandController.cpp \ - CellframeNodeTray/main.cpp - -RESOURCES += CellframeNodeTray/qml.qrc \ - CellframeNodeTray/Resources.qrc - -# Additional import path used to resolve QML modules in Qt Creator's code model -QML_IMPORT_PATH = - -# Additional import path used to resolve QML modules just for Qt Quick Designer -QML_DESIGNER_IMPORT_PATH = - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target - -HEADERS += \ - CellframeNodeTray/NodeTrayCommandController.h - -DISTFILES += diff --git a/cellframe-sdk b/cellframe-sdk new file mode 160000 index 0000000000000000000000000000000000000000..45751058d77a4a7501009ba3942b6aa507f722dc --- /dev/null +++ b/cellframe-sdk @@ -0,0 +1 @@ +Subproject commit 45751058d77a4a7501009ba3942b6aa507f722dc diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26f158626c0de977cda7d860286909f18c7537cb --- /dev/null +++ b/main.cpp @@ -0,0 +1,38 @@ +#include <QApplication> +#include <QQmlApplicationEngine> +#include <QIcon> +#include <QQuickWidget> +#include <QSystemTrayIcon> +#include <QQmlContext> +#include <QDebug> + +#include <QProcess> + +#include "NodeTrayCommandController.h" +#include "DiagnosticWorker.h" + +bool findArg(char** begin, char** end, const std::string& option) +{ + return std::find(begin, end, option) != end; +} + + +int main(int argc, char *argv[]) +{ + +qSetMessagePattern("%{type} %{if-category}%{category}: %{endif}%{function}: %{message}"); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + QApplication app(argc, argv); + + bool start_diag = !findArg(argv, argv+argc,"--no-diag"); + bool start_tray = !findArg(argv, argv+argc,"--no-tray"); + + if(start_diag) DiagnosticWorker::getInstance().init(); + if(start_tray) NodeTrayCommandController *trayCommandController = new NodeTrayCommandController(); + + + return app.exec(); +}