add,计划采集12:

在界面中显示任务和其子任务,并实时更新它们的状态;
This commit is contained in:
tangchao0503
2026-06-12 18:29:46 +08:00
parent 8329c00165
commit d9f1ed922b
9 changed files with 1243 additions and 390 deletions

View File

@ -164,6 +164,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="TabManager.cpp" /> <ClCompile Include="TabManager.cpp" />
<ClCompile Include="TaskTreeModel.cpp" />
<ClCompile Include="TimedDataCollection.cpp" /> <ClCompile Include="TimedDataCollection.cpp" />
<ClCompile Include="TimedDataCollectionDataStructures.cpp" /> <ClCompile Include="TimedDataCollectionDataStructures.cpp" />
<ClCompile Include="TwoMotorControl.cpp" /> <ClCompile Include="TwoMotorControl.cpp" />
@ -262,6 +263,7 @@
<QtMoc Include="SingleLensReflexCameraWindow.h" /> <QtMoc Include="SingleLensReflexCameraWindow.h" />
<QtMoc Include="TimedDataCollection.h" /> <QtMoc Include="TimedDataCollection.h" />
<QtMoc Include="TimedDataCollectionDataStructures.h" /> <QtMoc Include="TimedDataCollectionDataStructures.h" />
<QtMoc Include="TaskTreeModel.h" />
<ClInclude Include="utility_tc.h" /> <ClInclude Include="utility_tc.h" />
<QtMoc Include="aboutWindow.h" /> <QtMoc Include="aboutWindow.h" />
<ClInclude Include="hppaConfigFile.h" /> <ClInclude Include="hppaConfigFile.h" />

View File

@ -247,6 +247,9 @@
<ClCompile Include="PowerControl3D.cpp"> <ClCompile Include="PowerControl3D.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="TaskTreeModel.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="fileOperation.h"> <QtMoc Include="fileOperation.h">
@ -405,6 +408,9 @@
<QtMoc Include="PowerControl3D.h"> <QtMoc Include="PowerControl3D.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="TaskTreeModel.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="imageProcessor.h"> <ClInclude Include="imageProcessor.h">

584
HPPA/TaskTreeModel.cpp Normal file
View File

@ -0,0 +1,584 @@
#include "TaskTreeModel.h"
#include <QBrush>
#include <QFont>
#include <QIcon>
// ==================== 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<TreeNode*>(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<TreeNode*>(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<TreeNode*>(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<TreeNode*>(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<TreeNode*>(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<int>(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<TimedTask>& 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<int>(minutes) / 60;
int mins = static_cast<int>(minutes) % 60;
int secs = static_cast<int>((minutes - static_cast<int>(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);
}

106
HPPA/TaskTreeModel.h Normal file
View File

@ -0,0 +1,106 @@
#pragma once
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#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<TreeNode*> 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<TimedTask>& 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<TimedTask>& tasks() { return m_tasks; }
const QVector<TimedTask>& 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<TimedTask> m_tasks;
};

View File

@ -1,169 +1,462 @@
#include "TimedDataCollection.h" #include "TimedDataCollection.h"
#include <QDateTime> #include <QVBoxLayout>
#include <QDebug> #include <QHBoxLayout>
#include <QMessageBox>
#include <QGroupBox>
#include <QSplitter>
TimedDataCollection::TimedDataCollection(QWidget* parent) TimedDataCollection::TimedDataCollection(QWidget* parent)
: QDialog(parent) : QDialog(parent)
, m_scheduler(nullptr)
{ {
ui.setupUi(this); ui.setupUi(this);
ui.treeWidget->setDragEnabled(true); // 启用拖拽 setupUI();
ui.treeWidget->setAcceptDrops(true); // 接受拖放
ui.treeWidget->setDropIndicatorShown(true); // 显示插入位置指示线
ui.treeWidget->setDragDropMode(QAbstractItemView::InternalMove); // 内部移动
// 初始化调度器 // 初始化调度器
m_scheduler = new TaskScheduler(this); m_scheduler = new TaskScheduler(this);
// 连接调度器信号 setupConnections();
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);
// 采集相关信号透传 // 读取任务文件
connect(m_scheduler, &TaskScheduler::hyperCamParm, this, &TimedDataCollection::hyperCamParm); readTimedTaskFromFile("D:/0tmp/3Dtest/task.json");
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); void TimedDataCollection::setupUI()
connect(m_scheduler, &TaskScheduler::switchD65LampSignal, this, &TimedDataCollection::switchD65LampSignal); {
connect(m_scheduler, &TaskScheduler::switchSlrSignal, this, &TimedDataCollection::switchSlrSignal); // 设置窗口大小
//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() TimedDataCollection::~TimedDataCollection()
{ {
if (m_scheduler) { if (m_scheduler) {
m_scheduler->stop(); 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_currentSubTaskIndex = subTaskIndex;
m_scheduler->start();
} 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_taskModel->updateSubTaskStatus(taskId, subTaskIndex, TaskStatus::Finished);
m_scheduler->stop();
} 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<TreeNode*>(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<TreeNode*>(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<int>(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<int>(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) void TimedDataCollection::readTimedTaskFromFile(const QString& filePath)
{ {
// 从文件读取 QVector<TimedTask> loadedTasks;
if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, m_loadedTasks)) { if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, loadedTasks)) {
qDebug() << "Tasks loaded successfully, count:" << m_loadedTasks.size(); qDebug() << "Tasks loaded successfully, count:" << 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;
}
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个定时任务测试 if (m_scheduler) {
QVector<TimedTask> tasks; m_scheduler->loadTasks(m_taskModel->tasks());
m_scheduler->start();
for (int i = 0; i < 2; ++i) { ui.statusLabel->setText(QString::fromLocal8Bit("调度器已启动,等待任务执行..."));
TimedTask task; ui.statusLabel->setStyleSheet(
task.id = i + 1; "QLabel {"
task.scheduledTime = QDateTime::currentDateTime().addDays(i + 1); " background-color: #fff3e0;"
task.savePath = QString("D:/0tmp/data"); " border: 1px solid #ff9800;"
task.status = TaskStatus::Waiting; " border-radius: 4px;"
" padding: 8px;"
// 创建4种子任务 " color: #e65100;"
QVector<SubTaskType> types = { "}");
SubTaskType::HyperSpectual400_1000nm, }
SubTaskType::HyperSpectual1000_1700nm, }
SubTaskType::SingleLensReflex,
SubTaskType::DepthCamera void TimedDataCollection::stopScheduler()
}; {
if (m_scheduler) {
for (int j = 0; j < types.size(); ++j) { m_scheduler->stop();
SubTask subTask;
subTask.type = types[j]; m_currentTaskId = -1;
subTask.startTime = task.scheduledTime.addSecs(j * 3600); m_currentSubTaskIndex = -1;
subTask.endTime = subTask.startTime.addSecs(1800);
subTask.durationMinutes = 1800; ui.statusLabel->setText(QString::fromLocal8Bit("调度器已停止"));
subTask.estimatedDurationMinutes = 1800; ui.statusLabel->setStyleSheet(
subTask.pathLineFilePath = QString("D:/0tmp/3Dtest/pathLine/%1.RecordLine3").arg(j); "QLabel {"
subTask.status = TaskStatus::Waiting; " background-color: #fce4ec;"
" border: 1px solid #e91e63;"
// 根据类型设置特有属性 " border-radius: 4px;"
if (subTask.type == SubTaskType::HyperSpectual400_1000nm || " padding: 8px;"
subTask.type == SubTaskType::HyperSpectual1000_1700nm) { " color: #880e4f;"
subTask.frameRate = 30.0; "}");
subTask.exposureTime = 1; }
} }
if (subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
subTask.defaultRenderBand = 1200; void TimedDataCollection::subTaskCompleted(int status)
} {
if (subTask.type == SubTaskType::SingleLensReflex || emit sequenceCompleteSignal(status);
subTask.type == SubTaskType::DepthCamera) { }
subTask.captureIntervalSeconds = 5;
} void TimedDataCollection::onBack2Origin()
{
task.subTasks.append(subTask); emit Back2OriginSignal();
}
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<TimedTask> 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;
} }

View File

@ -1,56 +1,73 @@
#pragma once #pragma once
#include <QDialog> #include <QDialog>
#include <QNetworkRequest> #include <QTreeView>
#include <QNetworkReply> #include <QHeaderView>
#include <QNetworkAccessManager> #include <QLabel>
#include "ui_TimedDataCollection_ui.h" #include "ui_TimedDataCollection_ui.h"
#include "TimedDataCollectionDataStructures.h" #include "TimedDataCollectionDataStructures.h"
#include "TaskTreeModel.h"
class TimedDataCollection : public QDialog class TimedDataCollection : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
TimedDataCollection(QWidget* parent = nullptr); TimedDataCollection(QWidget* parent = nullptr);
~TimedDataCollection(); ~TimedDataCollection();
void readTimedTaskFromFile(const QString& filePath); void readTimedTaskFromFile(const QString& filePath);
public Q_SLOTS: public Q_SLOTS:
void startScheduler(); void startScheduler();
void stopScheduler(); void stopScheduler();
void subTaskCompleted(int status); void subTaskCompleted(int status);
void onBack2Origin(); 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: Q_SIGNALS:
void taskStarted(int taskId); void taskStarted(int taskId);
void taskFinished(int taskId, bool success); 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 hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
void subTaskFinished(int taskId, int subTaskIndex); void camParm(int camType, int captureIntervalSeconds, QString folder);
void errorOccurred(const QString& error); void motorParm(QString pathLineFilePath);
void startRecordSignal(int camType);
// 采集相关信号 (透传 TaskScheduler) void switchHalogenLampSignal(int state);
void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName); void switchD65LampSignal(int state);
void camParm(int camType, int captureIntervalSeconds, QString folder); void switchSlrSignal(int state);
void motorParm(QString pathLineFilePath);
void startRecordSignal(int camType);
void switchHalogenLampSignal(int state); void sequenceCompleteSignal(int status);
void switchD65LampSignal(int state); void Back2OriginSignal();
void switchSlrSignal(int state);
// 马达反馈的信号
void sequenceCompleteSignal(int status);
void Back2OriginSignal();
private: private:
Ui::TimedDataCollection_ui ui; void setupUI();
void setupConnections();
void updateStatusBar();
void expandRunningTask();
void writeRead(); Ui::TimedDataCollection_ui ui;
QVector<TimedTask> m_loadedTasks; TaskTreeModel* m_taskModel; // 任务树形模型
TaskScheduler* m_scheduler; TaskScheduler* m_scheduler;
int m_currentTaskId = -1;
int m_currentSubTaskIndex = -1;
}; };

View File

@ -331,6 +331,8 @@ void TaskExecutor::onSequenceComplete(int status)
break; break;
} }
} }
emit taskUpdated(m_task);
} }
void TaskExecutor::onBack2Origin() void TaskExecutor::onBack2Origin()
@ -355,12 +357,15 @@ void TaskExecutor::onBack2Origin()
// 所有子任务完成 // 所有子任务完成
m_task.endTime = QDateTime::currentDateTime(); m_task.endTime = QDateTime::currentDateTime();
m_task.durationMinutes = (double)m_task.startTime.secsTo(m_task.endTime) / 60; 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; qDebug() << "TaskExecutor: task time consuming(Minutes): " << m_task.durationMinutes;
m_isRunning = false; m_isRunning = false;
qDebug() << "TaskExecutor: All subtasks completed"; qDebug() << "TaskExecutor: All subtasks completed";
emit finished(true); emit finished(true);
} }
emit taskUpdated(m_task);
} }
void TaskExecutor::onError(const QString& error) void TaskExecutor::onError(const QString& error)
@ -383,6 +388,8 @@ void TaskExecutor::executeNextSubTask()
subTask.status = TaskStatus::Running; subTask.status = TaskStatus::Running;
subTask.startTime = QDateTime::currentDateTime(); subTask.startTime = QDateTime::currentDateTime();
emit taskUpdated(m_task);
QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast<int>(subTask.type); QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast<int>(subTask.type);
printMsgAndTime(tmp); printMsgAndTime(tmp);
//printMsgAndTime("excute " + QString::number(m_currentSubTaskIndex) + " subTask: "); //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::sequenceCompleteSignal, m_currentExecutor, &TaskExecutor::onSequenceComplete);
connect(this, &TaskScheduler::Back2OriginSignal, m_currentExecutor, &TaskExecutor::onBack2Origin); 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); m_currentExecutor->execute(task);
} }

View File

@ -137,6 +137,7 @@ public:
signals: signals:
void finished(bool success); // 任务完成 void finished(bool success); // 任务完成
void taskUpdated(const TimedTask& task); // 确保有这个信号
void subTaskStarted(int subTaskIndex, SubTaskType type); // 子任务开始 void subTaskStarted(int subTaskIndex, SubTaskType type); // 子任务开始
void subTaskFinished(int subTaskIndex, SubTaskType type, bool success); // 子任务完成 void subTaskFinished(int subTaskIndex, SubTaskType type, bool success); // 子任务完成
void errorOccurred(const QString& error); // 错误发生 void errorOccurred(const QString& error); // 错误发生
@ -194,6 +195,7 @@ public:
signals: signals:
void taskStarted(int taskId); // 任务开始 void taskStarted(int taskId); // 任务开始
void taskFinished(int taskId, bool success); // 任务完成 void taskFinished(int taskId, bool success); // 任务完成
void taskDataChanged(const TimedTask& task);
void schedulerStateChanged(bool running); // 调度器状态变化 void schedulerStateChanged(bool running); // 调度器状态变化

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1160</width> <width>1008</width>
<height>576</height> <height>576</height>
</rect> </rect>
</property> </property>
@ -18,229 +18,11 @@
<widget class="QWidget" name="widget" native="true"> <widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTreeWidget" name="treeWidget"> <widget class="QTreeView" name="taskTreeView"/>
<property name="allColumnsShowFocus">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>任务</string>
</property>
</column>
<column>
<property name="text">
<string>计划时间</string>
</property>
</column>
<column>
<property name="text">
<string>开始时间</string>
</property>
</column>
<column>
<property name="text">
<string>结束时间</string>
</property>
</column>
<column>
<property name="text">
<string>耗时</string>
</property>
</column>
<column>
<property name="text">
<string>状态</string>
</property>
</column>
<item>
<property name="text">
<string>1</string>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<item>
<property name="text">
<string>a</string>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
</item>
<item>
<property name="text">
<string>b</string>
</property>
</item>
</item>
<item>
<property name="text">
<string>2</string>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<item>
<property name="text">
<string>a</string>
</property>
</item>
<item>
<property name="text">
<string>b</string>
</property>
</item>
</item>
<item>
<property name="text">
<string>3</string>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<item>
<property name="text">
<string>a</string>
</property>
</item>
<item>
<property name="text">
<string>b</string>
</property>
</item>
</item>
<item>
<property name="text">
<string>4</string>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<item>
<property name="text">
<string>a</string>
</property>
</item>
<item>
<property name="text">
<string>b</string>
</property>
</item>
</item>
<item>
<property name="text">
<string>5</string>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="text">
<string/>
</property>
<item>
<property name="text">
<string>a</string>
</property>
</item>
<item>
<property name="text">
<string>b</string>
</property>
</item>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QPushButton" name="addToptask_btn">
<property name="text">
<string>添加总计划任务</string>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QPushButton" name="delToptask_btn"> <widget class="QPushButton" name="delToptask_btn">
<property name="text"> <property name="text">
@ -248,27 +30,69 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<widget class="QPushButton" name="addSubtask_btn"> <widget class="QPushButton" name="addSubtask_btn">
<property name="text"> <property name="text">
<string>添加子任务</string> <string>添加子任务</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="1" column="0">
<widget class="QComboBox" name="comboBox"/> <widget class="QPushButton" name="addToptask_btn">
<property name="text">
<string>添加总计划任务</string>
</property>
</widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QPushButton" name="delSubtask_btn"> <widget class="QPushButton" name="delSubtask_btn">
<property name="text"> <property name="text">
<string>删除子任务</string> <string>删除子任务</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="2"> <item row="5" column="2">
<widget class="QPushButton" name="run_btn"> <widget class="QPushButton" name="stop_btn">
<property name="text"> <property name="text">
<string>运行</string> <string>停止调度</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboBox"/>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="refresh_btn">
<property name="text">
<string>刷新</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="start_btn">
<property name="text">
<string>开始调度</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="expandAll_btn">
<property name="text">
<string>展开</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="collapseAll_btn">
<property name="text">
<string>折叠</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>TextLabel</string>
</property> </property>
</widget> </widget>
</item> </item>