feat: add ping assistant to project

This commit is contained in:
Qsaker 2024-01-10 12:01:47 +08:00
parent 4e34abe211
commit c8be12ee93
14 changed files with 686 additions and 1 deletions

View File

@ -89,6 +89,7 @@ list(APPEND SAK_ASSISTANT_SOURCES src/assistants/sakassistantsfactory.cc)
sak_import_assistant("crc")
sak_import_assistant("mdns")
sak_import_assistant("ping")
sak_import_assistant("ascii")
sak_import_assistant("base64")
sak_import_assistant("number")

View File

@ -74,5 +74,6 @@
<file>resources/icon/zoomout.svg</file>
<file>resources/icon/play.svg</file>
<file>resources/icon/stop.svg</file>
<file>resources/icon/ping.ico</file>
</qresource>
</RCC>

BIN
resources/icon/ping.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

View File

@ -35,6 +35,7 @@ endmacro()
add_subdirectory(crc)
add_subdirectory(mdns)
add_subdirectory(ping)
add_subdirectory(ascii)
add_subdirectory(base64)
add_subdirectory(number)

View File

@ -0,0 +1,6 @@
set(APP_ASSISTANT_OWN_SOURCE ${CMAKE_SOURCE_DIR}/src/common/commonui/sakcommonmainwindow.h
${CMAKE_SOURCE_DIR}/src/common/commonui/sakcommonmainwindow.cc)
set(APP_ASSISTANT_OWN_LIBS Qt${QT_VERSION_MAJOR}::Widgets)
sak_add_assistant("ping" "PingAssistant")
sak_3rd_setup_glog(PingAssistant)

View File

@ -0,0 +1,16 @@
/***************************************************************************************************
* Copyright 2024 Qsaker(qsaker@foxmail.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of QtSwissArmyKnife project.
*
* QtSwissArmyKnife is licensed according to the terms in the file LICENCE in the root of the source
* code directory.
**************************************************************************************************/
#include "sakpingassistant.h"
#include "sakui.h"
int main(int argc, char *argv[])
{
const QString appName = QObject::tr("Ping Assistant");
return sakExec<SAKPingAssistant>(argc, argv, appName, false);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "ping.ico"

View File

@ -0,0 +1,527 @@
/***************************************************************************************************
* Copyright 2024 Qsaker(qsaker@foxmail.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of QtSwissArmyKnife project.
*
* QtSwissArmyKnife is licensed according to the terms in the file LICENCE in the root of the source
* code directory.
**************************************************************************************************/
#include "sakpingassistant.h"
#include "ui_sakpingassistant.h"
#include <QApplication>
#include <QButtonGroup>
#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMenuBar>
#include <QMessageBox>
#include <QProcess>
#include <QRadioButton>
#include <QRunnable>
#include <QStandardPaths>
#include <QStyle>
#include <QThreadPool>
static const QString fileName()
{
QStandardPaths::StandardLocation type = QStandardPaths::AppConfigLocation;
QString path = QStandardPaths::writableLocation(type);
auto ret = QString("%1/%2.ini").arg(path, QCoreApplication::applicationName());
return ret;
}
class PingTask : public QRunnable
{
public:
PingTask(SAKPingAssistant *main_window, const QString &ip)
{
main_window_ = main_window;
ip_ = ip;
}
void run() override
{
QProcess p;
p.start("ping", QStringList() << "-a" << ip_);
main_window_->emitPingStarted(ip_);
bool ret = p.waitForFinished();
if (!ret) {
p.terminate();
}
QByteArray all = p.readAll();
QString all_string = QString::fromUtf8(all);
QString description = ip_;
if (all_string.contains("TTL=")) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QStringList list = all_string.split(" ", Qt::SkipEmptyParts);
#else
QStringList list = all_string.split(" ", QString::SkipEmptyParts);
#endif
if (list.length() >= 2) {
description = list.at(2);
}
main_window_->emitPingFinished(ip_, true, description);
} else {
main_window_->emitPingFinished(ip_, false, description);
}
}
private:
SAKPingAssistant *main_window_;
QString ip_;
};
SAKPingAssistant::SAKPingAssistant(QWidget *parent)
: SAKCommonMainWindow(parent)
, ui(new Ui::SAKPingAssistant)
, m_pausing(false)
, m_finishedCount(0)
{
ui->setupUi(this);
m_buildDate = QString(__DATE__);
m_buildTime = QString(__TIME__);
m_playTimer = new QTimer(this);
m_playTimer->setInterval(100);
m_playTimer->setSingleShot(true);
connect(m_playTimer, &QTimer::timeout, this, &SAKPingAssistant::onPlayTimerTimeout);
m_settings = new QSettings(::fileName(), QSettings::IniFormat, this);
connect(this,
&SAKPingAssistant::pingStarted,
this,
&SAKPingAssistant::onPingStarted,
Qt::QueuedConnection);
connect(this,
&SAKPingAssistant::pingFinished,
this,
&SAKPingAssistant::onPingFinished,
Qt::QueuedConnection);
#if 0
int count = QThread::idealThreadCount();
count = 10*count;
QThreadPool::globalInstance()->setMaxThreadCount(count);
#endif
init();
setWindowTitle("Ping Assistant");
setWindowIcon(QIcon(":/resources/icon/ping.ico"));
ui->tableWidget->setAlternatingRowColors(true);
}
SAKPingAssistant::~SAKPingAssistant()
{
delete ui;
}
void SAKPingAssistant::emitPingStarted(const QString &ip)
{
emit pingStarted(ip);
}
void SAKPingAssistant::emitPingFinished(const QString &ip,
bool is_online,
const QString &description)
{
emit pingFinished(ip, is_online, description);
}
void SAKPingAssistant::init()
{
initTableWidget();
initToolBar();
m_progressStatus = new QLabel(this);
QString txt = tr("Total: --, Active: --, Finished: --, Remain: --");
m_progressStatus->setText(txt);
ui->statusbar->addPermanentWidget(m_progressStatus);
}
void SAKPingAssistant::initTableWidget()
{
ui->tableWidget->setColumnCount(4);
QStringList header_labels;
header_labels << tr("Target Address") << tr("Online Status") << tr("Comparing")
<< tr("Host Name");
ui->tableWidget->setHorizontalHeaderLabels(header_labels);
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget->verticalHeader()->hide();
}
void SAKPingAssistant::initToolBar()
{
initRunToolBar();
initSettingToolBar();
initShowToolBar();
}
void SAKPingAssistant::initRunToolBar()
{
m_runToolBar = new QToolBar();
addToolBar(m_runToolBar);
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MediaPlay);
m_playAction = m_runToolBar->addAction(icon, "", [=]() { this->play(); });
m_playAction->setToolTip(tr("Start scan"));
connect(m_playAction, &QAction::hovered, this, [=]() {
ui->statusbar->showMessage(m_playAction->toolTip(), 3 * 1000);
});
icon = QApplication::style()->standardIcon(QStyle::SP_MediaPause);
m_pauseAction = m_runToolBar->addAction(icon, "", [=]() { this->pause(); });
m_pauseAction->setEnabled(false);
m_pauseAction->setToolTip(tr("Pause scan"));
connect(m_pauseAction, &QAction::hovered, this, [=]() {
ui->statusbar->showMessage(m_pauseAction->toolTip(), 3 * 1000);
});
icon = QApplication::style()->standardIcon(QStyle::SP_MediaStop);
m_stopAction = m_runToolBar->addAction(icon, "", [=]() { this->stop(); });
m_stopAction->setEnabled(false);
m_stopAction->setToolTip(tr("Stop scan"));
connect(m_stopAction, &QAction::hovered, this, [=]() {
ui->statusbar->showMessage(m_stopAction->toolTip(), 3 * 1000);
});
}
void SAKPingAssistant::initSettingToolBar()
{
m_settingToolBar = new QToolBar();
addToolBar(m_settingToolBar);
QWidget *w = new QWidget(this);
QHBoxLayout *h_box_layout = new QHBoxLayout(w);
h_box_layout->setContentsMargins(0, 0, 0, 0);
m_settingToolBar->addWidget(w);
h_box_layout->addWidget(new QLabel(tr("Start address")));
QLineEdit *begin_ip = new QLineEdit(this);
begin_ip->setText(m_settings->value(m_keyCtx.beginIp).toString());
m_beginIp = begin_ip->text().trimmed();
h_box_layout->addWidget(begin_ip);
connect(begin_ip, &QLineEdit::textChanged, this, [=](const QString &txt) {
this->m_beginIp = txt;
m_settings->setValue(this->m_keyCtx.beginIp, txt);
});
h_box_layout->addWidget(new QLabel(tr("End address")));
QLineEdit *end_ip = new QLineEdit(this);
end_ip->setText(m_settings->value(m_keyCtx.endIp).toString());
m_endIp = end_ip->text().trimmed();
h_box_layout->addWidget(end_ip);
connect(end_ip, &QLineEdit::textChanged, this, [=](const QString &txt) {
this->m_endIp = txt;
m_settings->setValue(this->m_keyCtx.endIp, txt);
});
}
void SAKPingAssistant::initShowToolBar()
{
QRadioButton *show_all = new QRadioButton(tr("Show all"));
QRadioButton *show_online = new QRadioButton(tr("Show online"));
QWidget *w = new QWidget();
QHBoxLayout *h_box_layout = new QHBoxLayout(w);
h_box_layout->setContentsMargins(0, 0, 0, 0);
h_box_layout->addWidget(show_all);
h_box_layout->addWidget(show_online);
QButtonGroup *button_group = new QButtonGroup(this);
button_group->addButton(show_all);
button_group->addButton(show_online);
m_showToolBar = new QToolBar();
m_showToolBar->addWidget(w);
addToolBar(m_showToolBar);
int model = m_settings->value(m_keyCtx.showModel).toInt();
m_showModel = model;
if (model == ShowAll) {
show_all->setChecked(true);
} else if (model == ShowOnline) {
show_online->setChecked(true);
} else {
m_showModel = ShowAll;
show_all->setChecked(true);
}
connect(show_all, &QRadioButton::clicked, this, [=]() {
this->m_showModel = ShowAll;
m_settings->setValue(m_keyCtx.showModel, ShowAll);
updateRowVisible();
});
connect(show_online, &QRadioButton::clicked, this, [=]() {
this->m_showModel = ShowOnline;
m_settings->setValue(m_keyCtx.showModel, ShowOnline);
updateRowVisible();
});
}
void SAKPingAssistant::play()
{
if (!isValidIp()) {
return;
}
m_playAction->setEnabled(false);
m_pauseAction->setEnabled(true);
m_stopAction->setEnabled(true);
if (m_pausing) {
m_pausing = false;
m_pauseAction->setEnabled(true);
}
m_preScanResult.clear();
for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
QTableWidgetItem *item1 = ui->tableWidget->item(i, 0);
QTableWidgetItem *item2 = ui->tableWidget->item(i, 1);
if (item1 && item2) {
QPair<QString, bool> p(item1->text(), item2->data(Qt::UserRole).toBool());
m_preScanResult.append(p);
}
}
m_finishedCount = 0;
int count = ipCount();
ui->tableWidget->setRowCount(count);
updateProgressStatus();
int index = m_beginIp.lastIndexOf('.');
QString begin_ip_left = m_beginIp.left(index + 1);
QString begin_ip_right = m_beginIp.right(m_beginIp.length() - index - 1);
int begin_ip = begin_ip_right.toInt();
for (int i = 0; i < count; i++) {
QString ip = begin_ip_left + QString::number(begin_ip + i);
QTableWidgetItem *ip_item = new QTableWidgetItem(ip);
ui->tableWidget->setItem(i, 0, ip_item);
QTableWidgetItem *status_item = new QTableWidgetItem("--");
status_item->setData(Qt::UserRole, false);
ui->tableWidget->setItem(i, 1, status_item);
QTableWidgetItem *compare_item = new QTableWidgetItem("--");
ui->tableWidget->setItem(i, 2, compare_item);
QTableWidgetItem *description_item = new QTableWidgetItem("--");
ui->tableWidget->setItem(i, 3, description_item);
}
m_settingToolBar->setEnabled(false);
updateRowVisible();
m_currentRow = 0;
m_playTimer->start();
}
void SAKPingAssistant::pause()
{
m_playAction->setEnabled(true);
m_pauseAction->setEnabled(false);
m_stopAction->setEnabled(true);
m_pausing = true;
}
void SAKPingAssistant::stop()
{
m_playAction->setEnabled(true);
m_pauseAction->setEnabled(false);
m_stopAction->setEnabled(false);
m_settingToolBar->setEnabled(true);
m_pausing = false;
m_playTimer->stop();
}
bool SAKPingAssistant::isValidIp()
{
if (m_beginIp.isEmpty() || m_endIp.isEmpty()) {
QMessageBox::warning(this,
tr("IP Error"),
tr("IP settings error, "
"the start ip and end ip must be set, "
"please check and try again. "));
return false;
}
int begin_ip_index = m_beginIp.lastIndexOf('.');
QString part_of_begin_ip = m_beginIp.left(begin_ip_index);
int end_ip_index = m_endIp.lastIndexOf('.');
QString part_of_end_ip = m_endIp.left(end_ip_index);
if (part_of_begin_ip != part_of_end_ip) {
QMessageBox::warning(this,
tr("IP Error"),
tr("IP settings error, "
"the start ip and end ip must be in the same network segment, "
"please check and try again. "));
return false;
}
if (ipCount() < 1) {
QMessageBox::warning(this,
tr("IP Error"),
tr("IP settings error, "
"the start ip must be less than the end ip, "
"please check and try again."));
return false;
}
return true;
}
int SAKPingAssistant::row(const QString &ip)
{
for (int row = 0; row < ui->tableWidget->rowCount(); row++) {
QTableWidgetItem *item = ui->tableWidget->item(row, 0);
if (item && item->text() == ip) {
return row;
}
}
return -1;
}
void SAKPingAssistant::updateRowVisible()
{
for (int row = 0; row < ui->tableWidget->rowCount(); row++) {
QTableWidgetItem *item = ui->tableWidget->item(row, 1);
if (item) {
if (m_showModel == ShowAll) {
ui->tableWidget->setRowHidden(row, false);
} else {
bool is_online = item->data(Qt::UserRole).toBool();
ui->tableWidget->setRowHidden(row, !is_online);
}
} else {
ui->tableWidget->setRowHidden(row, false);
}
}
}
void SAKPingAssistant::updateProgressStatus()
{
int all = ui->tableWidget->rowCount();
int active = QThreadPool::globalInstance()->activeThreadCount();
int finished = m_finishedCount;
int remain = all - m_finishedCount - active;
remain = remain > 0 ? remain : remain;
QString txt = tr("Total%1Active%2Finished%3Remain%4")
.arg(all)
.arg(active)
.arg(finished)
.arg(remain);
m_progressStatus->setText(txt);
}
int SAKPingAssistant::ipCount()
{
int begin = m_beginIp.split('.').last().toInt();
int end = m_endIp.split('.').last().toInt();
return end - begin + 1;
}
QString SAKPingAssistant::ip(int row)
{
if (row >= 0 && row < ui->tableWidget->rowCount()) {
QTableWidgetItem *item = ui->tableWidget->item(row, 0);
if (item) {
return item->text();
}
}
return "";
}
void SAKPingAssistant::onPlayTimerTimeout()
{
if (m_pausing) {
return;
}
int active_count = QThreadPool::globalInstance()->activeThreadCount();
int max_count = QThreadPool::globalInstance()->maxThreadCount();
if (active_count < max_count) {
const QString ip = this->ip(m_currentRow);
if (ip.isEmpty()) {
qDebug() << __FUNCTION__;
} else {
PingTask *ping_task = new PingTask(this, ip);
QThreadPool::globalInstance()->start(ping_task);
}
m_currentRow += 1;
}
if (m_currentRow == ui->tableWidget->rowCount()) {
} else {
m_playTimer->start();
}
updateProgressStatus();
}
void SAKPingAssistant::onPingStarted(const QString &ip)
{
int row = this->row(ip);
QTableWidgetItem *item = ui->tableWidget->item(row, 1);
if (item) {
item->setText(tr("Scaning"));
item->setBackground(QBrush(Qt::yellow));
}
updateProgressStatus();
}
void SAKPingAssistant::onPingFinished(const QString &ip, bool is_online, const QString &description)
{
int row = this->row(ip);
QTableWidgetItem *status_item = ui->tableWidget->item(row, 1);
if (status_item) {
status_item->setText(is_online ? tr("Online") : tr("Offline"));
status_item->setBackground(is_online ? QBrush(Qt::green) : QBrush(Qt::red));
status_item->setData(Qt::UserRole, is_online);
}
for (int i = 0; i < m_preScanResult.count(); i++) {
QPair<QString, bool> p = m_preScanResult.at(i);
if ((p.first == ip) && (p.second == false)) {
if (is_online) {
QTableWidgetItem *compare_item = ui->tableWidget->item(row, 2);
compare_item->setText("+");
break;
}
}
}
QTableWidgetItem *description_item = ui->tableWidget->item(row, 3);
if (description_item) {
description_item->setText(description);
}
m_finishedCount += 1;
if (m_finishedCount == ui->tableWidget->rowCount()) {
QMessageBox::information(
this,
tr("Scaning Finished"),
tr("Scaning finished, you can screen out the result by the toolbar."));
m_playAction->setEnabled(true);
m_pauseAction->setEnabled(false);
m_stopAction->setEnabled(false);
m_settingToolBar->setEnabled(true);
}
updateRowVisible();
updateProgressStatus();
}

View File

@ -0,0 +1,98 @@
/***************************************************************************************************
* Copyright 2024 Qsaker(qsaker@foxmail.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of QtSwissArmyKnife project.
*
* QtSwissArmyKnife is licensed according to the terms in the file LICENCE in the root of the source
* code directory.
**************************************************************************************************/
#ifndef SAKPINGASSISTANT_H
#define SAKPINGASSISTANT_H
#include <QLabel>
#include <QSettings>
#include <QTimer>
#include <QToolBar>
#include <QWidget>
#include "sakcommonmainwindow.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class SAKPingAssistant;
}
QT_END_NAMESPACE
class SAKPingAssistant : public SAKCommonMainWindow
{
Q_OBJECT
public:
Q_INVOKABLE SAKPingAssistant(QWidget *parent = nullptr);
~SAKPingAssistant();
void emitPingStarted(const QString &ip);
void emitPingFinished(const QString &ip, bool isOnline, const QString &description);
signals:
void pingStarted(const QString &ip);
void pingFinished(const QString &ip, bool isOnline, const QString &description);
private:
enum ShowModel { ShowAll, ShowOnline };
struct
{
const QString beginIp = "beginIp";
const QString endIp = "endIp";
const QString showModel = "showModel";
} m_keyCtx;
private:
Ui::SAKPingAssistant *ui;
QTimer *m_playTimer;
QString m_beginIp;
QString m_endIp;
int m_currentRow;
bool m_pausing;
QToolBar *m_runToolBar;
QToolBar *m_showToolBar;
QToolBar *m_settingToolBar;
QSettings *m_settings;
int m_finishedCount;
int m_showModel;
QList<QPair<QString, bool>> m_preScanResult;
QLabel *m_progressStatus;
QAction *m_playAction;
QAction *m_pauseAction;
QAction *m_stopAction;
QString m_buildDate;
QString m_buildTime;
private:
void init();
void initTableWidget();
void initToolBar();
void initRunToolBar();
void initSettingToolBar();
void initShowToolBar();
void play();
void pause();
void stop();
bool isValidIp();
int ipCount();
QString ip(int row);
int row(const QString &ip);
void updateRowVisible();
void updateProgressStatus();
void onPlayTimerTimeout();
void onPingStarted(const QString &ip);
void onPingFinished(const QString &ip, bool is_online, const QString &description);
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SAKPingAssistant</class>
<widget class="QMainWindow" name="SAKPingAssistant">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>609</width>
<height>367</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableWidget" name="tableWidget"/>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -36,6 +36,9 @@
#ifdef SAK_IMPORT_MODULE_MDNSASSISTANT
#include "sakmdnsassistant.h"
#endif
#ifdef SAK_IMPORT_MODULE_PINGASSISTANT
#include "sakpingassistant.h"
#endif
SAKAssistantsFactory::SAKAssistantsFactory(QObject* parent)
: QObject(parent)
@ -64,6 +67,9 @@ SAKAssistantsFactory::SAKAssistantsFactory(QObject* parent)
#ifdef SAK_IMPORT_MODULE_MDNSASSISTANT
registerAssistant<SAKMdnsAssistant>(AssistantTypesMdns, tr("MDNS Assistant"));
#endif
#ifdef SAK_IMPORT_MODULE_PINGASSISTANT
registerAssistant<SAKPingAssistant>(AssistantTypesPing, tr("Ping Assistant"));
#endif
}
QList<int> SAKAssistantsFactory::supportedAssistants()

View File

@ -27,7 +27,8 @@ private:
AssistantTypesString,
AssistantTypesBroadcast,
AssistantTypesBase64,
AssistantTypesMdns
AssistantTypesMdns,
AssistantTypesPing
};
public: