|
|
|
|
#include "rescueloadwidget.h"
|
|
|
|
|
#include <QQuickWidget>
|
|
|
|
|
#include "qjsonarray.h"
|
|
|
|
|
#include "ui_rescueloadwidget.h"
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QQuickView>
|
|
|
|
|
#include <QUrl>
|
|
|
|
|
#include <QRandomGenerator>
|
|
|
|
|
#include <QTextBlock>
|
|
|
|
|
#include <QtQml>
|
|
|
|
|
#include <QQmlContext>
|
|
|
|
|
#include <QQmlApplicationEngine>
|
|
|
|
|
#include <QQmlComponent>
|
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include "tiqgeoserviceproviderfactory.h"
|
|
|
|
|
RescueLoadWidget::RescueLoadWidget(QWidget *parent)
|
|
|
|
|
: QWidget(parent)
|
|
|
|
|
, ui(new Ui::RescueLoadWidget)
|
|
|
|
|
{
|
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
initialModel(); // 初始化数据model
|
|
|
|
|
initialUi(); // 初始化表格样式
|
|
|
|
|
|
|
|
|
|
// 数据交互和视图设置
|
|
|
|
|
rescueLoad = new RescueLoad(this);
|
|
|
|
|
rescueLoad->startCommunication(); // todo: 转移到子线程中运行
|
|
|
|
|
// rescueLoad->sendMessage(QString("Hello"));
|
|
|
|
|
|
|
|
|
|
connect(rescueLoad, &RescueLoad::dataUpdate, rescueLoad, [=](ImsiData data){
|
|
|
|
|
imsiDataModel->updateData(data);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 地图组件, 必须在RescueLoad和ImsiDataModel初始化后加载
|
|
|
|
|
openQLocationMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RescueLoadWidget::~RescueLoadWidget()
|
|
|
|
|
{
|
|
|
|
|
ui->deviceDetail->clearContents();
|
|
|
|
|
delete protoItem;
|
|
|
|
|
delete standardItem;
|
|
|
|
|
delete ui;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用QLocation加载地图模块 (这里可以考虑换成内嵌网页地图)
|
|
|
|
|
void RescueLoadWidget::openQLocationMap() {
|
|
|
|
|
Q_IMPORT_PLUGIN(TiQGeoServiceProviderFactory)
|
|
|
|
|
QQuickView *qmlView = new QQuickView();
|
|
|
|
|
// 列表和地图共享一个datamodel,减少重复的数据处理
|
|
|
|
|
qmlView->rootContext()->setContextProperty("imsiDataModel", imsiDataModel); // 注意,先绑定属性,再设置qml
|
|
|
|
|
qmlView->rootContext()->setContextProperty("imsiSelectModel", ui->deviceview->selectionModel()); // 注意,先绑定属性,再设置qml
|
|
|
|
|
qmlView->setSource(QUrl("qrc:/places_map.qml"));
|
|
|
|
|
QWidget *container = QWidget::createWindowContainer(qmlView, this);
|
|
|
|
|
ui->mapGroup->setLayout(new QVBoxLayout);
|
|
|
|
|
ui->mapGroup->layout()->addWidget(container);
|
|
|
|
|
qDebug()<<"[搜救] 搜救地图开启"<<imsiDataModel->getDataMap().keys().size();
|
|
|
|
|
// 接收QML的信号
|
|
|
|
|
QObject* object = (QObject* )qmlView->rootObject();
|
|
|
|
|
// connect(object, SIGNAL(locationClicked(QString)), this, SLOT(updateDetailTable(QString)));
|
|
|
|
|
QObject::connect(object, SIGNAL(indexClicked(int)), this, SLOT(tableIndexClicked(int)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RescueLoadWidget::tableIndexClicked(int row){
|
|
|
|
|
emit ui->deviceview->clicked(imsiDataModel->index(row));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RescueLoadWidget::initialUi()
|
|
|
|
|
{
|
|
|
|
|
// 设备imsi列表样式
|
|
|
|
|
setTable(ui->deviceview);
|
|
|
|
|
setTable(ui->bnameview);
|
|
|
|
|
setTable(ui->wnameview);
|
|
|
|
|
// 单元格样式
|
|
|
|
|
protoItem = new QTableWidgetItem;
|
|
|
|
|
protoItem->setTextAlignment(Qt::AlignCenter);
|
|
|
|
|
standardItem = new QStandardItem;
|
|
|
|
|
standardItem->setTextAlignment(Qt::AlignCenter);
|
|
|
|
|
QFont font = QFont("Microsoft YaHei UI", 12);
|
|
|
|
|
protoItem->setFont(font);
|
|
|
|
|
standardItem->setFont(font);
|
|
|
|
|
setDeviceDetail(ui->deviceDetail);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RescueLoadWidget::setTable(QTableView* tableView) {
|
|
|
|
|
// 设置样式表
|
|
|
|
|
tableView->setStyleSheet(
|
|
|
|
|
"QTableView::item {"
|
|
|
|
|
" background-color: transparent;"
|
|
|
|
|
" color: white;"
|
|
|
|
|
"}"
|
|
|
|
|
);
|
|
|
|
|
QFont font = QFont("Microsoft YaHei UI", 12);
|
|
|
|
|
|
|
|
|
|
tableView->horizontalHeader()->setFont(font);
|
|
|
|
|
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // 可拉伸填充
|
|
|
|
|
tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter); //表头信息显示居中
|
|
|
|
|
tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);//对第2列单独设置固定宽度
|
|
|
|
|
tableView->setColumnWidth(1, 100);//设置固定宽度
|
|
|
|
|
|
|
|
|
|
tableView->verticalHeader()->setFont(font);
|
|
|
|
|
tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
|
|
|
|
tableView->verticalHeader()->setDefaultAlignment(Qt::AlignCenter);
|
|
|
|
|
tableView->verticalHeader()->setFixedWidth(35); // 行表头(序号)设置固定宽度
|
|
|
|
|
// tableWidget->verticalHeader()->hide(); //将默认序号隐藏
|
|
|
|
|
tableView->setShowGrid(true);
|
|
|
|
|
tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不可编辑
|
|
|
|
|
|
|
|
|
|
// 选中行更新详细信息
|
|
|
|
|
tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // 选中行
|
|
|
|
|
|
|
|
|
|
// 单击后选中并显示详情,再次点击取消选中
|
|
|
|
|
connect(tableView, &QTableView::clicked, this, [=](QModelIndex index) {
|
|
|
|
|
static int preRow = -1; // 记录前一个点击事件所在行
|
|
|
|
|
int curRow = index.row();
|
|
|
|
|
QModelIndex imsiIndex = index.model()->index(curRow, 0); // note 第一列必须为imsi值
|
|
|
|
|
QString imsi =tableView->model()->data(imsiIndex).toString();
|
|
|
|
|
QItemSelectionModel* selectModel = tableView->selectionModel();
|
|
|
|
|
if (curRow == preRow) {
|
|
|
|
|
// 取消选中状态
|
|
|
|
|
if (selectModel->isSelected(index)) {
|
|
|
|
|
selectModel->select(index, QItemSelectionModel::Deselect | QItemSelectionModel::Rows);
|
|
|
|
|
preRow = -1; //取消后重置,否则无法选中
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
selectModel->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
|
|
|
|
preRow = curRow;
|
|
|
|
|
|
|
|
|
|
if (tableView == ui->deviceview){
|
|
|
|
|
updateDetailTable(imsi);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 选中行更新名单编辑区
|
|
|
|
|
connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged, tableView, [=]() {
|
|
|
|
|
QModelIndexList selectedIndexes = tableView->selectionModel()->selectedIndexes();
|
|
|
|
|
QAbstractItemModel* model = tableView->model();
|
|
|
|
|
ui->namelistEdit->clear();
|
|
|
|
|
|
|
|
|
|
QString selectedText;
|
|
|
|
|
for (const QModelIndex &index : selectedIndexes) {
|
|
|
|
|
if (index.column() == 0) {
|
|
|
|
|
selectedText = model->data(index).toString();
|
|
|
|
|
ui->namelistEdit->appendPlainText(selectedText.trimmed());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ui->namelistEdit->show();
|
|
|
|
|
// qDebug() << "选择状态变更: selcted: " << selectedIndexes;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void RescueLoadWidget::initialModel()
|
|
|
|
|
{
|
|
|
|
|
QStringList headers = QStringList()<<"IMSI"<<"运营商";
|
|
|
|
|
wnamelistModel = new QStandardItemModel(this);
|
|
|
|
|
wnamelistModel->setHorizontalHeaderLabels(headers);
|
|
|
|
|
wnamelistModel->setColumnCount(2);
|
|
|
|
|
ui->wnameview->setModel(wnamelistModel);
|
|
|
|
|
|
|
|
|
|
bnamelistModel = new QStandardItemModel(this);
|
|
|
|
|
bnamelistModel->setHorizontalHeaderLabels(headers);
|
|
|
|
|
bnamelistModel->setColumnCount(2);
|
|
|
|
|
ui->bnameview->setModel(bnamelistModel);
|
|
|
|
|
|
|
|
|
|
imsiDataModel = new ImsiTableModel(this);
|
|
|
|
|
ui->deviceview->setModel(imsiDataModel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void RescueLoadWidget::setDeviceDetail(QTableWidget* deviceDetail) {
|
|
|
|
|
// 设备详情表样式
|
|
|
|
|
deviceDetail->setStyleSheet(
|
|
|
|
|
"QTableWidget::item {"
|
|
|
|
|
" background-color: transparent;"
|
|
|
|
|
" color: white;"
|
|
|
|
|
"}"
|
|
|
|
|
);
|
|
|
|
|
deviceDetail->verticalHeader()->setFont(QFont("Microsoft YaHei UI", 12));
|
|
|
|
|
deviceDetail->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
|
|
|
|
deviceDetail->verticalHeader()->setDefaultAlignment(Qt::AlignCenter);
|
|
|
|
|
deviceDetail->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不可编辑
|
|
|
|
|
deviceDetail->setShowGrid(true);
|
|
|
|
|
// 初始化
|
|
|
|
|
for (int i=0; i<deviceDetail->rowCount(); i++) {
|
|
|
|
|
QTableWidgetItem *item = protoItem->clone();
|
|
|
|
|
deviceDetail->setItem(i, 0, item);
|
|
|
|
|
}
|
|
|
|
|
deviceDetail->show();
|
|
|
|
|
qDebug() << "设备详细信息初始化完成";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新指定imsi的详细信息
|
|
|
|
|
void RescueLoadWidget::updateDetailTable(const QString imsi)
|
|
|
|
|
{
|
|
|
|
|
const QHash<QString, ImsiData>& dataMap = imsiDataModel->getDataMap();
|
|
|
|
|
if (!dataMap.contains(imsi)) return;
|
|
|
|
|
ImsiData data = imsiDataModel->getDataMap().value(imsi);
|
|
|
|
|
// qDebug()<<"更新详细信息start: " << imsi;
|
|
|
|
|
emit imsiDataModel->detailUpdated(imsi, data.longitude.toDouble(), data.latitude.toDouble());
|
|
|
|
|
ui->deviceDetail->item(0, 0)->setText(data.imsi);
|
|
|
|
|
ui->deviceDetail->item(1, 0)->setText(data.operatorName);
|
|
|
|
|
ui->deviceDetail->item(2, 0)->setText(data.longitude);
|
|
|
|
|
ui->deviceDetail->item(3, 0)->setText(data.latitude);
|
|
|
|
|
// ui->deviceDetail->item(4, 0)->setText(QString::number(data.altitude));
|
|
|
|
|
ui->deviceDetail->item(4, 0)->setText(data.fcn);
|
|
|
|
|
ui->deviceDetail->item(5, 0)->setText(QString::number(data.rssi));
|
|
|
|
|
ui->deviceDetail->item(6, 0)->setText(data.createDate);
|
|
|
|
|
ui->deviceDetail->show();
|
|
|
|
|
|
|
|
|
|
// qDebug()<<"更新详细信息end: " << imsi;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_start4G_clicked()
|
|
|
|
|
{
|
|
|
|
|
// 开启4g功放
|
|
|
|
|
rescueLoad->sendMessage(QString("{\"cmd\": \"startCell\"}"));
|
|
|
|
|
rescueLoad->sendMessage(QString("{\"cmd\": \"heartbeat\"}"));
|
|
|
|
|
// 时间同步
|
|
|
|
|
rescueLoad->syncTime();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_stop4G_clicked()
|
|
|
|
|
{
|
|
|
|
|
rescueLoad->sendMessage(QString("{\"cmd\": \"stopCell\"}"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_start5G_clicked()
|
|
|
|
|
{
|
|
|
|
|
// 开启5g功放
|
|
|
|
|
rescueLoad->sendMessage(QString("{\"cmd\": \"startCell_5g\"}"));
|
|
|
|
|
rescueLoad->sendMessage(QString("{\"cmd\": \"heartbeat\"}"));
|
|
|
|
|
// 时间同步
|
|
|
|
|
rescueLoad->syncTime();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_stop5G_clicked()
|
|
|
|
|
{
|
|
|
|
|
rescueLoad->sendMessage(QString("{\"cmd\": \"stopCell_5g\"}"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_addblistBtn_clicked()
|
|
|
|
|
{
|
|
|
|
|
QList<QString> namelistToSend = getNameListEditList(); // 输入量,用于更新namelist
|
|
|
|
|
QString type = ui->namelistConfigType->currentText().toUpper();
|
|
|
|
|
QString sendType;
|
|
|
|
|
if (type.startsWith("ADD")) {
|
|
|
|
|
sendType = "ADD";
|
|
|
|
|
} else if (type.startsWith("SUB")) {
|
|
|
|
|
sendType = "SUB";
|
|
|
|
|
} else if (type.startsWith("SAVE")) {
|
|
|
|
|
sendType = "SAVE";
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
rescueLoad->sendNameList("block", sendType, QList<QString>(), namelistToSend);
|
|
|
|
|
updateWBnamelist("B", sendType, namelistToSend);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_addwlistBtn_clicked()
|
|
|
|
|
{
|
|
|
|
|
QList<QString> namelistToSend = getNameListEditList(); // 输入量,用于更新namelist
|
|
|
|
|
QString type = ui->namelistConfigType->currentText().toUpper();
|
|
|
|
|
QString sendType;
|
|
|
|
|
if (type.startsWith("ADD")) {
|
|
|
|
|
sendType = "ADD";
|
|
|
|
|
} else if (type.startsWith("SUB")) {
|
|
|
|
|
sendType = "SUB";
|
|
|
|
|
} else if (type.startsWith("SAVE")) {
|
|
|
|
|
sendType = "SAVE";
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
rescueLoad->sendNameList("block", sendType, namelistToSend, QList<QString>());
|
|
|
|
|
updateWBnamelist("W", sendType, namelistToSend);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 获取编辑区的imsi列表*/
|
|
|
|
|
QList<QString> RescueLoadWidget::getNameListEditList(){
|
|
|
|
|
|
|
|
|
|
QTextBlock block = ui->namelistEdit->document()->firstBlock();
|
|
|
|
|
QList<QString> namelist;
|
|
|
|
|
while (block.isValid()) {
|
|
|
|
|
QString tmp = block.text().trimmed();
|
|
|
|
|
if (! tmp.isEmpty()) {
|
|
|
|
|
namelist.append(tmp);
|
|
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
return namelist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新黑名单和白名单的数据与显示
|
|
|
|
|
void RescueLoadWidget::updateWBnamelist(QString listType, QString sendType, QList<QString> &namelistToUpdate)
|
|
|
|
|
{
|
|
|
|
|
QStandardItemModel *namelistModel = listType=="W"? wnamelistModel: bnamelistModel;
|
|
|
|
|
QSet<QString> &namelistToShow = listType=="W"? wnamelist : bnamelist;
|
|
|
|
|
QTableView *namelistView= listType=="W"? ui->wnameview : ui->bnameview;
|
|
|
|
|
|
|
|
|
|
// 更新黑/白名单列表
|
|
|
|
|
if (sendType == "ADD") { // 新增
|
|
|
|
|
for(const QString &imsi: namelistToUpdate) {
|
|
|
|
|
namelistToShow.insert(imsi);
|
|
|
|
|
}
|
|
|
|
|
} else if (sendType == "SUB") { // 删除
|
|
|
|
|
for(const QString &imsi: namelistToUpdate) {
|
|
|
|
|
namelistToShow.remove(imsi);
|
|
|
|
|
}
|
|
|
|
|
} else if (sendType == "SAVE") { // 替换现有在列表
|
|
|
|
|
namelistToShow.clear();
|
|
|
|
|
for(const QString &imsi: namelistToUpdate) {
|
|
|
|
|
namelistToShow.insert(imsi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 列表显示更新
|
|
|
|
|
namelistModel->clear();
|
|
|
|
|
namelistModel->setColumnCount(2);
|
|
|
|
|
namelistModel->setRowCount(namelistToShow.size());
|
|
|
|
|
namelistModel->setHorizontalHeaderLabels(QStringList()<<"IMSI"<<"运营商");
|
|
|
|
|
namelistView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);//对第2列单独设置固定宽度
|
|
|
|
|
namelistView->setColumnWidth(1, 100);//设置固定宽度
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (const QString &imsi : namelistToShow) {
|
|
|
|
|
QStandardItem *item1 =standardItem->clone();
|
|
|
|
|
QStandardItem *item2 =standardItem->clone();
|
|
|
|
|
item1->setText(imsi);
|
|
|
|
|
item2->setText(ImsiData::getOperatorNameByImsi(imsi));
|
|
|
|
|
namelistModel->setItem(i, 0, item1);
|
|
|
|
|
namelistModel->setItem(i, 1, item2);
|
|
|
|
|
i ++ ;
|
|
|
|
|
}
|
|
|
|
|
// qDebug() << "[搜救] 名单数据更新end"<< namelistModel->rowCount();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_ipportBtn_clicked()
|
|
|
|
|
{
|
|
|
|
|
QString newIp = ui->ipEdit->text().trimmed();
|
|
|
|
|
int newPort = ui->portEdit->text().trimmed().toInt();
|
|
|
|
|
rescueLoad->remoteIp = newIp;
|
|
|
|
|
rescueLoad->remotePort = newPort;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_saveFileButton_clicked()
|
|
|
|
|
{
|
|
|
|
|
QString timeStr = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss");
|
|
|
|
|
QString defaultPath = QString("%1/搜救信息%2.csv").arg(imsiDataModel->getSaveDir(), timeStr);
|
|
|
|
|
QString filePath = QFileDialog::getSaveFileName(this, tr("保存文件"), defaultPath, tr("CSV 文件 (*.csv)"));
|
|
|
|
|
qDebug()<< "[搜救] 文件保存"<< filePath;
|
|
|
|
|
if (!filePath.isEmpty()) {
|
|
|
|
|
imsiDataModel->saveDataListToCsv(filePath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slots void RescueLoadWidget::on_clearBtn_clicked()
|
|
|
|
|
{
|
|
|
|
|
imsiDataModel->clearData();
|
|
|
|
|
}
|