2026-06-02 18:22:02 +08:00
|
|
|
|
#include "TimedDataCollectionDataStructures.h"
|
2026-06-02 16:33:17 +08:00
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
|
#include <QFile>
|
|
|
|
|
|
#include <QTextStream>
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 公共接口实现 ====================
|
|
|
|
|
|
|
|
|
|
|
|
bool TimedDataCollectionDataStructuresReaderWriter::saveTasksToFile(const QString& filePath, const QVector<TimedTask>& tasks)
|
|
|
|
|
|
{
|
|
|
|
|
|
QJsonObject root;
|
|
|
|
|
|
root["version"] = "1.0";
|
|
|
|
|
|
root["taskCount"] = tasks.size();
|
|
|
|
|
|
|
|
|
|
|
|
QJsonArray tasksArray;
|
|
|
|
|
|
for (const auto& task : tasks) {
|
|
|
|
|
|
tasksArray.append(timedTaskToJson(task));
|
|
|
|
|
|
}
|
|
|
|
|
|
root["tasks"] = tasksArray;
|
|
|
|
|
|
|
|
|
|
|
|
QJsonDocument doc(root);
|
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
|
|
|
|
qWarning() << "Failed to open file for writing:" << filePath;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QTextStream out(&file);
|
|
|
|
|
|
//out.setEncoding(QStringConverter::Utf8);
|
|
|
|
|
|
out << doc.toJson(QJsonDocument::Indented);
|
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(const QString& filePath, QVector<TimedTask>& tasks)
|
|
|
|
|
|
{
|
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
|
|
qWarning() << "Failed to open file for reading:" << filePath;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QByteArray jsonData = file.readAll();
|
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
|
|
QJsonParseError parseError;
|
|
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);
|
|
|
|
|
|
if (parseError.error != QJsonParseError::NoError) {
|
|
|
|
|
|
qWarning() << "JSON parse error:" << parseError.errorString();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!doc.isObject()) {
|
|
|
|
|
|
qWarning() << "Invalid JSON root object";
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QJsonObject root = doc.object();
|
|
|
|
|
|
QJsonArray tasksArray = root["tasks"].toArray();
|
|
|
|
|
|
tasks.clear();
|
|
|
|
|
|
tasks.reserve(tasksArray.size());
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& taskValue : tasksArray) {
|
|
|
|
|
|
if (!taskValue.isObject()) continue;
|
|
|
|
|
|
TimedTask task;
|
|
|
|
|
|
if (jsonToTimedTask(taskValue.toObject(), task)) {
|
|
|
|
|
|
tasks.append(task);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 枚举转换函数 ====================
|
|
|
|
|
|
|
|
|
|
|
|
QString TimedDataCollectionDataStructuresReaderWriter::taskStatusToString(TaskStatus status)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
|
case TaskStatus::Waiting: return "Waiting";
|
|
|
|
|
|
case TaskStatus::Running: return "Running";
|
|
|
|
|
|
case TaskStatus::Finished: return "Finished";
|
|
|
|
|
|
default: return "Waiting";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TaskStatus TimedDataCollectionDataStructuresReaderWriter::stringToTaskStatus(const QString& str)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (str == "Running") return TaskStatus::Running;
|
|
|
|
|
|
if (str == "Finished") return TaskStatus::Finished;
|
|
|
|
|
|
return TaskStatus::Waiting;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString TimedDataCollectionDataStructuresReaderWriter::subTaskTypeToString(SubTaskType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case SubTaskType::HyperSpectual400_1000nm: return "HyperSpectual400_1000nm";
|
|
|
|
|
|
case SubTaskType::HyperSpectual1000_1700nm: return "HyperSpectual1000_1700nm";
|
|
|
|
|
|
case SubTaskType::SingleLensReflex: return "SingleLensReflex";
|
|
|
|
|
|
case SubTaskType::DepthCamera: return "DepthCamera";
|
|
|
|
|
|
default: return "Unknown";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SubTaskType TimedDataCollectionDataStructuresReaderWriter::stringToSubTaskType(const QString& str)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (str == "HyperSpectual400_1000nm") return SubTaskType::HyperSpectual400_1000nm;
|
|
|
|
|
|
if (str == "HyperSpectual1000_1700nm") return SubTaskType::HyperSpectual1000_1700nm;
|
|
|
|
|
|
if (str == "SingleLensReflex") return SubTaskType::SingleLensReflex;
|
|
|
|
|
|
if (str == "DepthCamera") return SubTaskType::DepthCamera;
|
|
|
|
|
|
return SubTaskType::SingleLensReflex;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== SubTask序列化 ====================
|
|
|
|
|
|
|
|
|
|
|
|
QJsonObject TimedDataCollectionDataStructuresReaderWriter::subTaskToJson(const SubTask& subTask)
|
|
|
|
|
|
{
|
|
|
|
|
|
QJsonObject obj;
|
|
|
|
|
|
obj["type"] = subTaskTypeToString(subTask.type);
|
|
|
|
|
|
obj["startTime"] = subTask.startTime.toString(Qt::ISODate);
|
|
|
|
|
|
obj["endTime"] = subTask.endTime.toString(Qt::ISODate);
|
|
|
|
|
|
obj["durationSeconds"] = subTask.durationSeconds;
|
|
|
|
|
|
obj["estimatedDurationSeconds"] = subTask.estimatedDurationSeconds;
|
|
|
|
|
|
obj["pathLineFilePath"] = subTask.pathLineFilePath;
|
|
|
|
|
|
obj["status"] = taskStatusToString(subTask.status);
|
|
|
|
|
|
obj["frameRate"] = subTask.frameRate;
|
|
|
|
|
|
obj["exposureTime"] = subTask.exposureTime;
|
|
|
|
|
|
obj["defaultRenderBand"] = subTask.defaultRenderBand;
|
|
|
|
|
|
obj["captureIntervalSeconds"] = subTask.captureIntervalSeconds;
|
|
|
|
|
|
return obj;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TimedDataCollectionDataStructuresReaderWriter::jsonToSubTask(const QJsonObject& json, SubTask& subTask)
|
|
|
|
|
|
{
|
|
|
|
|
|
subTask.type = stringToSubTaskType(json["type"].toString());
|
|
|
|
|
|
subTask.startTime = QDateTime::fromString(json["startTime"].toString(), Qt::ISODate);
|
|
|
|
|
|
subTask.endTime = QDateTime::fromString(json["endTime"].toString(), Qt::ISODate);
|
|
|
|
|
|
subTask.durationSeconds = json["durationSeconds"].toInt();
|
|
|
|
|
|
subTask.estimatedDurationSeconds = json["estimatedDurationSeconds"].toInt();
|
|
|
|
|
|
subTask.pathLineFilePath = json["pathLineFilePath"].toString();
|
|
|
|
|
|
subTask.status = stringToTaskStatus(json["status"].toString());
|
|
|
|
|
|
subTask.frameRate = json["frameRate"].toDouble();
|
|
|
|
|
|
subTask.exposureTime = json["exposureTime"].toDouble();
|
|
|
|
|
|
subTask.defaultRenderBand = json["defaultRenderBand"].toInt();
|
|
|
|
|
|
subTask.captureIntervalSeconds = json["captureIntervalSeconds"].toInt();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== TimedTask序列化 ====================
|
|
|
|
|
|
|
|
|
|
|
|
QJsonObject TimedDataCollectionDataStructuresReaderWriter::timedTaskToJson(const TimedTask& task)
|
|
|
|
|
|
{
|
|
|
|
|
|
QJsonObject obj;
|
|
|
|
|
|
obj["id"] = task.id;
|
|
|
|
|
|
obj["scheduledTime"] = task.scheduledTime.toString(Qt::ISODate);
|
|
|
|
|
|
obj["startTime"] = task.startTime.toString(Qt::ISODate);
|
|
|
|
|
|
obj["endTime"] = task.endTime.toString(Qt::ISODate);
|
|
|
|
|
|
obj["durationSeconds"] = task.durationSeconds;
|
|
|
|
|
|
obj["savePath"] = task.savePath;
|
|
|
|
|
|
obj["status"] = taskStatusToString(task.status);
|
2026-06-11 15:27:02 +08:00
|
|
|
|
obj["HalogenLampPreheatingTime_Minute"] = task.HalogenLampPreheatingTime_Minute;
|
2026-06-02 16:33:17 +08:00
|
|
|
|
|
|
|
|
|
|
QJsonArray subTasksArray;
|
|
|
|
|
|
for (const auto& subTask : task.subTasks) {
|
|
|
|
|
|
subTasksArray.append(subTaskToJson(subTask));
|
|
|
|
|
|
}
|
|
|
|
|
|
obj["subTasks"] = subTasksArray;
|
|
|
|
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TimedDataCollectionDataStructuresReaderWriter::jsonToTimedTask(const QJsonObject& json, TimedTask& task)
|
|
|
|
|
|
{
|
|
|
|
|
|
task.id = json["id"].toInt();
|
|
|
|
|
|
task.scheduledTime = QDateTime::fromString(json["scheduledTime"].toString(), Qt::ISODate);
|
|
|
|
|
|
task.startTime = QDateTime::fromString(json["startTime"].toString(), Qt::ISODate);
|
|
|
|
|
|
task.endTime = QDateTime::fromString(json["endTime"].toString(), Qt::ISODate);
|
|
|
|
|
|
task.durationSeconds = json["durationSeconds"].toInt();
|
|
|
|
|
|
task.savePath = json["savePath"].toString();
|
|
|
|
|
|
task.status = stringToTaskStatus(json["status"].toString());
|
2026-06-11 15:27:02 +08:00
|
|
|
|
task.HalogenLampPreheatingTime_Minute = json["HalogenLampPreheatingTime_Minute"].toDouble();
|
2026-06-02 16:33:17 +08:00
|
|
|
|
|
|
|
|
|
|
QJsonArray subTasksArray = json["subTasks"].toArray();
|
|
|
|
|
|
task.subTasks.clear();
|
|
|
|
|
|
task.subTasks.reserve(subTasksArray.size());
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& subTaskValue : subTasksArray) {
|
|
|
|
|
|
if (!subTaskValue.isObject()) continue;
|
|
|
|
|
|
SubTask subTask;
|
|
|
|
|
|
if (jsonToSubTask(subTaskValue.toObject(), subTask)) {
|
|
|
|
|
|
task.subTasks.append(subTask);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== TaskExecutor 实现 ====================
|
|
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
TaskExecutor::TaskExecutor(QObject* parent)
|
2026-06-02 18:22:02 +08:00
|
|
|
|
: QObject(parent)
|
|
|
|
|
|
, m_currentSubTaskIndex(0)
|
|
|
|
|
|
, m_isRunning(false)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TaskExecutor::~TaskExecutor()
|
|
|
|
|
|
{
|
|
|
|
|
|
stop();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskExecutor::execute(const TimedTask& task)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_isRunning) {
|
|
|
|
|
|
qWarning() << "TaskExecutor: Already running, ignoring execute request";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_task = task;
|
|
|
|
|
|
m_currentSubTaskIndex = 0;
|
|
|
|
|
|
m_isRunning = true;
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "TaskExecutor: Starting task" << task.id;
|
|
|
|
|
|
|
2026-06-10 18:10:47 +08:00
|
|
|
|
// 打开卤素灯预热
|
|
|
|
|
|
emit switchHalogenLampSignal(1);
|
2026-06-11 15:27:02 +08:00
|
|
|
|
printMsgAndTime("open HalogenLamp");
|
|
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
makeFolder(m_task.savePath);
|
2026-06-10 18:10:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 开始执行第一个子任务
|
2026-06-11 15:27:02 +08:00
|
|
|
|
double sleepTimeSecond = m_task.HalogenLampPreheatingTime_Minute * 60;
|
|
|
|
|
|
QTimer::singleShot(sleepTimeSecond *1000, this, &TaskExecutor::executeNextSubTask);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskExecutor::printMsgAndTime(QString msg)
|
|
|
|
|
|
{
|
|
|
|
|
|
QDateTime now = QDateTime::currentDateTime();
|
|
|
|
|
|
QString timeString = now.toString("yyyy-MM-dd hh:mm:ss.zzz");
|
|
|
|
|
|
qDebug() << msg + " time:" << timeString;
|
2026-06-02 18:22:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskExecutor::stop()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_isRunning) return;
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "TaskExecutor: Stopping task" << m_task.id;
|
|
|
|
|
|
|
|
|
|
|
|
m_isRunning = false;
|
|
|
|
|
|
emit finished(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
void TaskExecutor::makeFolder(QString savePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
QDir dir(savePath);
|
|
|
|
|
|
if (!dir.exists()) {
|
|
|
|
|
|
if (dir.mkpath(".")) {
|
|
|
|
|
|
qDebug() << "TaskExecutor: Created data folder:" << savePath;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
qWarning() << "TaskExecutor: Failed to create data folder:" << savePath;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
qDebug() << "TaskExecutor: Data folder already exists:" << savePath;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString TaskExecutor::makeSubTaskDataFolder(QString suffix)
|
|
|
|
|
|
{
|
|
|
|
|
|
QString dateStr = QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss");
|
|
|
|
|
|
QString folderPath = m_task.savePath + QDir::separator() + dateStr + "_" + suffix;
|
|
|
|
|
|
makeFolder(folderPath);
|
|
|
|
|
|
|
|
|
|
|
|
return folderPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 18:22:02 +08:00
|
|
|
|
void TaskExecutor::onSequenceComplete(int status)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_isRunning) return;
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "TaskExecutor: Sequence complete, status:" << status;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新当前子任务状态
|
|
|
|
|
|
if (m_currentSubTaskIndex < m_task.subTasks.size()) {
|
|
|
|
|
|
SubTask& subTask = m_task.subTasks[m_currentSubTaskIndex];
|
|
|
|
|
|
subTask.status = (status == 0) ? TaskStatus::Finished : TaskStatus::Waiting;
|
|
|
|
|
|
|
|
|
|
|
|
emit subTaskFinished(m_currentSubTaskIndex, subTask.type, (status == 0));
|
|
|
|
|
|
}
|
2026-06-10 18:10:47 +08:00
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
switch (m_task.subTasks[m_currentSubTaskIndex].type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SubTaskType::SingleLensReflex:
|
|
|
|
|
|
{
|
|
|
|
|
|
emit switchD65LampSignal(0);
|
2026-06-11 15:27:02 +08:00
|
|
|
|
emit switchSlrSignal(0);
|
2026-06-10 18:10:47 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case SubTaskType::DepthCamera:
|
|
|
|
|
|
{
|
|
|
|
|
|
emit switchD65LampSignal(0);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断下一次的任务是否为高光谱任务,如果不是关闭卤素灯
|
|
|
|
|
|
int nestSubTaskIndex = m_currentSubTaskIndex + 1;
|
|
|
|
|
|
if (nestSubTaskIndex >= m_task.subTasks.size())
|
|
|
|
|
|
{
|
|
|
|
|
|
emit switchHalogenLampSignal(0);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
switch (m_task.subTasks[nestSubTaskIndex].type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SubTaskType::SingleLensReflex:
|
|
|
|
|
|
{
|
|
|
|
|
|
emit switchHalogenLampSignal(0);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case SubTaskType::DepthCamera:
|
|
|
|
|
|
{
|
|
|
|
|
|
emit switchHalogenLampSignal(0);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-04 18:01:22 +08:00
|
|
|
|
}
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
2026-06-04 18:01:22 +08:00
|
|
|
|
void TaskExecutor::onBack2Origin()
|
|
|
|
|
|
{
|
2026-06-02 18:22:02 +08:00
|
|
|
|
// 检查是否还有更多子任务
|
|
|
|
|
|
m_currentSubTaskIndex++;
|
|
|
|
|
|
if (m_currentSubTaskIndex < m_task.subTasks.size()) {
|
|
|
|
|
|
// 执行下一个子任务
|
2026-06-11 15:27:02 +08:00
|
|
|
|
if(m_task.subTasks[m_currentSubTaskIndex].type== SubTaskType::SingleLensReflex)
|
|
|
|
|
|
{
|
|
|
|
|
|
printMsgAndTime("Slr task,for weak up,please wait 135 seconds!");
|
|
|
|
|
|
emit switchSlrSignal(0);
|
|
|
|
|
|
|
|
|
|
|
|
QTimer::singleShot(135*1000, this, &TaskExecutor::executeNextSubTask);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
QTimer::singleShot(1000, this, &TaskExecutor::executeNextSubTask);
|
|
|
|
|
|
}
|
2026-06-04 18:01:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
else {
|
2026-06-02 18:22:02 +08:00
|
|
|
|
// 所有子任务完成
|
|
|
|
|
|
m_isRunning = false;
|
|
|
|
|
|
qDebug() << "TaskExecutor: All subtasks completed";
|
|
|
|
|
|
emit finished(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskExecutor::onError(const QString& error)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_isRunning) return;
|
|
|
|
|
|
|
|
|
|
|
|
qWarning() << "TaskExecutor: Error occurred:" << error;
|
|
|
|
|
|
m_isRunning = false;
|
|
|
|
|
|
emit errorOccurred(error);
|
|
|
|
|
|
emit finished(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskExecutor::executeNextSubTask()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_isRunning || m_currentSubTaskIndex >= m_task.subTasks.size()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SubTask& subTask = m_task.subTasks[m_currentSubTaskIndex];
|
|
|
|
|
|
subTask.status = TaskStatus::Running;
|
|
|
|
|
|
subTask.startTime = QDateTime::currentDateTime();
|
|
|
|
|
|
|
2026-06-11 15:27:02 +08:00
|
|
|
|
QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast<int>(subTask.type);
|
|
|
|
|
|
printMsgAndTime(tmp);
|
|
|
|
|
|
//printMsgAndTime("excute " + QString::number(m_currentSubTaskIndex) + " subTask: ");
|
|
|
|
|
|
|
|
|
|
|
|
//qDebug() << "TaskExecutor: Starting subtask" << m_currentSubTaskIndex
|
|
|
|
|
|
// << "type:" << static_cast<int>(subTask.type);
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
|
|
|
|
|
emit subTaskStarted(m_currentSubTaskIndex, subTask.type);
|
2026-06-04 10:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
emit motorParm(subTask.pathLineFilePath);
|
|
|
|
|
|
|
|
|
|
|
|
int camType;
|
|
|
|
|
|
switch (subTask.type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SubTaskType::HyperSpectual400_1000nm:
|
|
|
|
|
|
{
|
|
|
|
|
|
camType = 0;
|
|
|
|
|
|
emit hyperCamParm(camType, subTask.frameRate, subTask.exposureTime, makeSubTaskDataFolder("L"), "test");
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case SubTaskType::HyperSpectual1000_1700nm:
|
|
|
|
|
|
{
|
|
|
|
|
|
camType = 1;
|
|
|
|
|
|
emit hyperCamParm(camType, subTask.frameRate, subTask.exposureTime, makeSubTaskDataFolder("NIR"), "test");
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case SubTaskType::SingleLensReflex:
|
|
|
|
|
|
{
|
|
|
|
|
|
camType = 2;
|
|
|
|
|
|
emit camParm(camType, 3, makeSubTaskDataFolder("SLR"));
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
2026-06-10 18:10:47 +08:00
|
|
|
|
emit switchD65LampSignal(1);
|
|
|
|
|
|
|
2026-06-11 15:27:02 +08:00
|
|
|
|
|
|
|
|
|
|
emit switchSlrSignal(1);
|
2026-06-10 18:10:47 +08:00
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case SubTaskType::DepthCamera:
|
|
|
|
|
|
{
|
|
|
|
|
|
camType = 3;
|
|
|
|
|
|
emit camParm(camType, 3, makeSubTaskDataFolder("DepthCamera"));
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
2026-06-10 18:10:47 +08:00
|
|
|
|
emit switchD65LampSignal(1);
|
|
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
break;
|
2026-06-02 18:22:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
emit startRecordSignal(camType);
|
2026-06-02 18:22:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== TaskScheduler 实现 ====================
|
|
|
|
|
|
|
|
|
|
|
|
TaskScheduler::TaskScheduler(QObject* parent)
|
|
|
|
|
|
: QObject(parent)
|
|
|
|
|
|
, m_timer(nullptr)
|
|
|
|
|
|
, m_currentExecutor(nullptr)
|
|
|
|
|
|
, m_currentTaskId(-1)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TaskScheduler::~TaskScheduler()
|
|
|
|
|
|
{
|
|
|
|
|
|
stop();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::loadTasks(const QVector<TimedTask>& tasks)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_tasks = tasks;
|
|
|
|
|
|
qDebug() << "TaskScheduler: Loaded" << tasks.size() << "tasks";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::start()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_timer) {
|
|
|
|
|
|
qDebug() << "TaskScheduler: Already running";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "TaskScheduler: Starting";
|
|
|
|
|
|
|
|
|
|
|
|
m_timer = new QTimer(this);
|
|
|
|
|
|
connect(m_timer, &QTimer::timeout, this, &TaskScheduler::checkTasks);
|
|
|
|
|
|
m_timer->start(1000); // 每秒检查一次
|
|
|
|
|
|
|
|
|
|
|
|
emit schedulerStateChanged(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::stop()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_timer) {
|
|
|
|
|
|
m_timer->stop();
|
|
|
|
|
|
delete m_timer;
|
|
|
|
|
|
m_timer = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (m_currentExecutor) {
|
|
|
|
|
|
m_currentExecutor->stop();
|
|
|
|
|
|
m_currentExecutor->deleteLater();
|
|
|
|
|
|
m_currentExecutor = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qDebug() << "TaskScheduler: Stopped";
|
|
|
|
|
|
emit schedulerStateChanged(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::checkTasks()
|
|
|
|
|
|
{
|
|
|
|
|
|
QDateTime now = QDateTime::currentDateTime();
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& task : m_tasks) {
|
|
|
|
|
|
if (task.status != TaskStatus::Waiting) continue;
|
2026-06-04 10:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 超过计划时间1分钟以上,认为任务已过时,跳过
|
|
|
|
|
|
if (task.scheduledTime.addSecs(60) < now) {
|
|
|
|
|
|
std::cerr << "TaskScheduler::checkTasks,任务已过时,跳过:" << task.id << std::endl;
|
|
|
|
|
|
task.status = TaskStatus::Finished;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 18:22:02 +08:00
|
|
|
|
if (task.scheduledTime > now) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 到达计划时间,启动任务
|
2026-06-04 10:55:12 +08:00
|
|
|
|
std::cerr << "TaskScheduler::checkTasks,到达计划时间,启动任务" << std::endl;
|
2026-06-02 18:22:02 +08:00
|
|
|
|
executeTask(task);
|
|
|
|
|
|
break; // 一次只执行一个任务
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::onTaskFinished(bool success)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_currentTaskId > 0) {
|
|
|
|
|
|
TaskStatus status = success ? TaskStatus::Finished : TaskStatus::Waiting;
|
|
|
|
|
|
updateTaskStatus(m_currentTaskId, status);
|
|
|
|
|
|
emit taskFinished(m_currentTaskId, success);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理执行器
|
|
|
|
|
|
if (m_currentExecutor) {
|
|
|
|
|
|
m_currentExecutor->deleteLater();
|
|
|
|
|
|
m_currentExecutor = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
m_currentTaskId = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::onSubTaskStarted(int subTaskIndex, SubTaskType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_currentTaskId > 0) {
|
|
|
|
|
|
emit subTaskStarted(m_currentTaskId, subTaskIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::onSubTaskFinished(int subTaskIndex, SubTaskType type, bool success)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_currentTaskId > 0) {
|
|
|
|
|
|
emit subTaskFinished(m_currentTaskId, subTaskIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::onExecutorError(const QString& error)
|
|
|
|
|
|
{
|
|
|
|
|
|
emitError(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::executeTask(TimedTask& task)
|
|
|
|
|
|
{
|
|
|
|
|
|
qDebug() << "TaskScheduler: Executing task" << task.id;
|
|
|
|
|
|
|
|
|
|
|
|
updateTaskStatus(task.id, TaskStatus::Running);
|
|
|
|
|
|
m_currentTaskId = task.id;
|
|
|
|
|
|
|
|
|
|
|
|
emit taskStarted(task.id);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建任务执行器
|
2026-06-04 10:55:12 +08:00
|
|
|
|
m_currentExecutor = new TaskExecutor(this);
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 连接信号
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::finished,
|
|
|
|
|
|
this, &TaskScheduler::onTaskFinished);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::subTaskStarted,
|
|
|
|
|
|
this, &TaskScheduler::onSubTaskStarted);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::subTaskFinished,
|
|
|
|
|
|
this, &TaskScheduler::onSubTaskFinished);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::errorOccurred,
|
|
|
|
|
|
this, &TaskScheduler::onExecutorError);
|
2026-06-04 10:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 采集相关信号透传
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::hyperCamParm,
|
|
|
|
|
|
this, &TaskScheduler::hyperCamParm);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::camParm,
|
|
|
|
|
|
this, &TaskScheduler::camParm);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::motorParm,
|
|
|
|
|
|
this, &TaskScheduler::motorParm);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::startRecordSignal,
|
|
|
|
|
|
this, &TaskScheduler::startRecordSignal);
|
|
|
|
|
|
|
2026-06-10 18:10:47 +08:00
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::switchHalogenLampSignal, this, &TaskScheduler::switchHalogenLampSignal);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::switchD65LampSignal, this, &TaskScheduler::switchD65LampSignal);
|
|
|
|
|
|
connect(m_currentExecutor, &TaskExecutor::switchSlrSignal, this, &TaskScheduler::switchSlrSignal);
|
|
|
|
|
|
|
2026-06-04 10:55:12 +08:00
|
|
|
|
connect(this, &TaskScheduler::sequenceCompleteSignal, m_currentExecutor, &TaskExecutor::onSequenceComplete);
|
2026-06-04 18:01:22 +08:00
|
|
|
|
connect(this, &TaskScheduler::Back2OriginSignal, m_currentExecutor, &TaskExecutor::onBack2Origin);
|
2026-06-02 18:22:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 开始执行
|
|
|
|
|
|
m_currentExecutor->execute(task);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::updateTaskStatus(int taskId, TaskStatus status)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (auto& task : m_tasks) {
|
|
|
|
|
|
if (task.id == taskId) {
|
|
|
|
|
|
task.status = status;
|
|
|
|
|
|
if (status == TaskStatus::Running) {
|
|
|
|
|
|
task.startTime = QDateTime::currentDateTime();
|
|
|
|
|
|
} else if (status == TaskStatus::Finished) {
|
|
|
|
|
|
task.endTime = QDateTime::currentDateTime();
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TaskScheduler::emitError(const QString& error)
|
|
|
|
|
|
{
|
|
|
|
|
|
qWarning() << "TaskScheduler: Error:" << error;
|
|
|
|
|
|
emit errorOccurred(error);
|
|
|
|
|
|
}
|