#include "TimedDataCollectionDataStructures.h" #include #include #include #include #include #include #include #include #include // ==================== 公共接口实现 ==================== bool TimedDataCollectionDataStructuresReaderWriter::saveTasksToFile(const QString& filePath, const QVector& 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& 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::default; } 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["durationMinutes"] = subTask.durationMinutes; obj["estimatedDurationMinutes"] = subTask.estimatedDurationMinutes; 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(QString(), Qt::ISODate); subTask.endTime = QDateTime::fromString(QString(), Qt::ISODate); subTask.durationMinutes = json["durationMinutes"].toDouble(); subTask.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble(); 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"].toDouble(); 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["durationMinutes"] = task.durationMinutes; obj["estimatedDurationMinutes"] = task.estimatedDurationMinutes; obj["savePath"] = task.savePath; obj["status"] = taskStatusToString(task.status); obj["HalogenLampPreheatingTime_Minute"] = task.HalogenLampPreheatingTime_Minute; 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(QString(), Qt::ISODate); task.endTime = QDateTime::fromString(QString(), Qt::ISODate); task.durationMinutes = json["durationMinutes"].toDouble(); task.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble(); task.savePath = json["savePath"].toString(); task.status = stringToTaskStatus(json["status"].toString()); task.HalogenLampPreheatingTime_Minute = json["HalogenLampPreheatingTime_Minute"].toDouble(); 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; } // ==================== TaskExecutor 实现 ==================== TaskExecutor::TaskExecutor(QObject* parent) : QObject(parent) , m_currentSubTaskIndex(-1) , 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_task.startTime = QDateTime::currentDateTime(); m_task.status = TaskStatus::Running; emit taskUpdated(m_task); m_currentSubTaskIndex = -1; m_isRunning = true; qDebug() << "TaskExecutor: Starting task" << task.id; // 打开卤素灯预热 emit switchHalogenLampSignal(1); printMsgAndTime("open HalogenLamp"); makeFolder(m_task.savePath); // 开始执行第一个子任务 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; } void TaskExecutor::stop() { if (!m_isRunning) return; qDebug() << "TaskExecutor: Stopping task" << m_task.id; m_isRunning = false; emit finished(false); } 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; } 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; subTask.endTime = QDateTime::currentDateTime(); subTask.durationMinutes = (double)subTask.startTime.secsTo(subTask.endTime) / 60; qDebug() << "TaskExecutor: subtask "<< m_currentSubTaskIndex<< " time consuming(Minutes): "<< subTask.durationMinutes; // 拷贝subTask.pathLineFilePath到m_currentFolder if (!subTask.pathLineFilePath.isEmpty() && QFile::exists(subTask.pathLineFilePath)) { QFileInfo fileInfo(subTask.pathLineFilePath); QString destPath = m_currentFolder + QDir::separator() + fileInfo.fileName(); if (QFile::copy(subTask.pathLineFilePath, destPath)) { qDebug() << "TaskExecutor: Copied path line file to" << destPath; } else { qDebug() << "TaskExecutor: Failed to copy path line file from" << subTask.pathLineFilePath << "to" << destPath; } } emit subTaskFinished(m_currentSubTaskIndex, subTask.type, (status == 0)); emit taskUpdated(m_task); } // switch (m_task.subTasks[m_currentSubTaskIndex].type) { case SubTaskType::SingleLensReflex: { emit switchD65LampSignal(0); break; } case SubTaskType::DepthCamera: { emit switchD65LampSignal(0); break; } } // 判断下一次的任务是否为高光谱任务,如果不是关闭卤素灯 int nestSubTaskIndex = m_currentSubTaskIndex + 1; if (nestSubTaskIndex >= m_task.subTasks.size()) { emit switchD65LampSignal(0); emit switchSlrSignal(0); emit switchHalogenLampSignal(0); return; } switch (m_task.subTasks[nestSubTaskIndex].type) { case SubTaskType::SingleLensReflex: { emit switchHalogenLampSignal(0); break; } case SubTaskType::DepthCamera: { emit switchHalogenLampSignal(0); break; } } emit taskUpdated(m_task); } void TaskExecutor::onBack2Origin() { // 关闭单反电源 switch (m_task.subTasks[m_currentSubTaskIndex].type) { case SubTaskType::SingleLensReflex: { emit switchSlrSignal(0); break; } } // 检查是否还有更多子任务 int nestSubTaskIndex = m_currentSubTaskIndex + 1; if (nestSubTaskIndex < m_task.subTasks.size()) { // 执行下一个子任务 if(m_task.subTasks[nestSubTaskIndex].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); } } else { // 所有子任务完成 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) { if (!m_isRunning) return; qWarning() << "TaskExecutor: Error occurred:" << error; m_isRunning = false; emit errorOccurred(error); emit finished(false); } void TaskExecutor::executeNextSubTask() { m_currentSubTaskIndex++; 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(); 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: "); //qDebug() << "TaskExecutor: Starting subtask" << m_currentSubTaskIndex // << "type:" << static_cast(subTask.type); emit subTaskStarted(m_currentSubTaskIndex, subTask.type); emit motorParm(subTask.pathLineFilePath); int camType; switch (subTask.type) { case SubTaskType::HyperSpectual400_1000nm: { camType = 0; m_currentFolder = makeSubTaskDataFolder("L"); emit hyperCamParm(camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "L"); break; } case SubTaskType::HyperSpectual1000_1700nm: { camType = 1; m_currentFolder = makeSubTaskDataFolder("NIR"); emit hyperCamParm(camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "NIR"); break; } case SubTaskType::SingleLensReflex: { camType = 2; m_currentFolder = makeSubTaskDataFolder("SLR"); emit camParm(camType, 3, m_currentFolder); emit switchD65LampSignal(1); emit switchSlrSignal(1); break; } case SubTaskType::DepthCamera: { camType = 3; m_currentFolder = makeSubTaskDataFolder("DepthCamera"); emit camParm(camType, 3, m_currentFolder); emit switchD65LampSignal(1); break; } } emit startRecordSignal(camType); } // ==================== TaskScheduler 实现 ==================== TaskScheduler::TaskScheduler(QObject* parent) : QObject(parent) , m_timer(nullptr) , m_currentExecutor(nullptr) , m_currentTaskId(-1) { } TaskScheduler::~TaskScheduler() { stop(); } void TaskScheduler::loadTasks(const QVector& 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() { // 检查是否所有任务都不需要执行 bool allTasksDone = std::all_of(m_tasks.begin(), m_tasks.end(), [](const TimedTask& task) { return task.status != TaskStatus::Waiting; }); if (allTasksDone) { std::cerr << "TaskScheduler::checkTasks,没有任务等待执行,停止调度器" << std::endl; stop(); } QDateTime now = QDateTime::currentDateTime(); if (m_currentTaskId >= 0)//有任务正在执行 { return; } for (auto& task : m_tasks) { if (task.status != TaskStatus::Waiting) continue; // 超过计划时间threSecs,认为任务已过时,跳过 qint64 threSecs = 30; if (task.scheduledTime.addSecs(threSecs) < now) { std::cerr << "TaskScheduler::checkTasks,任务已过时,跳过:" << task.id << std::endl; task.status = TaskStatus::Timeout; for (size_t i = 0; i < task.subTaskCount(); i++) { task.subTasks[i].status = TaskStatus::Timeout; } emit taskDataChanged(task); continue; } if (task.scheduledTime > now) continue; qint64 fireThreSecs = 5; if (task.scheduledTime.addSecs(-1*fireThreSecs) < now && task.scheduledTime.addSecs(fireThreSecs) > now)// 到达计划时间,启动任务 { std::cerr << "TaskScheduler::checkTasks,到达计划时间,启动任务" << std::endl; 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); // 创建任务执行器 m_currentExecutor = new TaskExecutor(this); // 连接信号 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); // 采集相关信号透传 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); connect(m_currentExecutor, &TaskExecutor::switchHalogenLampSignal, this, &TaskScheduler::switchHalogenLampSignal); connect(m_currentExecutor, &TaskExecutor::switchD65LampSignal, this, &TaskScheduler::switchD65LampSignal); connect(m_currentExecutor, &TaskExecutor::switchSlrSignal, this, &TaskScheduler::switchSlrSignal); 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); } 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); }