|
|
|
|
@ -3,18 +3,12 @@
|
|
|
|
|
#include "mappage.h" // For PathPlanningDialog and MapPage
|
|
|
|
|
#include <QFile> // Added for file operations
|
|
|
|
|
#include <QDateTime> // Added for saving paths
|
|
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QInputDialog>
|
|
|
|
|
#include <QDialog>
|
|
|
|
|
#include <QFormLayout>
|
|
|
|
|
#include <QDialogButtonBox>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QSpinBox>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QTextEdit>
|
|
|
|
|
#include <QGroupBox>
|
|
|
|
|
#include <QCheckBox>
|
|
|
|
|
#include <QJsonDocument> // Added for JSON formatting
|
|
|
|
|
#include <QJsonObject> // Added for QJsonObject
|
|
|
|
|
#include <QJsonArray> // Added for QJsonArray
|
|
|
|
|
#include <QDialogButtonBox> // Added for dialog buttons
|
|
|
|
|
#include <QDir> // Added for QDir
|
|
|
|
|
#include <cmath> // Added for math functions (sin, cos, sqrt, atan2, M_PI)
|
|
|
|
|
|
|
|
|
|
TaskDecisionPage::TaskDecisionPage(QWidget* parent)
|
|
|
|
|
: QWidget(parent)
|
|
|
|
|
@ -38,19 +32,33 @@ TaskDecisionPage::TaskDecisionPage(QWidget* parent)
|
|
|
|
|
connect(targetManager_, &TargetManager::targetStatusChanged, this, &TaskDecisionPage::updateTargetTable);
|
|
|
|
|
connect(targetManager_, &TargetManager::targetPriorityChanged, this, &TaskDecisionPage::updateTargetTable);
|
|
|
|
|
connect(targetManager_, &TargetManager::targetStrikeMethodChanged, this, &TaskDecisionPage::updateTargetTable);
|
|
|
|
|
|
|
|
|
|
taskData_ = new TaskDataObject();
|
|
|
|
|
// Remove: QMessageBox and initTaskData
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::setupUI() {
|
|
|
|
|
auto* mainLayout = new QHBoxLayout(this);
|
|
|
|
|
auto* mainLayout = new QVBoxLayout(this); // Change to VBox for stacking
|
|
|
|
|
mainLayout->setContentsMargins(10, 10, 10, 10);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stack_ = new QStackedWidget();
|
|
|
|
|
mainLayout->addWidget(stack_);
|
|
|
|
|
|
|
|
|
|
// Page 0: Welcome page with new task button
|
|
|
|
|
stack_->addWidget(createWelcomeWidget());
|
|
|
|
|
|
|
|
|
|
// Page 1: Main decision UI
|
|
|
|
|
QWidget* decisionWidget = new QWidget();
|
|
|
|
|
auto* decisionLayout = new QHBoxLayout(decisionWidget);
|
|
|
|
|
|
|
|
|
|
// Add existing UI to decisionLayout
|
|
|
|
|
// 左侧步骤导航
|
|
|
|
|
stepList_ = new QListWidget();
|
|
|
|
|
stepList_->setFixedWidth(200);
|
|
|
|
|
stepList_->addItems({"1. 目标位置确认", "2. 路径规划", "3. 区域搜索策略", "4. 打击目标清单", "5. 任务预览与生成"});
|
|
|
|
|
connect(stepList_, &QListWidget::currentRowChanged, this, &TaskDecisionPage::onStepChanged);
|
|
|
|
|
mainLayout->addWidget(stepList_);
|
|
|
|
|
|
|
|
|
|
decisionLayout->addWidget(stepList_);
|
|
|
|
|
|
|
|
|
|
// 中间内容区
|
|
|
|
|
auto* contentLayout = new QVBoxLayout();
|
|
|
|
|
contentStack_ = new QStackedWidget();
|
|
|
|
|
@ -60,7 +68,7 @@ void TaskDecisionPage::setupUI() {
|
|
|
|
|
contentStack_->addWidget(createStep4Widget());
|
|
|
|
|
contentStack_->addWidget(createStep5Widget());
|
|
|
|
|
contentLayout->addWidget(contentStack_, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 底部导航
|
|
|
|
|
auto* bottomLayout = new QHBoxLayout();
|
|
|
|
|
prevBtn_ = new QPushButton("上一步");
|
|
|
|
|
@ -75,10 +83,12 @@ void TaskDecisionPage::setupUI() {
|
|
|
|
|
bottomLayout->addStretch(1);
|
|
|
|
|
bottomLayout->addWidget(generateBtn_);
|
|
|
|
|
contentLayout->addLayout(bottomLayout);
|
|
|
|
|
|
|
|
|
|
mainLayout->addLayout(contentLayout, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
decisionLayout->addLayout(contentLayout, 1);
|
|
|
|
|
stack_->addWidget(decisionWidget);
|
|
|
|
|
|
|
|
|
|
// 初始化
|
|
|
|
|
stack_->setCurrentIndex(0); // Start with welcome page
|
|
|
|
|
onStepChanged(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -134,16 +144,13 @@ QWidget* TaskDecisionPage::createStep2Widget() {
|
|
|
|
|
MapPage* previewMap = new MapPage();
|
|
|
|
|
previewMap->setWindowTitle("路径预览");
|
|
|
|
|
previewMap->resize(800, 600);
|
|
|
|
|
|
|
|
|
|
// 等待页面加载完成后可视化路径
|
|
|
|
|
connect(previewMap->getMapView()->page(), &QWebEnginePage::loadFinished, [previewMap, this](bool ok){
|
|
|
|
|
if (ok) {
|
|
|
|
|
previewMap->visualizePath(pathData_);
|
|
|
|
|
previewMap->visualizePath(pathData_, true); // 清除旧路径
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::warning(this, "错误", "地图加载失败,无法预览路径");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
previewMap->show();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
@ -170,7 +177,7 @@ QWidget* TaskDecisionPage::createStep2Widget() {
|
|
|
|
|
histPreview->setWindowTitle("历史路径预览");
|
|
|
|
|
histPreview->resize(800, 600);
|
|
|
|
|
connect(histPreview->getMapView()->page(), &QWebEnginePage::loadFinished, [histPreview, histPath](bool ok){
|
|
|
|
|
if (ok) histPreview->visualizePath(histPath);
|
|
|
|
|
if (ok) histPreview->visualizePath(histPath, true);
|
|
|
|
|
});
|
|
|
|
|
histPreview->show();
|
|
|
|
|
}
|
|
|
|
|
@ -185,26 +192,22 @@ QWidget* TaskDecisionPage::createStep3Widget() {
|
|
|
|
|
|
|
|
|
|
layout->addWidget(new QLabel("步骤3: 规划区域搜索策略"));
|
|
|
|
|
|
|
|
|
|
areaSize_ = new QLineEdit();
|
|
|
|
|
areaSize_->setPlaceholderText("区域大小/形状 (e.g., 圆形,半径5km)");
|
|
|
|
|
layout->addWidget(areaSize_);
|
|
|
|
|
|
|
|
|
|
droneCount_ = new QSpinBox();
|
|
|
|
|
droneCount_->setRange(1, 10);
|
|
|
|
|
layout->addWidget(droneCount_);
|
|
|
|
|
|
|
|
|
|
auto* recBtn = new QPushButton("推荐无人机数量");
|
|
|
|
|
connect(recBtn, &QPushButton::clicked, this, &TaskDecisionPage::onRecommendDrones);
|
|
|
|
|
layout->addWidget(recBtn);
|
|
|
|
|
|
|
|
|
|
auto* replanBtn = new QPushButton("重新规划");
|
|
|
|
|
connect(replanBtn, &QPushButton::clicked, this, &TaskDecisionPage::onReplanSearch);
|
|
|
|
|
layout->addWidget(replanBtn);
|
|
|
|
|
// 新增区域覆盖规划按钮
|
|
|
|
|
auto* areaCoverageBtn = new QPushButton("区域覆盖规划");
|
|
|
|
|
areaCoverageBtn->setProperty("primary", true);
|
|
|
|
|
connect(areaCoverageBtn, &QPushButton::clicked, this, &TaskDecisionPage::onAreaCoveragePlanning);
|
|
|
|
|
layout->addWidget(areaCoverageBtn);
|
|
|
|
|
|
|
|
|
|
searchTable_ = new QTableWidget(0, 4);
|
|
|
|
|
searchTable_->setHorizontalHeaderLabels({"无人机ID", "搜索路径", "时间", "范围占比"});
|
|
|
|
|
searchTable_->setHorizontalHeaderLabels({"无人机ID", "搜索路径", "时间", "覆盖率"});
|
|
|
|
|
layout->addWidget(searchTable_);
|
|
|
|
|
|
|
|
|
|
// 新增保存数据按钮
|
|
|
|
|
auto* saveDataBtn = new QPushButton("保存数据");
|
|
|
|
|
saveDataBtn->setProperty("secondary", true);
|
|
|
|
|
connect(saveDataBtn, &QPushButton::clicked, this, &TaskDecisionPage::onSaveSearchData);
|
|
|
|
|
layout->addWidget(saveDataBtn);
|
|
|
|
|
|
|
|
|
|
searchTime_ = new QLabel("预计搜索时间: N/A");
|
|
|
|
|
coverage_ = new QLabel("覆盖范围: N/A");
|
|
|
|
|
layout->addWidget(searchTime_);
|
|
|
|
|
@ -286,6 +289,7 @@ void TaskDecisionPage::onStepChanged(int step) {
|
|
|
|
|
prevBtn_->setEnabled(step > 0);
|
|
|
|
|
nextBtn_->setEnabled(step < 4);
|
|
|
|
|
generateBtn_->setEnabled(step == 4);
|
|
|
|
|
generateBtn_->setVisible(step == 4); // Show only in last step
|
|
|
|
|
|
|
|
|
|
// 调试信息
|
|
|
|
|
qDebug() << "Step changed to:" << step << "Stack index:" << contentStack_->currentIndex();
|
|
|
|
|
@ -342,25 +346,191 @@ void TaskDecisionPage::onGeneratePath() {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onRecommendDrones() {
|
|
|
|
|
// 模拟推荐(基于区域大小)
|
|
|
|
|
droneCount_->setValue(3); // 示例
|
|
|
|
|
QMessageBox::information(this, "推荐", "推荐使用3架无人机");
|
|
|
|
|
void TaskDecisionPage::onAreaCoveragePlanning() {
|
|
|
|
|
// 创建独立的地图画布窗口
|
|
|
|
|
MapPage* coverageMap = new MapPage(this);
|
|
|
|
|
coverageMap->setWindowTitle("区域覆盖规划 - 任务决策");
|
|
|
|
|
coverageMap->resize(1200, 800);
|
|
|
|
|
|
|
|
|
|
// 设置威胁区域按钮文本为"设置搜索区域"
|
|
|
|
|
coverageMap->setThreatButtonText("设置搜索区域");
|
|
|
|
|
|
|
|
|
|
coverageMap->show();
|
|
|
|
|
|
|
|
|
|
// 等待地图加载完成后打开区域覆盖规划对话框
|
|
|
|
|
connect(coverageMap->getMapView()->page(), &QWebEnginePage::loadFinished, [this, coverageMap](bool ok) {
|
|
|
|
|
if (ok) {
|
|
|
|
|
// 创建区域覆盖规划对话框
|
|
|
|
|
AreaCoverageDialog* coverageDialog = new AreaCoverageDialog(this, coverageMap, "区域覆盖规划 - 任务决策");
|
|
|
|
|
coverageDialog->show();
|
|
|
|
|
|
|
|
|
|
// 连接对话框关闭信号,清理资源
|
|
|
|
|
connect(coverageDialog, &QDialog::finished, [this, coverageMap, coverageDialog](int result) {
|
|
|
|
|
if (result == QDialog::Accepted) {
|
|
|
|
|
// 获取覆盖路径数据
|
|
|
|
|
QString coveragePathData = coverageDialog->getCoveragePathData();
|
|
|
|
|
if (!coveragePathData.isEmpty()) {
|
|
|
|
|
// 更新搜索数据
|
|
|
|
|
searchData_ = coveragePathData;
|
|
|
|
|
|
|
|
|
|
// 解析覆盖路径数据并更新搜索表格
|
|
|
|
|
updateSearchTableFromCoverageData(coveragePathData);
|
|
|
|
|
|
|
|
|
|
QMessageBox::information(coverageDialog, "成功", "区域覆盖规划已完成!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
coverageMap->close();
|
|
|
|
|
coverageMap->deleteLater();
|
|
|
|
|
coverageDialog->deleteLater();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onSaveSearchData() {
|
|
|
|
|
// 检查是否有搜索数据
|
|
|
|
|
if (searchData_.isEmpty()) {
|
|
|
|
|
QMessageBox::warning(this, "警告", "没有可保存的搜索数据!请先进行区域覆盖规划。");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建data_area目录(相对于项目根目录)
|
|
|
|
|
QString projectRoot = QDir::currentPath();
|
|
|
|
|
QDir dataDir(projectRoot + "/Src/data_area");
|
|
|
|
|
if (!dataDir.exists()) {
|
|
|
|
|
if (!dataDir.mkpath(".")) {
|
|
|
|
|
QMessageBox::critical(this, "错误", "无法创建data_area目录!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成文件名(包含时间戳)
|
|
|
|
|
QDateTime currentTime = QDateTime::currentDateTime();
|
|
|
|
|
QString fileName = QString("search_data_%1.json").arg(currentTime.toString("yyyyMMdd_hhmmss"));
|
|
|
|
|
QString filePath = dataDir.absoluteFilePath(fileName);
|
|
|
|
|
|
|
|
|
|
// 准备保存的数据
|
|
|
|
|
QJsonObject searchDataObj;
|
|
|
|
|
searchDataObj["timestamp"] = currentTime.toString("yyyy-MM-dd hh:mm:ss");
|
|
|
|
|
searchDataObj["coverage"] = coverage_->text();
|
|
|
|
|
searchDataObj["searchTime"] = searchTime_->text();
|
|
|
|
|
|
|
|
|
|
// 添加表格数据
|
|
|
|
|
QJsonArray tableData;
|
|
|
|
|
for (int i = 0; i < searchTable_->rowCount(); ++i) {
|
|
|
|
|
QJsonObject rowData;
|
|
|
|
|
rowData["droneId"] = searchTable_->item(i, 0) ? searchTable_->item(i, 0)->text() : "";
|
|
|
|
|
rowData["path"] = searchTable_->item(i, 1) ? searchTable_->item(i, 1)->text() : "";
|
|
|
|
|
rowData["time"] = searchTable_->item(i, 2) ? searchTable_->item(i, 2)->text() : "";
|
|
|
|
|
rowData["coverage"] = searchTable_->item(i, 3) ? searchTable_->item(i, 3)->text() : "";
|
|
|
|
|
tableData.append(rowData);
|
|
|
|
|
}
|
|
|
|
|
searchDataObj["droneCount"] = tableData.size(); // Use table data size as drone count
|
|
|
|
|
searchDataObj["tableData"] = tableData;
|
|
|
|
|
|
|
|
|
|
// 保存到文件
|
|
|
|
|
QJsonDocument doc(searchDataObj);
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
|
|
|
file.write(doc.toJson());
|
|
|
|
|
file.close();
|
|
|
|
|
QMessageBox::information(this, "成功", QString("搜索数据已保存到:\n%1").arg(filePath));
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::critical(this, "错误", "无法保存文件!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onReplanSearch() {
|
|
|
|
|
// 模拟规划
|
|
|
|
|
searchTable_->setRowCount(droneCount_->value());
|
|
|
|
|
for (int i = 0; i < droneCount_->value(); ++i) {
|
|
|
|
|
void TaskDecisionPage::updateSearchTableFromCoverageData(const QString& coveragePathData) {
|
|
|
|
|
// 解析JSON格式的覆盖路径数据
|
|
|
|
|
QJsonParseError error;
|
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(coveragePathData.toUtf8(), &error);
|
|
|
|
|
|
|
|
|
|
if (error.error != QJsonParseError::NoError) {
|
|
|
|
|
qDebug() << "JSON解析错误:" << error.errorString();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonArray droneArray = doc.array();
|
|
|
|
|
int droneCount = droneArray.size();
|
|
|
|
|
|
|
|
|
|
// 更新搜索表格
|
|
|
|
|
if (searchTable_ != nullptr) {
|
|
|
|
|
searchTable_->setRowCount(droneCount);
|
|
|
|
|
} else {
|
|
|
|
|
qDebug() << "searchTable_ is null, cannot update.";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double totalDistance = 0.0;
|
|
|
|
|
double totalCoverageArea = 0.0;
|
|
|
|
|
double maxFlightTime = 0.0;
|
|
|
|
|
QList<double> droneCoverageAreas; // 存储每台无人机的覆盖面积
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < droneCount; ++i) {
|
|
|
|
|
QJsonObject droneObj = droneArray[i].toObject();
|
|
|
|
|
QJsonArray pathArray = droneObj["path"].toArray();
|
|
|
|
|
|
|
|
|
|
// 无人机ID
|
|
|
|
|
searchTable_->setItem(i, 0, new QTableWidgetItem(QString("Drone %1").arg(i+1)));
|
|
|
|
|
searchTable_->setItem(i, 1, new QTableWidgetItem("路径描述"));
|
|
|
|
|
searchTable_->setItem(i, 2, new QTableWidgetItem("5min"));
|
|
|
|
|
searchTable_->setItem(i, 3, new QTableWidgetItem("30%"));
|
|
|
|
|
|
|
|
|
|
// 搜索路径描述
|
|
|
|
|
QString pathDesc = QString("覆盖路径 (%1个点)").arg(pathArray.size());
|
|
|
|
|
searchTable_->setItem(i, 1, new QTableWidgetItem(pathDesc));
|
|
|
|
|
|
|
|
|
|
// 计算路径长度和飞行时间
|
|
|
|
|
double pathDistance = 0.0;
|
|
|
|
|
for (int j = 1; j < pathArray.size(); ++j) {
|
|
|
|
|
QJsonArray point1 = pathArray[j-1].toArray();
|
|
|
|
|
QJsonArray point2 = pathArray[j].toArray();
|
|
|
|
|
|
|
|
|
|
double lng1 = point1[0].toDouble();
|
|
|
|
|
double lat1 = point1[1].toDouble();
|
|
|
|
|
double lng2 = point2[0].toDouble();
|
|
|
|
|
double lat2 = point2[1].toDouble();
|
|
|
|
|
|
|
|
|
|
// 使用Haversine公式计算距离
|
|
|
|
|
double dLat = (lat2 - lat1) * M_PI / 180.0;
|
|
|
|
|
double dLng = (lng2 - lng1) * M_PI / 180.0;
|
|
|
|
|
double a = sin(dLat/2) * sin(dLat/2) + cos(lat1 * M_PI / 180.0) * cos(lat2 * M_PI / 180.0) * sin(dLng/2) * sin(dLng/2);
|
|
|
|
|
double c = 2 * atan2(sqrt(a), sqrt(1-a));
|
|
|
|
|
pathDistance += 6371000.0 * c; // 地球半径米
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalDistance += pathDistance;
|
|
|
|
|
|
|
|
|
|
// 假设飞行速度为15m/s
|
|
|
|
|
double flightTime = pathDistance / 15.0 / 60.0; // 转换为分钟
|
|
|
|
|
if (flightTime > maxFlightTime) maxFlightTime = flightTime;
|
|
|
|
|
|
|
|
|
|
searchTable_->setItem(i, 2, new QTableWidgetItem(QString("%1min").arg(QString::number(flightTime, 'f', 1))));
|
|
|
|
|
|
|
|
|
|
// 计算每台无人机的覆盖面积
|
|
|
|
|
double radius = droneObj["radius"].toDouble();
|
|
|
|
|
double droneCoverageArea = pathArray.size() * M_PI * radius * radius;
|
|
|
|
|
totalCoverageArea += droneCoverageArea;
|
|
|
|
|
|
|
|
|
|
// 存储每台无人机的覆盖面积,用于后续计算相对覆盖率
|
|
|
|
|
droneCoverageAreas.append(droneCoverageArea);
|
|
|
|
|
}
|
|
|
|
|
searchTime_->setText("预计搜索时间: 15min");
|
|
|
|
|
coverage_->setText("覆盖范围: 90%");
|
|
|
|
|
searchData_ = "搜索策略生成";
|
|
|
|
|
QMessageBox::information(this, "规划", "搜索策略已重新规划!");
|
|
|
|
|
|
|
|
|
|
// 重新计算每台无人机的相对覆盖率,使总和为100%
|
|
|
|
|
for (int i = 0; i < droneCount; ++i) {
|
|
|
|
|
double relativeCoveragePercent = (droneCoverageAreas[i] / totalCoverageArea) * 100.0;
|
|
|
|
|
searchTable_->setItem(i, 3, new QTableWidgetItem(QString("%1%").arg(QString::number(relativeCoveragePercent, 'f', 1))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算总覆盖率并更新统计信息
|
|
|
|
|
double totalCoveragePercent = 100.0;
|
|
|
|
|
double overlapAdjustment = 0.0; // 简单重叠调整,例如假设10%重叠
|
|
|
|
|
for (int i = 0; i < droneCount; ++i) {
|
|
|
|
|
QString coverageStr = droneArray[i].toObject()["coverage"].toString();
|
|
|
|
|
double droneCoverage = coverageStr.remove("%").toDouble();
|
|
|
|
|
totalCoveragePercent += droneCoverage;
|
|
|
|
|
if (i > 0) overlapAdjustment += droneCoverage * 0.1; // 假设每对无人机重叠10%
|
|
|
|
|
}
|
|
|
|
|
totalCoveragePercent -= overlapAdjustment;
|
|
|
|
|
totalCoveragePercent = std::max(0.0, std::min(100.0, totalCoveragePercent));
|
|
|
|
|
searchTime_->setText(QString("预计搜索时间: %1min").arg(QString::number(maxFlightTime, 'f', 1)));
|
|
|
|
|
coverage_->setText(QString("覆盖范围: %1%").arg(QString::number(totalCoveragePercent, 'f', 1)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onAddTargetType() {
|
|
|
|
|
@ -369,36 +539,40 @@ void TaskDecisionPage::onAddTargetType() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onGenerateTask() {
|
|
|
|
|
// 汇总数据生成预览
|
|
|
|
|
// Validation
|
|
|
|
|
if (pathData_.isEmpty() && searchData_.isEmpty() && targetTable_->rowCount() == 0) {
|
|
|
|
|
QMessageBox::warning(this, "警告", "所有数据为空,无法生成任务包。请完成至少一个步骤。");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 汇总数据生成预览(模拟)
|
|
|
|
|
QString preview = "任务摘要:\n";
|
|
|
|
|
preview += QString("目标明确: %1\n").arg(isTargetClear_ ? "是" : "否");
|
|
|
|
|
preview += "路径: " + pathData_ + "\n";
|
|
|
|
|
if (!isTargetClear_) preview += "搜索策略: " + searchData_ + "\n";
|
|
|
|
|
|
|
|
|
|
preview += "打击清单:\n";
|
|
|
|
|
auto targets = targetManager_->getTargetsSortedByPriority();
|
|
|
|
|
for (auto* target : targets) {
|
|
|
|
|
preview += QString("- %1 (%2) - 优先级: %3, 打击方式: %4, 状态: %5\n")
|
|
|
|
|
.arg(target->getName())
|
|
|
|
|
.arg(target->getTypeString())
|
|
|
|
|
.arg(target->getPriority())
|
|
|
|
|
.arg(target->getStrikeMethodString())
|
|
|
|
|
.arg(target->getStatusString());
|
|
|
|
|
for (int i = 0; i < targetTable_->rowCount(); ++i) {
|
|
|
|
|
auto* type = qobject_cast<QComboBox*>(targetTable_->cellWidget(i, 0));
|
|
|
|
|
auto* pri = qobject_cast<QSpinBox*>(targetTable_->cellWidget(i, 1));
|
|
|
|
|
auto* way = qobject_cast<QComboBox*>(targetTable_->cellWidget(i, 2));
|
|
|
|
|
preview += QString("%1 (优先级 %2, 方式 %3)\n").arg(type->currentText()).arg(pri->value()).arg(way->currentText());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
preview += QString("\n统计信息:\n");
|
|
|
|
|
preview += QString("目标总数: %1\n").arg(targetManager_->getTargetCount());
|
|
|
|
|
preview += QString("高优先级目标: %1\n").arg(targetManager_->getHighPriorityTargets().size());
|
|
|
|
|
|
|
|
|
|
taskPreview_->setText(preview);
|
|
|
|
|
|
|
|
|
|
// 生成任务包(模拟保存JSON)
|
|
|
|
|
QFile file("task_path.json");
|
|
|
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
|
|
|
file.write(pathData_.toUtf8());
|
|
|
|
|
file.close();
|
|
|
|
|
// 生成任务包(保存到data_paths目录)
|
|
|
|
|
if (!pathData_.isEmpty()) {
|
|
|
|
|
QDir dataDir;
|
|
|
|
|
if (!dataDir.exists("Src/data_paths")) {
|
|
|
|
|
dataDir.mkdir("Src/data_paths");
|
|
|
|
|
}
|
|
|
|
|
QFile file("Src/data_paths/task_path.json");
|
|
|
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
|
|
|
file.write(pathData_.toUtf8());
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QMessageBox::information(this, "生成", "任务数据包已生成!路径保存到task_path.json");
|
|
|
|
|
saveTaskData();
|
|
|
|
|
QMessageBox::information(this, "生成", "任务数据包已生成!路径保存到 Src/data_paths/task_path.json");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onNextStep() {
|
|
|
|
|
@ -434,11 +608,21 @@ void TaskDecisionPage::saveCurrentPath() {
|
|
|
|
|
QMessageBox::warning(this, "错误", "无当前路径可保存");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QFile file(QString("saved_path_%1.json").arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss")));
|
|
|
|
|
|
|
|
|
|
// 确保data_paths目录存在
|
|
|
|
|
QDir dataDir;
|
|
|
|
|
if (!dataDir.exists("Src/data_paths")) {
|
|
|
|
|
dataDir.mkdir("Src/data_paths");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(pathData_.toUtf8());
|
|
|
|
|
QString formatted = doc.toJson(QJsonDocument::Indented); // 添加换行/缩进
|
|
|
|
|
QString fileName = QString("Src/data_paths/saved_path_%1.json").arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"));
|
|
|
|
|
QFile file(fileName);
|
|
|
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
|
|
|
file.write(pathData_.toUtf8());
|
|
|
|
|
file.write(formatted.toUtf8());
|
|
|
|
|
file.close();
|
|
|
|
|
QMessageBox::information(this, "成功", "当前路径已保存到文件");
|
|
|
|
|
QMessageBox::information(this, "成功", QString("当前路径已保存到: %1").arg(fileName));
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::warning(this, "错误", "保存失败");
|
|
|
|
|
}
|
|
|
|
|
@ -868,3 +1052,62 @@ void TaskDecisionPage::showBatchUpdateDialog() {
|
|
|
|
|
|
|
|
|
|
dialog.exec();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::initTaskData(bool isNew) {
|
|
|
|
|
if (isNew) {
|
|
|
|
|
taskData_->pathData = "";
|
|
|
|
|
taskData_->searchData = "";
|
|
|
|
|
taskData_->strikeList = QJsonArray();
|
|
|
|
|
} else {
|
|
|
|
|
// Optionally load from file, but for now, assume new
|
|
|
|
|
initTaskData(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::saveTaskData() {
|
|
|
|
|
// Update strikeList from table
|
|
|
|
|
taskData_->strikeList = QJsonArray();
|
|
|
|
|
for (int i = 0; i < targetTable_->rowCount(); ++i) {
|
|
|
|
|
QJsonObject item;
|
|
|
|
|
item["type"] = qobject_cast<QComboBox*>(targetTable_->cellWidget(i, 0))->currentText();
|
|
|
|
|
item["priority"] = qobject_cast<QSpinBox*>(targetTable_->cellWidget(i, 1))->value();
|
|
|
|
|
item["method"] = qobject_cast<QComboBox*>(targetTable_->cellWidget(i, 2))->currentText();
|
|
|
|
|
taskData_->strikeList.append(item);
|
|
|
|
|
}
|
|
|
|
|
// Save to file
|
|
|
|
|
QJsonDocument doc(taskData_->toJson());
|
|
|
|
|
QFile file("task_data.json");
|
|
|
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
|
|
|
file.write(doc.toJson());
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::viewTaskData() {
|
|
|
|
|
QJsonDocument doc(taskData_->toJson());
|
|
|
|
|
QMessageBox::information(this, "任务数据", doc.toJson(QJsonDocument::Indented));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskDecisionPage::onNewTaskClicked() {
|
|
|
|
|
QMessageBox::StandardButton reply = QMessageBox::question(this, "新建任务", "是否新建一个任务?", QMessageBox::Yes | QMessageBox::No);
|
|
|
|
|
if (reply == QMessageBox::Yes) {
|
|
|
|
|
initTaskData(true);
|
|
|
|
|
stack_->setCurrentIndex(1); // Switch to main decision page
|
|
|
|
|
onStepChanged(0); // Proceed to step 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget* TaskDecisionPage::createWelcomeWidget() {
|
|
|
|
|
QWidget* welcome = new QWidget();
|
|
|
|
|
auto* layout = new QVBoxLayout(welcome);
|
|
|
|
|
layout->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
|
|
|
|
QLabel* label = new QLabel("欢迎进入任务决策页面");
|
|
|
|
|
layout->addWidget(label);
|
|
|
|
|
|
|
|
|
|
newTaskBtn_ = new QPushButton("新建任务");
|
|
|
|
|
connect(newTaskBtn_, &QPushButton::clicked, this, &TaskDecisionPage::onNewTaskClicked);
|
|
|
|
|
layout->addWidget(newTaskBtn_);
|
|
|
|
|
|
|
|
|
|
return welcome;
|
|
|
|
|
}
|
|
|
|
|
|