From d9f1ed922b1b29975b88341665b35509e9802a65 Mon Sep 17 00:00:00 2001
From: tangchao0503 <735056338@qq.com>
Date: Fri, 12 Jun 2026 18:29:46 +0800
Subject: [PATCH] =?UTF-8?q?add=EF=BC=8C=E8=AE=A1=E5=88=92=E9=87=87?=
=?UTF-8?q?=E9=9B=8612=EF=BC=9A=20=E5=9C=A8=E7=95=8C=E9=9D=A2=E4=B8=AD?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=BB=BB=E5=8A=A1=E5=92=8C=E5=85=B6=E5=AD=90?=
=?UTF-8?q?=E4=BB=BB=E5=8A=A1=EF=BC=8C=E5=B9=B6=E5=AE=9E=E6=97=B6=E6=9B=B4?=
=?UTF-8?q?=E6=96=B0=E5=AE=83=E4=BB=AC=E7=9A=84=E7=8A=B6=E6=80=81=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
HPPA/HPPA.vcxproj | 2 +
HPPA/HPPA.vcxproj.filters | 6 +
HPPA/TaskTreeModel.cpp | 584 +++++++++++++++++++++
HPPA/TaskTreeModel.h | 106 ++++
HPPA/TimedDataCollection.cpp | 551 ++++++++++++++-----
HPPA/TimedDataCollection.h | 83 +--
HPPA/TimedDataCollectionDataStructures.cpp | 21 +-
HPPA/TimedDataCollectionDataStructures.h | 2 +
HPPA/TimedDataCollection_ui.ui | 278 ++--------
9 files changed, 1243 insertions(+), 390 deletions(-)
create mode 100644 HPPA/TaskTreeModel.cpp
create mode 100644 HPPA/TaskTreeModel.h
diff --git a/HPPA/HPPA.vcxproj b/HPPA/HPPA.vcxproj
index 349d2e9..9f13fa9 100644
--- a/HPPA/HPPA.vcxproj
+++ b/HPPA/HPPA.vcxproj
@@ -164,6 +164,7 @@
Create
+
@@ -262,6 +263,7 @@
+
diff --git a/HPPA/HPPA.vcxproj.filters b/HPPA/HPPA.vcxproj.filters
index 79bc464..4ccbd4e 100644
--- a/HPPA/HPPA.vcxproj.filters
+++ b/HPPA/HPPA.vcxproj.filters
@@ -247,6 +247,9 @@
Source Files
+
+ Source Files
+
@@ -405,6 +408,9 @@
Header Files
+
+ Header Files
+
diff --git a/HPPA/TaskTreeModel.cpp b/HPPA/TaskTreeModel.cpp
new file mode 100644
index 0000000..87d76f1
--- /dev/null
+++ b/HPPA/TaskTreeModel.cpp
@@ -0,0 +1,584 @@
+#include "TaskTreeModel.h"
+#include
+#include
+#include
+
+// ==================== TreeNode 实现 ====================
+
+TreeNode::TreeNode(TreeNode* parent)
+ : m_parent(parent)
+{
+}
+
+TreeNode::~TreeNode()
+{
+ qDeleteAll(m_children);
+}
+
+void TreeNode::appendChild(TreeNode* child)
+{
+ m_children.append(child);
+}
+
+TreeNode* TreeNode::child(int row)
+{
+ if (row < 0 || row >= m_children.size())
+ return nullptr;
+ return m_children.at(row);
+}
+
+int TreeNode::childCount() const
+{
+ return m_children.size();
+}
+
+int TreeNode::row() const
+{
+ if (m_parent) {
+ return m_parent->m_children.indexOf(const_cast(this));
+ }
+ return 0;
+}
+
+TreeNode* TreeNode::parentNode()
+{
+ return m_parent;
+}
+
+// ==================== TaskTreeModel 实现 ====================
+
+TaskTreeModel::TaskTreeModel(QObject* parent)
+ : QAbstractItemModel(parent)
+ , m_rootNode(new TreeNode())
+{
+ m_rootNode->nodeType = TreeNodeType::Root;
+}
+
+TaskTreeModel::~TaskTreeModel()
+{
+ delete m_rootNode;
+}
+
+QModelIndex TaskTreeModel::index(int row, int column, const QModelIndex& parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ TreeNode* parentNode;
+ if (!parent.isValid())
+ parentNode = m_rootNode;
+ else
+ parentNode = static_cast(parent.internalPointer());
+
+ TreeNode* childNode = parentNode->child(row);
+ if (childNode)
+ return createIndex(row, column, childNode);
+
+ return QModelIndex();
+}
+
+QModelIndex TaskTreeModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ TreeNode* childNode = static_cast(index.internalPointer());
+ TreeNode* parentNode = childNode->parentNode();
+
+ if (parentNode == m_rootNode)
+ return QModelIndex();
+
+ return createIndex(parentNode->row(), 0, parentNode);
+}
+
+int TaskTreeModel::rowCount(const QModelIndex& parent) const
+{
+ TreeNode* parentNode;
+ if (!parent.isValid())
+ parentNode = m_rootNode;
+ else
+ parentNode = static_cast(parent.internalPointer());
+
+ return parentNode->childCount();
+}
+
+int TaskTreeModel::columnCount(const QModelIndex& parent) const
+{
+ Q_UNUSED(parent)
+ return ColumnCount;
+}
+
+QVariant TaskTreeModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ TreeNode* node = static_cast(index.internalPointer());
+
+ if (role == Qt::DisplayRole) {
+ if (node->nodeType == TreeNodeType::Task && node->taskData) {
+ const TimedTask& task = *node->taskData;
+ switch (index.column()) {
+ case ColName:
+ return QString::fromLocal8Bit("定时任务 %1").arg(task.id);
+ case ColScheduledTime:
+ return task.scheduledTime.toString("yyyy-MM-dd HH:mm:ss");
+ case ColStartTime:
+ return task.startTime.isValid() ?
+ task.startTime.toString("HH:mm:ss") : "-";
+ case ColEndTime:
+ return task.endTime.isValid() ?
+ task.endTime.toString("HH:mm:ss") : "-";
+ case ColDuration:
+ return formatDuration(task.durationMinutes);
+ case ColStatus:
+ return statusToString(task.status);
+ case ColProgress: {
+ int finished = 0;
+ for (const auto& sub : task.subTasks) {
+ if (sub.status == TaskStatus::Finished) finished++;
+ }
+ return QString("%1/%2").arg(finished).arg(task.subTasks.size());
+ }
+ case ColPath:
+ return task.savePath;
+ }
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ const SubTask& subTask = *node->subTaskData;
+ switch (index.column()) {
+ case ColName:
+ return subTaskTypeToString(subTask.type);
+ case ColScheduledTime:
+ return "-";
+ case ColStartTime:
+ return subTask.startTime.isValid() ?
+ subTask.startTime.toString("HH:mm:ss") : "-";
+ case ColEndTime:
+ return subTask.endTime.isValid() ?
+ subTask.endTime.toString("HH:mm:ss") : "-";
+ case ColDuration:
+ return formatDuration(subTask.durationMinutes);
+ case ColStatus:
+ return statusToString(subTask.status);
+ case ColProgress:
+ return "-";
+ case ColPath:
+ return "-";
+ }
+ }
+ }
+ else if (role == Qt::BackgroundRole) {
+ TaskStatus status = TaskStatus::Waiting;
+ if (node->nodeType == TreeNodeType::Task && node->taskData) {
+ status = node->taskData->status;
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ status = node->subTaskData->status;
+ }
+ return QBrush(statusColor(status));
+ }
+ else if (role == Qt::ForegroundRole) {
+ TaskStatus status = TaskStatus::Waiting;
+ if (node->nodeType == TreeNodeType::Task && node->taskData) {
+ status = node->taskData->status;
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ status = node->subTaskData->status;
+ }
+ if (status == TaskStatus::Running) {
+ return QBrush(QColor(0, 100, 0)); // 深绿色文字
+ }
+ }
+ else if (role == Qt::FontRole) {
+ QFont font;
+ if (node->nodeType == TreeNodeType::Task) {
+ font.setBold(true);
+ if (node->taskData && node->taskData->status == TaskStatus::Running) {
+ font.setItalic(true);
+ }
+ }
+ else if (node->nodeType == TreeNodeType::SubTask) {
+ if (node->subTaskData && node->subTaskData->status == TaskStatus::Running) {
+ font.setBold(true);
+ }
+ }
+ return font;
+ }
+ else if (role == Qt::DecorationRole && index.column() == ColName) {
+ if (node->nodeType == TreeNodeType::Task) {
+ // 可以返回任务图标
+ // return QIcon(":/icons/task.png");
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ // 根据子任务类型返回不同图标
+ switch (node->subTaskData->type) {
+ case SubTaskType::HyperSpectual400_1000nm:
+ case SubTaskType::HyperSpectual1000_1700nm:
+ // return QIcon(":/icons/hyperspectral.png");
+ break;
+ case SubTaskType::SingleLensReflex:
+ // return QIcon(":/icons/camera.png");
+ break;
+ case SubTaskType::DepthCamera:
+ // return QIcon(":/icons/depth.png");
+ break;
+ }
+ }
+ }
+ else if (role == Qt::TextAlignmentRole) {
+ if (index.column() == ColName || index.column() == ColPath) {
+ return static_cast(Qt::AlignLeft | Qt::AlignVCenter);
+ }
+ return Qt::AlignCenter;
+ }
+ else if (role == Qt::ToolTipRole) {
+ if (node->nodeType == TreeNodeType::Task && node->taskData) {
+ return QString::fromLocal8Bit("任务ID: %1\n保存路径: %2\n预热时间: %3 分钟")
+ .arg(node->taskData->id)
+ .arg(node->taskData->savePath)
+ .arg(node->taskData->HalogenLampPreheatingTime_Minute);
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ QString tip = QString::fromLocal8Bit("类型: %1\n路径文件: %2")
+ .arg(subTaskTypeToString(node->subTaskData->type))
+ .arg(node->subTaskData->pathLineFilePath);
+
+ if (node->subTaskData->type == SubTaskType::HyperSpectual400_1000nm ||
+ node->subTaskData->type == SubTaskType::HyperSpectual1000_1700nm) {
+ tip += QString::fromLocal8Bit("\n帧率: %1\n曝光时间: %2")
+ .arg(node->subTaskData->frameRate)
+ .arg(node->subTaskData->exposureTime);
+ }
+ return tip;
+ }
+ }
+
+ return QVariant();
+}
+
+QVariant TaskTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
+ return QVariant();
+
+ switch (section) {
+ case ColName: return QString::fromLocal8Bit("名称");
+ case ColScheduledTime: return QString::fromLocal8Bit("计划时间");
+ case ColStartTime: return QString::fromLocal8Bit("开始时间");
+ case ColEndTime: return QString::fromLocal8Bit("结束时间");
+ case ColDuration: return QString::fromLocal8Bit("耗时");
+ case ColStatus: return QString::fromLocal8Bit("状态");
+ case ColProgress: return QString::fromLocal8Bit("进度");
+ case ColPath: return QString::fromLocal8Bit("路径");
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags TaskTreeModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+void TaskTreeModel::setTasks(const QVector& tasks)
+{
+ beginResetModel();
+
+ m_tasks = tasks;
+ clearTree();
+ setupModelData();
+
+ endResetModel();
+}
+
+void TaskTreeModel::setupModelData()
+{
+ for (int i = 0; i < m_tasks.size(); ++i) {
+ TimedTask& task = m_tasks[i];
+
+ // 创建任务节点
+ TreeNode* taskNode = new TreeNode(m_rootNode);
+ taskNode->nodeType = TreeNodeType::Task;
+ taskNode->taskId = task.id;
+ taskNode->taskData = &task;
+ m_rootNode->appendChild(taskNode);
+
+ // 为每个子任务创建节点
+ for (int j = 0; j < task.subTasks.size(); ++j) {
+ SubTask& subTask = task.subTasks[j];
+
+ TreeNode* subTaskNode = new TreeNode(taskNode);
+ subTaskNode->nodeType = TreeNodeType::SubTask;
+ subTaskNode->taskId = task.id;
+ subTaskNode->subTaskIndex = j;
+ subTaskNode->subTaskData = &subTask;
+ taskNode->appendChild(subTaskNode);
+ }
+ }
+}
+
+void TaskTreeModel::clearTree()
+{
+ // 删除所有子节点
+ while (m_rootNode->childCount() > 0) {
+ delete m_rootNode->child(0);
+ }
+}
+
+void TaskTreeModel::updateTask(const TimedTask& task)
+{
+ int dataIndex = findTaskDataIndex(task.id);
+ if (dataIndex < 0) return;
+
+ // 更新数据
+ m_tasks[dataIndex] = task;
+
+ // 重新设置指针(因为数据被复制了)
+ TreeNode* taskNode = findTaskNode(task.id);
+ if (taskNode) {
+ taskNode->taskData = &m_tasks[dataIndex];
+
+ // 更新子任务指针
+ for (int i = 0; i < taskNode->childCount() && i < m_tasks[dataIndex].subTasks.size(); ++i) {
+ TreeNode* subNode = taskNode->child(i);
+ if (subNode) {
+ subNode->subTaskData = &m_tasks[dataIndex].subTasks[i];
+ }
+ }
+ }
+
+ // 通知视图更新任务行
+ QModelIndex taskIndex = getTaskIndex(task.id);
+ if (taskIndex.isValid()) {
+ QModelIndex topLeft = index(taskIndex.row(), 0, taskIndex.parent());
+ QModelIndex bottomRight = index(taskIndex.row(), ColumnCount - 1, taskIndex.parent());
+ emit dataChanged(topLeft, bottomRight);
+
+ // 更新所有子任务行
+ int subCount = rowCount(taskIndex);
+ if (subCount > 0) {
+ QModelIndex subTopLeft = index(0, 0, taskIndex);
+ QModelIndex subBottomRight = index(subCount - 1, ColumnCount - 1, taskIndex);
+ emit dataChanged(subTopLeft, subBottomRight);
+ }
+ }
+
+ emit taskUpdated(task.id);
+}
+
+void TaskTreeModel::updateSubTask(int taskId, int subTaskIndex, const SubTask& subTask)
+{
+ int dataIndex = findTaskDataIndex(taskId);
+ if (dataIndex < 0 || subTaskIndex >= m_tasks[dataIndex].subTasks.size())
+ return;
+
+ m_tasks[dataIndex].subTasks[subTaskIndex] = subTask;
+
+ // 重新设置指针
+ TreeNode* subNode = findSubTaskNode(taskId, subTaskIndex);
+ if (subNode) {
+ subNode->subTaskData = &m_tasks[dataIndex].subTasks[subTaskIndex];
+ }
+
+ QModelIndex subTaskModelIndex = getSubTaskIndex(taskId, subTaskIndex);
+ if (subTaskModelIndex.isValid()) {
+ QModelIndex topLeft = index(subTaskModelIndex.row(), 0, subTaskModelIndex.parent());
+ QModelIndex bottomRight = index(subTaskModelIndex.row(), ColumnCount - 1, subTaskModelIndex.parent());
+ emit dataChanged(topLeft, bottomRight);
+ }
+
+ // 同时更新父任务的进度显示
+ QModelIndex taskIndex = getTaskIndex(taskId);
+ if (taskIndex.isValid()) {
+ QModelIndex progressIndex = index(taskIndex.row(), ColProgress, taskIndex.parent());
+ emit dataChanged(progressIndex, progressIndex);
+ }
+
+ emit subTaskUpdated(taskId, subTaskIndex);
+}
+
+void TaskTreeModel::updateTaskStatus(int taskId, TaskStatus status)
+{
+ int dataIndex = findTaskDataIndex(taskId);
+ if (dataIndex < 0) return;
+
+ m_tasks[dataIndex].status = status;
+
+ if (status == TaskStatus::Running) {
+ m_tasks[dataIndex].startTime = QDateTime::currentDateTime();
+ }
+ else if (status == TaskStatus::Finished) {
+ m_tasks[dataIndex].endTime = QDateTime::currentDateTime();
+ if (m_tasks[dataIndex].startTime.isValid()) {
+ m_tasks[dataIndex].durationMinutes =
+ m_tasks[dataIndex].startTime.secsTo(m_tasks[dataIndex].endTime) / 60.0;
+ }
+ }
+
+ QModelIndex taskIndex = getTaskIndex(taskId);
+ if (taskIndex.isValid()) {
+ QModelIndex topLeft = index(taskIndex.row(), 0, taskIndex.parent());
+ QModelIndex bottomRight = index(taskIndex.row(), ColumnCount - 1, taskIndex.parent());
+ emit dataChanged(topLeft, bottomRight);
+ }
+}
+
+void TaskTreeModel::updateSubTaskStatus(int taskId, int subTaskIndex, TaskStatus status)
+{
+ int dataIndex = findTaskDataIndex(taskId);
+ if (dataIndex < 0 || subTaskIndex >= m_tasks[dataIndex].subTasks.size())
+ return;
+
+ SubTask& subTask = m_tasks[dataIndex].subTasks[subTaskIndex];
+ subTask.status = status;
+
+ if (status == TaskStatus::Running) {
+ subTask.startTime = QDateTime::currentDateTime();
+ }
+ else if (status == TaskStatus::Finished) {
+ subTask.endTime = QDateTime::currentDateTime();
+ if (subTask.startTime.isValid()) {
+ subTask.durationMinutes = subTask.startTime.secsTo(subTask.endTime) / 60.0;
+ }
+ }
+
+ QModelIndex subTaskModelIndex = getSubTaskIndex(taskId, subTaskIndex);
+ if (subTaskModelIndex.isValid()) {
+ QModelIndex topLeft = index(subTaskModelIndex.row(), 0, subTaskModelIndex.parent());
+ QModelIndex bottomRight = index(subTaskModelIndex.row(), ColumnCount - 1, subTaskModelIndex.parent());
+ emit dataChanged(topLeft, bottomRight);
+ }
+
+ // 更新父任务进度
+ QModelIndex taskIndex = getTaskIndex(taskId);
+ if (taskIndex.isValid()) {
+ QModelIndex progressIndex = index(taskIndex.row(), ColProgress, taskIndex.parent());
+ emit dataChanged(progressIndex, progressIndex);
+ }
+}
+
+TimedTask* TaskTreeModel::getTask(int taskId)
+{
+ int idx = findTaskDataIndex(taskId);
+ if (idx >= 0) {
+ return &m_tasks[idx];
+ }
+ return nullptr;
+}
+
+SubTask* TaskTreeModel::getSubTask(int taskId, int subTaskIndex)
+{
+ TimedTask* task = getTask(taskId);
+ if (task && subTaskIndex >= 0 && subTaskIndex < task->subTasks.size()) {
+ return &task->subTasks[subTaskIndex];
+ }
+ return nullptr;
+}
+
+QModelIndex TaskTreeModel::getTaskIndex(int taskId) const
+{
+ for (int i = 0; i < m_rootNode->childCount(); ++i) {
+ TreeNode* node = m_rootNode->child(i);
+ if (node && node->taskId == taskId) {
+ return createIndex(i, 0, node);
+ }
+ }
+ return QModelIndex();
+}
+
+QModelIndex TaskTreeModel::getSubTaskIndex(int taskId, int subTaskIndex) const
+{
+ TreeNode* taskNode = findTaskNode(taskId);
+ if (taskNode && subTaskIndex < taskNode->childCount()) {
+ TreeNode* subNode = taskNode->child(subTaskIndex);
+ if (subNode) {
+ return createIndex(subTaskIndex, 0, subNode);
+ }
+ }
+ return QModelIndex();
+}
+
+TreeNode* TaskTreeModel::findTaskNode(int taskId) const
+{
+ for (int i = 0; i < m_rootNode->childCount(); ++i) {
+ TreeNode* node = m_rootNode->child(i);
+ if (node && node->taskId == taskId) {
+ return node;
+ }
+ }
+ return nullptr;
+}
+
+TreeNode* TaskTreeModel::findSubTaskNode(int taskId, int subTaskIndex) const
+{
+ TreeNode* taskNode = findTaskNode(taskId);
+ if (taskNode && subTaskIndex < taskNode->childCount()) {
+ return taskNode->child(subTaskIndex);
+ }
+ return nullptr;
+}
+
+int TaskTreeModel::findTaskDataIndex(int taskId) const
+{
+ for (int i = 0; i < m_tasks.size(); ++i) {
+ if (m_tasks[i].id == taskId) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+QString TaskTreeModel::statusToString(TaskStatus status) const
+{
+ switch (status) {
+ case TaskStatus::Waiting: return QString::fromLocal8Bit("等待中");
+ case TaskStatus::Running: return QString::fromLocal8Bit("运行中");
+ case TaskStatus::Finished: return QString::fromLocal8Bit("已完成");
+ case TaskStatus::Skiped: return QString::fromLocal8Bit("已跳过");
+ }
+ return "未知";
+}
+
+QString TaskTreeModel::subTaskTypeToString(SubTaskType type) const
+{
+ switch (type) {
+ case SubTaskType::HyperSpectual400_1000nm: return QString::fromLocal8Bit("高光谱 400-1000nm");
+ case SubTaskType::HyperSpectual1000_1700nm: return QString::fromLocal8Bit("高光谱 1000-1700nm");
+ case SubTaskType::SingleLensReflex: return QString::fromLocal8Bit("单反相机");
+ case SubTaskType::DepthCamera: return QString::fromLocal8Bit("深度相机");
+ }
+ return "未知类型";
+}
+
+QString TaskTreeModel::formatDuration(double minutes) const
+{
+ if (minutes <= 0) return "-";
+
+ int hours = static_cast(minutes) / 60;
+ int mins = static_cast(minutes) % 60;
+ int secs = static_cast((minutes - static_cast(minutes)) * 60);
+
+ if (hours > 0) {
+ return QString::fromLocal8Bit(("%1时%2分%3秒")).arg(hours).arg(mins).arg(secs);
+ }
+ else if (mins > 0) {
+ return QString::fromLocal8Bit(("%1分%2秒")).arg(mins).arg(secs);
+ }
+ else {
+ return QString::fromLocal8Bit(("%1秒")).arg(secs);
+ }
+}
+
+QColor TaskTreeModel::statusColor(TaskStatus status) const
+{
+ switch (status) {
+ case TaskStatus::Waiting: return QColor(255, 255, 230); // 浅黄
+ case TaskStatus::Running: return QColor(200, 255, 200); // 浅绿
+ case TaskStatus::Finished: return QColor(220, 220, 255); // 浅蓝
+ case TaskStatus::Skiped: return QColor(220, 220, 220); // 浅灰
+ }
+ return QColor(255, 255, 255);
+}
diff --git a/HPPA/TaskTreeModel.h b/HPPA/TaskTreeModel.h
new file mode 100644
index 0000000..e60ed38
--- /dev/null
+++ b/HPPA/TaskTreeModel.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include
+#include
+#include
+#include "TimedDataCollectionDataStructures.h"
+
+// 树节点类型
+enum class TreeNodeType {
+ Root,
+ Task,
+ SubTask
+};
+
+// 树节点数据
+class TreeNode
+{
+public:
+ explicit TreeNode(TreeNode* parent = nullptr);
+ ~TreeNode();
+
+ void appendChild(TreeNode* child);
+ TreeNode* child(int row);
+ int childCount() const;
+ int row() const;
+ TreeNode* parentNode();
+
+ TreeNodeType nodeType = TreeNodeType::Root;
+ int taskId = -1; // 任务ID
+ int subTaskIndex = -1; // 子任务索引(仅子任务节点有效)
+
+ // 直接存储数据指针,方便更新
+ TimedTask* taskData = nullptr;
+ SubTask* subTaskData = nullptr;
+
+private:
+ QVector m_children;
+ TreeNode* m_parent;
+};
+
+// 树形模型
+class TaskTreeModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ enum Column {
+ ColName = 0, // 任务名称/子任务类型
+ ColScheduledTime, // 计划时间
+ ColStartTime, // 开始时间
+ ColEndTime, // 结束时间
+ ColDuration, // 耗时
+ ColStatus, // 状态
+ ColProgress, // 进度(仅任务有效)
+ ColPath, // 路径
+ ColumnCount
+ };
+
+ explicit TaskTreeModel(QObject* parent = nullptr);
+ ~TaskTreeModel();
+
+ // QAbstractItemModel 接口实现
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex& index) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ // 数据操作
+ void setTasks(const QVector& tasks);
+ void updateTask(const TimedTask& task);
+ void updateSubTask(int taskId, int subTaskIndex, const SubTask& subTask);
+ void updateTaskStatus(int taskId, TaskStatus status);
+ void updateSubTaskStatus(int taskId, int subTaskIndex, TaskStatus status);
+
+ // 获取数据
+ QVector& tasks() { return m_tasks; }
+ const QVector& tasks() const { return m_tasks; }
+ TimedTask* getTask(int taskId);
+ SubTask* getSubTask(int taskId, int subTaskIndex);
+
+ // 获取模型索引
+ QModelIndex getTaskIndex(int taskId) const;
+ QModelIndex getSubTaskIndex(int taskId, int subTaskIndex) const;
+
+signals:
+ void taskUpdated(int taskId);
+ void subTaskUpdated(int taskId, int subTaskIndex);
+
+private:
+ void setupModelData();
+ void clearTree();
+ TreeNode* findTaskNode(int taskId) const;
+ TreeNode* findSubTaskNode(int taskId, int subTaskIndex) const;
+ int findTaskDataIndex(int taskId) const;
+
+ QString statusToString(TaskStatus status) const;
+ QString subTaskTypeToString(SubTaskType type) const;
+ QString formatDuration(double minutes) const;
+ QColor statusColor(TaskStatus status) const;
+
+ TreeNode* m_rootNode;
+ QVector m_tasks;
+};
diff --git a/HPPA/TimedDataCollection.cpp b/HPPA/TimedDataCollection.cpp
index f812e97..f21f85a 100644
--- a/HPPA/TimedDataCollection.cpp
+++ b/HPPA/TimedDataCollection.cpp
@@ -1,169 +1,462 @@
#include "TimedDataCollection.h"
-#include
-#include
+#include
+#include
+#include
+#include
+#include
TimedDataCollection::TimedDataCollection(QWidget* parent)
- : QDialog(parent)
- , m_scheduler(nullptr)
+ : QDialog(parent)
{
- ui.setupUi(this);
+ ui.setupUi(this);
- ui.treeWidget->setDragEnabled(true); // 启用拖拽
- ui.treeWidget->setAcceptDrops(true); // 接受拖放
- ui.treeWidget->setDropIndicatorShown(true); // 显示插入位置指示线
- ui.treeWidget->setDragDropMode(QAbstractItemView::InternalMove); // 内部移动
+ setupUI();
- // 初始化调度器
- m_scheduler = new TaskScheduler(this);
+ // 初始化调度器
+ m_scheduler = new TaskScheduler(this);
- // 连接调度器信号
- connect(m_scheduler, &TaskScheduler::taskStarted, this, &TimedDataCollection::taskStarted);
- connect(m_scheduler, &TaskScheduler::taskFinished, this, &TimedDataCollection::taskFinished);
- connect(m_scheduler, &TaskScheduler::subTaskStarted, this, &TimedDataCollection::subTaskStarted);
- connect(m_scheduler, &TaskScheduler::subTaskFinished, this, &TimedDataCollection::subTaskFinished);
- connect(m_scheduler, &TaskScheduler::errorOccurred, this, &TimedDataCollection::errorOccurred);
+ setupConnections();
- // 采集相关信号透传
- connect(m_scheduler, &TaskScheduler::hyperCamParm, this, &TimedDataCollection::hyperCamParm);
- connect(m_scheduler, &TaskScheduler::camParm, this, &TimedDataCollection::camParm);
- connect(m_scheduler, &TaskScheduler::motorParm, this, &TimedDataCollection::motorParm);
- connect(m_scheduler, &TaskScheduler::startRecordSignal, this, &TimedDataCollection::startRecordSignal);
+ // 读取任务文件
+ readTimedTaskFromFile("D:/0tmp/3Dtest/task.json");
+}
- connect(m_scheduler, &TaskScheduler::switchHalogenLampSignal, this, &TimedDataCollection::switchHalogenLampSignal);
- connect(m_scheduler, &TaskScheduler::switchD65LampSignal, this, &TimedDataCollection::switchD65LampSignal);
- connect(m_scheduler, &TaskScheduler::switchSlrSignal, this, &TimedDataCollection::switchSlrSignal);
+void TimedDataCollection::setupUI()
+{
+ // 设置窗口大小
+ //resize(1200, 600);
+ //setWindowTitle("定时数据采集系统");
- connect(this, &TimedDataCollection::sequenceCompleteSignal, m_scheduler, &TaskScheduler::sequenceCompleteSignal);
- connect(this, &TimedDataCollection::Back2OriginSignal, m_scheduler, &TaskScheduler::Back2OriginSignal);
+ // 创建任务树形模型
+ m_taskModel = new TaskTreeModel(this);
- //writeRead();
- readTimedTaskFromFile("D:/0tmp/3Dtest/task.json");
+ // 创建树形视图
+ ui.taskTreeView->setModel(m_taskModel);
- // 加载任务到调度器
- m_scheduler->loadTasks(m_loadedTasks);
+ // 配置树形视图
+ ui.taskTreeView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ ui.taskTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui.taskTreeView->setAlternatingRowColors(true);
+ ui.taskTreeView->setAnimated(true);
+ ui.taskTreeView->setExpandsOnDoubleClick(true);
+ ui.taskTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ ui.taskTreeView->setUniformRowHeights(true);
+ ui.taskTreeView->setIndentation(25);
- connect(ui.run_btn, &QPushButton::clicked, this, &TimedDataCollection::startScheduler);
+ // 配置表头
+ QHeaderView* header = ui.taskTreeView->header();
+ header->setStretchLastSection(true);
+ header->setSectionResizeMode(QHeaderView::Interactive);
+ header->setDefaultAlignment(Qt::AlignCenter);
- startScheduler();
+ // 设置列宽
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColName, 200);
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColScheduledTime, 150);
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColStartTime, 100);
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColEndTime, 100);
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColDuration, 100);
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColStatus, 100);
+ ui.taskTreeView->setColumnWidth(TaskTreeModel::ColProgress, 80);
+
+ // 创建状态标签
+ ui.statusLabel->setStyleSheet(
+ "QLabel {"
+ " background-color: #f0f0f0;"
+ " border: 1px solid #ccc;"
+ " border-radius: 4px;"
+ " padding: 8px;"
+ " font-size: 12px;"
+ "}"
+ );
+
+ // 连接按钮信号
+ connect(ui.start_btn, &QPushButton::clicked, this, &TimedDataCollection::startScheduler);
+ connect(ui.stop_btn, &QPushButton::clicked, this, &TimedDataCollection::stopScheduler);
+ connect(ui.refresh_btn, &QPushButton::clicked, this, [this]() {
+ readTimedTaskFromFile("D:/0tmp/3Dtest/task.json");
+ });
+ connect(ui.expandAll_btn, &QPushButton::clicked, ui.taskTreeView, &QTreeView::expandAll);
+ connect(ui.collapseAll_btn, &QPushButton::clicked, ui.taskTreeView, &QTreeView::collapseAll);
+
+ // 树视图交互信号
+ connect(ui.taskTreeView, &QTreeView::clicked,
+ this, &TimedDataCollection::onTreeItemClicked);
+ connect(ui.taskTreeView, &QTreeView::doubleClicked,
+ this, &TimedDataCollection::onTreeItemDoubleClicked);
+ connect(ui.taskTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &TimedDataCollection::onTreeSelectionChanged);
+}
+
+void TimedDataCollection::setupConnections()
+{
+ // 调度器信号连接
+ connect(m_scheduler, &TaskScheduler::taskStarted,
+ this, &TimedDataCollection::onTaskStarted);
+ connect(m_scheduler, &TaskScheduler::taskFinished,
+ this, &TimedDataCollection::onTaskFinished);
+ connect(m_scheduler, &TaskScheduler::subTaskStarted,
+ this, &TimedDataCollection::onSubTaskStarted);
+ connect(m_scheduler, &TaskScheduler::subTaskFinished,
+ this, &TimedDataCollection::onSubTaskFinished);
+ connect(m_scheduler, &TaskScheduler::taskDataChanged,
+ this, &TimedDataCollection::onTaskDataChanged);
+ connect(m_scheduler, &TaskScheduler::errorOccurred,
+ this, &TimedDataCollection::onErrorOccurred);
+
+ // 采集相关信号透传
+ connect(m_scheduler, &TaskScheduler::hyperCamParm,
+ this, &TimedDataCollection::hyperCamParm);
+ connect(m_scheduler, &TaskScheduler::camParm,
+ this, &TimedDataCollection::camParm);
+ connect(m_scheduler, &TaskScheduler::motorParm,
+ this, &TimedDataCollection::motorParm);
+ connect(m_scheduler, &TaskScheduler::startRecordSignal,
+ this, &TimedDataCollection::startRecordSignal);
+
+ connect(m_scheduler, &TaskScheduler::switchHalogenLampSignal,
+ this, &TimedDataCollection::switchHalogenLampSignal);
+ connect(m_scheduler, &TaskScheduler::switchD65LampSignal,
+ this, &TimedDataCollection::switchD65LampSignal);
+ connect(m_scheduler, &TaskScheduler::switchSlrSignal,
+ this, &TimedDataCollection::switchSlrSignal);
+
+ connect(this, &TimedDataCollection::sequenceCompleteSignal,
+ m_scheduler, &TaskScheduler::sequenceCompleteSignal);
+ connect(this, &TimedDataCollection::Back2OriginSignal,
+ m_scheduler, &TaskScheduler::Back2OriginSignal);
}
TimedDataCollection::~TimedDataCollection()
{
- if (m_scheduler) {
- m_scheduler->stop();
- }
+ if (m_scheduler) {
+ m_scheduler->stop();
+ }
}
-void TimedDataCollection::subTaskCompleted(int status)
+// ==================== 任务状态更新槽实现 ====================
+
+void TimedDataCollection::onTaskStarted(int taskId)
{
- emit sequenceCompleteSignal(status);
+ m_currentTaskId = taskId;
+ m_currentSubTaskIndex = -1;
+
+ m_taskModel->updateTaskStatus(taskId, TaskStatus::Running);
+
+ // 展开当前运行的任务
+ expandRunningTask();
+
+ updateStatusBar();
+ emit taskStarted(taskId);
}
-void TimedDataCollection::onBack2Origin()
+void TimedDataCollection::onTaskFinished(int taskId, bool success)
{
- emit Back2OriginSignal();
+ TaskStatus status = success ? TaskStatus::Finished : TaskStatus::Skiped;
+ m_taskModel->updateTaskStatus(taskId, status);
+
+ if (m_currentTaskId == taskId) {
+ m_currentTaskId = -1;
+ m_currentSubTaskIndex = -1;
+ }
+
+ updateStatusBar();
+ emit taskFinished(taskId, success);
}
-void TimedDataCollection::startScheduler()
+void TimedDataCollection::onSubTaskStarted(int taskId, int subTaskIndex)
{
- if (m_scheduler) {
- m_scheduler->start();
- }
+ m_currentSubTaskIndex = subTaskIndex;
+
+ m_taskModel->updateSubTaskStatus(taskId, subTaskIndex, TaskStatus::Running);
+
+ // 滚动到当前子任务
+ QModelIndex subTaskIndex_model = m_taskModel->getSubTaskIndex(taskId, subTaskIndex);
+ if (subTaskIndex_model.isValid()) {
+ ui.taskTreeView->scrollTo(subTaskIndex_model);
+ ui.taskTreeView->setCurrentIndex(subTaskIndex_model);
+ }
+
+ updateStatusBar();
+ emit subTaskStarted(taskId, subTaskIndex);
}
-void TimedDataCollection::stopScheduler()
+void TimedDataCollection::onSubTaskFinished(int taskId, int subTaskIndex)
{
- if (m_scheduler) {
- m_scheduler->stop();
- }
+ m_taskModel->updateSubTaskStatus(taskId, subTaskIndex, TaskStatus::Finished);
+
+ updateStatusBar();
+ emit subTaskFinished(taskId, subTaskIndex);
+}
+
+void TimedDataCollection::onTaskDataChanged(const TimedTask& task)
+{
+ // 核心:从 TaskExecutor 同步回来的完整任务数据
+ m_taskModel->updateTask(task);
+
+ updateStatusBar();
+}
+
+void TimedDataCollection::onErrorOccurred(const QString& error)
+{
+ ui.statusLabel->setText(QString::fromLocal8Bit("错误: %1").arg(error));
+ ui.statusLabel->setStyleSheet(
+ "QLabel {"
+ " background-color: #ffebee;"
+ " border: 1px solid #f44336;"
+ " border-radius: 4px;"
+ " padding: 8px;"
+ " color: #c62828;"
+ "}"
+ );
+
+ QMessageBox::warning(this, "错误", error);
+ emit errorOccurred(error);
+}
+
+void TimedDataCollection::onTreeItemClicked(const QModelIndex& index)
+{
+ if (!index.isValid()) return;
+
+ TreeNode* node = static_cast(index.internalPointer());
+ if (!node) return;
+
+ QString info;
+ if (node->nodeType == TreeNodeType::Task && node->taskData) {
+ info = QString::fromLocal8Bit("选中任务 %1 - 状态: %2")
+ .arg(node->taskData->id)
+ .arg(node->taskData->status == TaskStatus::Running ? "运行中" :
+ node->taskData->status == TaskStatus::Finished ? "已完成" : "等待中");
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ info = QString::fromLocal8Bit("选中子任务 - 类型: %1")
+ .arg(m_taskModel->data(m_taskModel->index(index.row(),
+ TaskTreeModel::ColName, index.parent())).toString());
+ }
+
+ // 可以更新状态栏或其他UI
+}
+
+void TimedDataCollection::onTreeItemDoubleClicked(const QModelIndex& index)
+{
+ if (!index.isValid()) return;
+
+ TreeNode* node = static_cast(index.internalPointer());
+ if (!node) return;
+
+ if (node->nodeType == TreeNodeType::Task && node->taskData) {
+ const TimedTask& task = *node->taskData;
+ QString details = QString::fromLocal8Bit(
+ "【任务详情】\n\n"
+ "任务ID: %1\n"
+ "计划时间: %2\n"
+ "开始时间: %3\n"
+ "结束时间: %4\n"
+ "耗时: %5 分钟\n"
+ "状态: %6\n"
+ "子任务数: %7\n"
+ "保存路径: %8\n"
+ "卤素灯预热时间: %9 分钟"
+ ).arg(task.id)
+ .arg(task.scheduledTime.toString("yyyy-MM-dd HH:mm:ss"))
+ .arg(task.startTime.isValid() ? task.startTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
+ .arg(task.endTime.isValid() ? task.endTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
+ .arg(task.durationMinutes, 0, 'f', 2)
+ .arg(static_cast(task.status))
+ .arg(task.subTasks.size())
+ .arg(task.savePath)
+ .arg(task.HalogenLampPreheatingTime_Minute);
+
+ QMessageBox::information(this, QString::fromLocal8Bit("任务详情"), details);
+ }
+ else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
+ const SubTask& subTask = *node->subTaskData;
+ QString typeStr;
+ switch (subTask.type) {
+ case SubTaskType::HyperSpectual400_1000nm: typeStr = QString::fromLocal8Bit("高光谱 400-1000nm"); break;
+ case SubTaskType::HyperSpectual1000_1700nm: typeStr = QString::fromLocal8Bit("高光谱 1000-1700nm"); break;
+ case SubTaskType::SingleLensReflex: typeStr = QString::fromLocal8Bit("单反相机"); break;
+ case SubTaskType::DepthCamera: typeStr = QString::fromLocal8Bit("深度相机"); break;
+ }
+
+ QString details = QString::fromLocal8Bit(
+ "【子任务详情】\n\n"
+ "类型: %1\n"
+ "开始时间: %2\n"
+ "结束时间: %3\n"
+ "耗时: %4 分钟\n"
+ "路径文件: %5\n"
+ "状态: %6"
+ ).arg(typeStr)
+ .arg(subTask.startTime.isValid() ? subTask.startTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
+ .arg(subTask.endTime.isValid() ? subTask.endTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
+ .arg(subTask.durationMinutes, 0, 'f', 2)
+ .arg(subTask.pathLineFilePath)
+ .arg(static_cast(subTask.status));
+
+ if (subTask.type == SubTaskType::HyperSpectual400_1000nm ||
+ subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
+ details += QString::fromLocal8Bit("\n帧率: %1\n曝光时间: %2")
+ .arg(subTask.frameRate)
+ .arg(subTask.exposureTime);
+ }
+
+ QMessageBox::information(this, QString::fromLocal8Bit("子任务详情"), details);
+ }
+}
+
+void TimedDataCollection::onTreeSelectionChanged()
+{
+ // 可以根据选择更新其他UI组件
+}
+
+void TimedDataCollection::updateStatusBar()
+{
+ QString statusText;
+ QString styleSheet;
+
+ if (m_currentTaskId > 0) {
+ TimedTask* task = m_taskModel->getTask(m_currentTaskId);
+ if (task) {
+ int finished = 0;
+ for (const auto& sub : task->subTasks) {
+ if (sub.status == TaskStatus::Finished) finished++;
+ }
+
+ statusText = QString::fromLocal8Bit("▶ 正在执行任务 %1 | 子任务进度: %2/%3")
+ .arg(m_currentTaskId)
+ .arg(finished)
+ .arg(task->subTasks.size());
+
+ if (m_currentSubTaskIndex >= 0 && m_currentSubTaskIndex < task->subTasks.size()) {
+ QString subTypeStr;
+ switch (task->subTasks[m_currentSubTaskIndex].type) {
+ case SubTaskType::HyperSpectual400_1000nm: subTypeStr = QString::fromLocal8Bit("高光谱400-1000nm"); break;
+ case SubTaskType::HyperSpectual1000_1700nm: subTypeStr = QString::fromLocal8Bit("高光谱1000-1700nm"); break;
+ case SubTaskType::SingleLensReflex: subTypeStr = QString::fromLocal8Bit("单反相机"); break;
+ case SubTaskType::DepthCamera: subTypeStr = QString::fromLocal8Bit("深度相机"); break;
+ }
+ statusText += QString::fromLocal8Bit(" | 当前: %1").arg(subTypeStr);
+ }
+
+ styleSheet =
+ "QLabel {"
+ " background-color: #e8f5e9;"
+ " border: 1px solid #4CAF50;"
+ " border-radius: 4px;"
+ " padding: 8px;"
+ " color: #2e7d32;"
+ " font-weight: bold;"
+ "}";
+ }
+ }
+ else {
+ // 统计任务状态
+ int waiting = 0, running = 0, finished = 0;
+ for (const auto& task : m_taskModel->tasks()) {
+ switch (task.status) {
+ case TaskStatus::Waiting: waiting++; break;
+ case TaskStatus::Running: running++; break;
+ case TaskStatus::Finished: finished++; break;
+ default: break;
+ }
+ }
+
+ statusText = QString::fromLocal8Bit("任务统计 | 等待: %1 | 运行中: %2 | 已完成: %3 | 总计: %4")
+ .arg(waiting).arg(running).arg(finished)
+ .arg(m_taskModel->tasks().size());
+
+ styleSheet =
+ "QLabel {"
+ " background-color: #f5f5f5;"
+ " border: 1px solid #ccc;"
+ " border-radius: 4px;"
+ " padding: 8px;"
+ "}";
+ }
+
+ ui.statusLabel->setText(statusText);
+ ui.statusLabel->setStyleSheet(styleSheet);
+}
+
+void TimedDataCollection::expandRunningTask()
+{
+ if (m_currentTaskId > 0) {
+ QModelIndex taskIndex = m_taskModel->getTaskIndex(m_currentTaskId);
+ if (taskIndex.isValid()) {
+ ui.taskTreeView->expand(taskIndex);
+ ui.taskTreeView->scrollTo(taskIndex);
+ ui.taskTreeView->setCurrentIndex(taskIndex);
+ }
+ }
}
void TimedDataCollection::readTimedTaskFromFile(const QString& filePath)
{
- // 从文件读取
- if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, m_loadedTasks)) {
- qDebug() << "Tasks loaded successfully, count:" << m_loadedTasks.size();
- for (const auto& t : m_loadedTasks) {
- qDebug() << " Task ID:" << t.id << "SubTasks:" << t.subTasks.size();
- }
- }
- else {
- qDebug() << "Failed to load tasks from:" << filePath;
- }
+ QVector loadedTasks;
+ if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, loadedTasks)) {
+ qDebug() << "Tasks loaded successfully, count:" << loadedTasks.size();
- int a = 1;
+ m_taskModel->setTasks(loadedTasks);
+ ui.taskTreeView->expandAll(); // 默认展开所有任务
+
+ // 更新调度器
+ if (m_scheduler) {
+ m_scheduler->loadTasks(loadedTasks);
+ }
+
+ updateStatusBar();
+ }
+ else {
+ qDebug() << "Failed to load tasks from:" << filePath;
+ QMessageBox::warning(this, "加载失败",
+ QString("无法从文件加载任务:\n%1").arg(filePath));
+ }
}
-void TimedDataCollection::writeRead()
+void TimedDataCollection::startScheduler()
{
- // 创建2个定时任务测试
- QVector tasks;
+ if (m_scheduler) {
+ m_scheduler->loadTasks(m_taskModel->tasks());
+ m_scheduler->start();
- for (int i = 0; i < 2; ++i) {
- TimedTask task;
- task.id = i + 1;
- task.scheduledTime = QDateTime::currentDateTime().addDays(i + 1);
- task.savePath = QString("D:/0tmp/data");
- task.status = TaskStatus::Waiting;
-
- // 创建4种子任务
- QVector types = {
- SubTaskType::HyperSpectual400_1000nm,
- SubTaskType::HyperSpectual1000_1700nm,
- SubTaskType::SingleLensReflex,
- SubTaskType::DepthCamera
- };
-
- for (int j = 0; j < types.size(); ++j) {
- SubTask subTask;
- subTask.type = types[j];
- subTask.startTime = task.scheduledTime.addSecs(j * 3600);
- subTask.endTime = subTask.startTime.addSecs(1800);
- subTask.durationMinutes = 1800;
- subTask.estimatedDurationMinutes = 1800;
- subTask.pathLineFilePath = QString("D:/0tmp/3Dtest/pathLine/%1.RecordLine3").arg(j);
- subTask.status = TaskStatus::Waiting;
-
- // 根据类型设置特有属性
- if (subTask.type == SubTaskType::HyperSpectual400_1000nm ||
- subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
- subTask.frameRate = 30.0;
- subTask.exposureTime = 1;
- }
- if (subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
- subTask.defaultRenderBand = 1200;
- }
- if (subTask.type == SubTaskType::SingleLensReflex ||
- subTask.type == SubTaskType::DepthCamera) {
- subTask.captureIntervalSeconds = 5;
- }
-
- task.subTasks.append(subTask);
- }
-
- tasks.append(task);
- }
-
- // 保存到文件
- QString filePath = "D:/0tmp/3Dtest/task.json";
- if (TimedDataCollectionDataStructuresReaderWriter::saveTasksToFile(filePath, tasks)) {
- qDebug() << "Tasks saved to:" << filePath;
- }
- else {
- qDebug() << "Failed to save tasks to:" << filePath;
- }
-
- // 从文件读取
- QVector loadedTasks;
- if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, loadedTasks)) {
- qDebug() << "Tasks loaded successfully, count:" << loadedTasks.size();
- for (const auto& t : loadedTasks) {
- qDebug() << " Task ID:" << t.id << "SubTasks:" << t.subTasks.size();
- }
- }
- else {
- qDebug() << "Failed to load tasks from:" << filePath;
- }
-
- int a = 1;
+ ui.statusLabel->setText(QString::fromLocal8Bit("调度器已启动,等待任务执行..."));
+ ui.statusLabel->setStyleSheet(
+ "QLabel {"
+ " background-color: #fff3e0;"
+ " border: 1px solid #ff9800;"
+ " border-radius: 4px;"
+ " padding: 8px;"
+ " color: #e65100;"
+ "}");
+ }
+}
+
+void TimedDataCollection::stopScheduler()
+{
+ if (m_scheduler) {
+ m_scheduler->stop();
+
+ m_currentTaskId = -1;
+ m_currentSubTaskIndex = -1;
+
+ ui.statusLabel->setText(QString::fromLocal8Bit("调度器已停止"));
+ ui.statusLabel->setStyleSheet(
+ "QLabel {"
+ " background-color: #fce4ec;"
+ " border: 1px solid #e91e63;"
+ " border-radius: 4px;"
+ " padding: 8px;"
+ " color: #880e4f;"
+ "}");
+ }
+}
+
+void TimedDataCollection::subTaskCompleted(int status)
+{
+ emit sequenceCompleteSignal(status);
+}
+
+void TimedDataCollection::onBack2Origin()
+{
+ emit Back2OriginSignal();
}
diff --git a/HPPA/TimedDataCollection.h b/HPPA/TimedDataCollection.h
index afc88f9..6faa2d7 100644
--- a/HPPA/TimedDataCollection.h
+++ b/HPPA/TimedDataCollection.h
@@ -1,56 +1,73 @@
#pragma once
#include
-#include
-#include
-#include
-
+#include
+#include
+#include
#include "ui_TimedDataCollection_ui.h"
#include "TimedDataCollectionDataStructures.h"
+#include "TaskTreeModel.h"
class TimedDataCollection : public QDialog
{
- Q_OBJECT
+ Q_OBJECT
public:
- TimedDataCollection(QWidget* parent = nullptr);
- ~TimedDataCollection();
+ TimedDataCollection(QWidget* parent = nullptr);
+ ~TimedDataCollection();
- void readTimedTaskFromFile(const QString& filePath);
+ void readTimedTaskFromFile(const QString& filePath);
public Q_SLOTS:
- void startScheduler();
- void stopScheduler();
- void subTaskCompleted(int status);
- void onBack2Origin();
+ void startScheduler();
+ void stopScheduler();
+ void subTaskCompleted(int status);
+ void onBack2Origin();
+
+private Q_SLOTS:
+ // 任务状态更新槽
+ void onTaskStarted(int taskId);
+ void onTaskFinished(int taskId, bool success);
+ void onSubTaskStarted(int taskId, int subTaskIndex);
+ void onSubTaskFinished(int taskId, int subTaskIndex);
+ void onTaskDataChanged(const TimedTask& task);
+ void onErrorOccurred(const QString& error);
+
+ // 树视图交互
+ void onTreeItemClicked(const QModelIndex& index);
+ void onTreeItemDoubleClicked(const QModelIndex& index);
+ void onTreeSelectionChanged();
Q_SIGNALS:
- void taskStarted(int taskId);
- void taskFinished(int taskId, bool success);
+ void taskStarted(int taskId);
+ void taskFinished(int taskId, bool success);
+ void subTaskStarted(int taskId, int subTaskIndex);
+ void subTaskFinished(int taskId, int subTaskIndex);
+ void errorOccurred(const QString& error);
- void subTaskStarted(int taskId, int subTaskIndex);
- void subTaskFinished(int taskId, int subTaskIndex);
- void errorOccurred(const QString& error);
+ void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
+ void camParm(int camType, int captureIntervalSeconds, QString folder);
+ void motorParm(QString pathLineFilePath);
+ void startRecordSignal(int camType);
- // 采集相关信号 (透传 TaskScheduler)
- void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
- void camParm(int camType, int captureIntervalSeconds, QString folder);
- void motorParm(QString pathLineFilePath);
- void startRecordSignal(int camType);
+ void switchHalogenLampSignal(int state);
+ void switchD65LampSignal(int state);
+ void switchSlrSignal(int state);
- void switchHalogenLampSignal(int state);
- void switchD65LampSignal(int state);
- void switchSlrSignal(int state);
-
- // 马达反馈的信号
- void sequenceCompleteSignal(int status);
- void Back2OriginSignal();
+ void sequenceCompleteSignal(int status);
+ void Back2OriginSignal();
private:
- Ui::TimedDataCollection_ui ui;
+ void setupUI();
+ void setupConnections();
+ void updateStatusBar();
+ void expandRunningTask();
- void writeRead();
+ Ui::TimedDataCollection_ui ui;
- QVector m_loadedTasks;
- TaskScheduler* m_scheduler;
+ TaskTreeModel* m_taskModel; // 任务树形模型
+ TaskScheduler* m_scheduler;
+
+ int m_currentTaskId = -1;
+ int m_currentSubTaskIndex = -1;
};
diff --git a/HPPA/TimedDataCollectionDataStructures.cpp b/HPPA/TimedDataCollectionDataStructures.cpp
index b903899..2d9d30f 100644
--- a/HPPA/TimedDataCollectionDataStructures.cpp
+++ b/HPPA/TimedDataCollectionDataStructures.cpp
@@ -220,7 +220,7 @@ void TaskExecutor::execute(const TimedTask& task)
m_task = task;
m_task.startTime = QDateTime::currentDateTime();
-
+
m_currentSubTaskIndex = 0;
m_isRunning = true;
@@ -331,6 +331,8 @@ void TaskExecutor::onSequenceComplete(int status)
break;
}
}
+
+ emit taskUpdated(m_task);
}
void TaskExecutor::onBack2Origin()
@@ -355,12 +357,15 @@ void TaskExecutor::onBack2Origin()
// 所有子任务完成
m_task.endTime = QDateTime::currentDateTime();
m_task.durationMinutes = (double)m_task.startTime.secsTo(m_task.endTime) / 60;
+ m_task.status = TaskStatus::Finished;
qDebug() << "TaskExecutor: task time consuming(Minutes): " << m_task.durationMinutes;
m_isRunning = false;
qDebug() << "TaskExecutor: All subtasks completed";
emit finished(true);
}
+
+ emit taskUpdated(m_task);
}
void TaskExecutor::onError(const QString& error)
@@ -383,6 +388,8 @@ void TaskExecutor::executeNextSubTask()
subTask.status = TaskStatus::Running;
subTask.startTime = QDateTime::currentDateTime();
+ emit taskUpdated(m_task);
+
QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast(subTask.type);
printMsgAndTime(tmp);
//printMsgAndTime("excute " + QString::number(m_currentSubTaskIndex) + " subTask: ");
@@ -589,6 +596,18 @@ void TaskScheduler::executeTask(TimedTask& task)
connect(this, &TaskScheduler::sequenceCompleteSignal, m_currentExecutor, &TaskExecutor::onSequenceComplete);
connect(this, &TaskScheduler::Back2OriginSignal, m_currentExecutor, &TaskExecutor::onBack2Origin);
+ connect(m_currentExecutor, &TaskExecutor::taskUpdated,
+ this, [this](const TimedTask& updatedTask) {
+ for (int i = 0; i < m_tasks.size(); ++i) {
+ if (m_tasks[i].id == updatedTask.id) {
+ m_tasks[i] = updatedTask;
+ break;
+ }
+ }
+ emit taskDataChanged(updatedTask);
+ });
+
+
// 开始执行
m_currentExecutor->execute(task);
}
diff --git a/HPPA/TimedDataCollectionDataStructures.h b/HPPA/TimedDataCollectionDataStructures.h
index 35f62e7..7571707 100644
--- a/HPPA/TimedDataCollectionDataStructures.h
+++ b/HPPA/TimedDataCollectionDataStructures.h
@@ -137,6 +137,7 @@ public:
signals:
void finished(bool success); // 任务完成
+ void taskUpdated(const TimedTask& task); // 确保有这个信号
void subTaskStarted(int subTaskIndex, SubTaskType type); // 子任务开始
void subTaskFinished(int subTaskIndex, SubTaskType type, bool success); // 子任务完成
void errorOccurred(const QString& error); // 错误发生
@@ -194,6 +195,7 @@ public:
signals:
void taskStarted(int taskId); // 任务开始
void taskFinished(int taskId, bool success); // 任务完成
+ void taskDataChanged(const TimedTask& task);
void schedulerStateChanged(bool running); // 调度器状态变化
diff --git a/HPPA/TimedDataCollection_ui.ui b/HPPA/TimedDataCollection_ui.ui
index de0255e..0c33fef 100644
--- a/HPPA/TimedDataCollection_ui.ui
+++ b/HPPA/TimedDataCollection_ui.ui
@@ -6,7 +6,7 @@
0
0
- 1160
+ 1008
576
@@ -18,229 +18,11 @@
-
-
-
- false
-
-
- true
-
-
-
- 任务
-
-
-
-
- 计划时间
-
-
-
-
- 开始时间
-
-
-
-
- 结束时间
-
-
-
-
- 耗时
-
-
-
-
- 状态
-
-
-
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- a
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
- b
-
-
-
- -
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- a
-
-
- -
-
- b
-
-
-
- -
-
- 3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- a
-
-
- -
-
- b
-
-
-
- -
-
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- a
-
-
- -
-
- b
-
-
-
- -
-
- 5
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- a
-
-
- -
-
- b
-
-
-
-
-
- -
-
-
- 1
-
-
-
-
+
- -
-
-
- 添加总计划任务
-
-
-
-
@@ -248,27 +30,69 @@
- -
+
-
添加子任务
- -
-
+
-
+
+
+ 添加总计划任务
+
+
- -
+
-
删除子任务
- -
-
+
-
+
- 运行
+ 停止调度
+
+
+
+ -
+
+
+ -
+
+
+ 刷新
+
+
+
+ -
+
+
+ 开始调度
+
+
+
+ -
+
+
+ 展开
+
+
+
+ -
+
+
+ 折叠
+
+
+
+ -
+
+
+ TextLabel