#include "proceduremanager.h" #include "procedureparser.h" #include #include #include #include #include #include #include #include ProcedureManager::ProcedureManager(QObject *parent) : QObject(parent) { // 不再自动创建示例数据,等待调用 loadProcedureList } ProcedureManager::~ProcedureManager() { } QVector ProcedureManager::loadProcedureList(const QString &directory) { m_procedureDirectory = directory; m_procedureList.clear(); QDir dir(directory); if (!dir.exists()) { qWarning() << "Procedure directory does not exist:" << directory; createSampleProcedures(); return m_procedureList; } // 扫描目录中的 YAML 和 JSON 文件 QStringList filters; filters << "*.yaml" << "*.yml" << "*.json"; dir.setNameFilters(filters); QFileInfoList files = dir.entryInfoList(QDir::Files | QDir::Readable); qDebug() << "Found" << files.size() << "procedure files in" << directory; for (const QFileInfo &fileInfo : files) { QString filePath = fileInfo.absoluteFilePath(); qDebug() << "Scanning procedure file:" << filePath; // 使用 ProcedureParser 解析文件 ProcedureParser parser; if (parser.loadConfig(filePath)) { ProcedureConfig config = parser.parseProcedureConfig(); // 创建摘要信息 ProcedureSummary summary; summary.id = config.procedureId; summary.name = config.procedureName; summary.version = config.version; summary.description = config.description; summary.filePath = filePath; m_procedureList.append(summary); qDebug() << "Loaded procedure:" << summary.name << "ID:" << summary.id << "Version:" << summary.version; } else { qWarning() << "Failed to parse procedure file:" << filePath; } } // 如果没有找到任何文件,创建示例数据 if (m_procedureList.isEmpty()) { qDebug() << "No valid procedure files found, creating sample data"; createSampleProcedures(); } else { // 即使找到了真实文件,也添加mock异常数据用于测试 qDebug() << "Adding mock exception data for testing"; createMockExceptionData(); } return m_procedureList; } QVector ProcedureManager::searchProcedures(const QString &keyword) { if (keyword.isEmpty()) { return m_procedureList; } QVector results; for (const auto &proc : m_procedureList) { if (proc.id.contains(keyword, Qt::CaseInsensitive) || proc.name.contains(keyword, Qt::CaseInsensitive)) { results.append(proc); } } return results; } ProcedureData ProcedureManager::loadProcedure(const QString &procedureId) { if (m_loadedProcedures.contains(procedureId)) { return m_loadedProcedures[procedureId]; } // 创建示例规程 ProcedureData proc; proc.id = procedureId; proc.workOrderId = m_workOrderId; if (procedureId == "KMCIXRCP503") { proc.name = "一回路温度传感器绝缘和连续性检查:TP RCP63"; proc.version = "C4"; proc.description = "本程序适用于热停堆(热功率稳定)工况下执行RCP63程序"; // 创建任务组 TaskGroup group1; group1.id = "preInspection"; group1.name = "5.1 试验前状态说明和检查"; // 添加步骤 StepData step1; step1.id = "step_001"; step1.type = StepType::Manual; step1.content = "本程序适用于热停堆(热功率稳定)工况下执行RCP63程序(K-MT-I-X-RCP-501)统一进行Sensor Check 部分使用。"; step1.status = StepStatus::Confirmed; group1.steps.append(step1); StepData step2; step2.id = "step_002"; step2.type = StepType::Automatic; step2.content = "执行前确认KIC中一回路三个环路平均温度变化量不超过0.2℃;"; step2.status = StepStatus::Passed; step2.tableRefs.append("temperatureCheckTable"); step2.highlightFields.append("loop1Temp"); step2.highlightFields.append("loop2Temp"); step2.highlightFields.append("loop3Temp"); group1.steps.append(step2); StepData step3; step3.id = "step_003"; step3.type = StepType::Automatic; step3.content = "若KIC中显示的一回路三个环路平均温度变化超过0.2℃,则采取滑动平均的计算方式记录滑动周期: (建议60s), 一环路平均温度滑动平均最大最小值之差;二环路平均温度滑动平均最大最小值之差;三环路平均温度滑动平均最大最小值之差;每个数据测量的最短时间(应大于滑动周期):。确认三个环路平均温度的滑动平均最大与最小值之差不超过0.2℃。"; step3.status = StepStatus::Pending; group1.steps.append(step3); StepData step4; step4.id = "step_004"; step4.type = StepType::Manual; step4.content = "确认 RPR、RPN上无相关的试验检修工作。"; step4.status = StepStatus::Pending; group1.steps.append(step4); StepData step5; step5.id = "step_005"; step5.type = StepType::Automatic; step5.content = "按附表1检查相关模拟量指示并记录,确认结果满意。"; step5.status = StepStatus::Pending; step5.tableRefs.append("analogReadingsTable"); group1.steps.append(step5); StepData step6; step6.id = "step_006"; step6.type = StepType::Automatic; step6.content = "试验前根据附表1报警清单检查报警,确认无异常报警,方可进行后续检查工作"; step6.status = StepStatus::Pending; step6.tableRefs.append("alarmCheckTable"); group1.steps.append(step6); proc.taskGroups.append(group1); // 创建表格 TableData tempTable; tempTable.id = "temperatureCheckTable"; tempTable.name = "环路平均温度变化"; tempTable.description = "检验前后的环路温度数据"; TableField f1; f1.id = "loop1Temp"; f1.name = "一环路平均温度"; f1.type = "numeric"; f1.unit = "℃"; TableField f2; f2.id = "loop2Temp"; f2.name = "二环路平均温度"; f2.type = "numeric"; f2.unit = "℃"; TableField f3; f3.id = "loop3Temp"; f3.name = "三环路平均温度"; f3.type = "numeric"; f3.unit = "℃"; tempTable.columns.append(f1); tempTable.columns.append(f2); tempTable.columns.append(f3); proc.tables.append(tempTable); TableData analogTable; analogTable.id = "analogReadingsTable"; analogTable.name = "实验前后模拟量数据"; proc.tables.append(analogTable); TableData alarmTable; alarmTable.id = "alarmCheckTable"; alarmTable.name = "试验前后报警检查"; proc.tables.append(alarmTable); // 计算步骤数 proc.totalSteps = group1.steps.size(); proc.completedSteps = 2; // 已完成2个步骤 } else if (procedureId == "SIMPLE63") { proc.name = "范例 一回路温度传感器绝缘和连续性检查 TP RCP63"; proc.version = "SIMPLE"; proc.description = "范例规程"; proc.totalSteps = 6; proc.completedSteps = 0; } else if (procedureId == "SIMPLE_TEST") { proc.name = "简单测试规程"; proc.version = "1.0"; proc.description = "这是一个简单的测试规程,用于演示 ProcedurePlayer 的基本功能。包含一个测试任务组和结果展示。"; proc.totalSteps = 4; proc.completedSteps = 0; } else { // 尝试从 JSON 文件加载 mock 数据 proc = loadMockProcedureFromJson(procedureId); // 如果 JSON 中没有找到,返回空数据 if (proc.name.isEmpty()) { qWarning() << "Procedure not found:" << procedureId; } } m_loadedProcedures[procedureId] = proc; emit procedureLoaded(proc); return proc; } void ProcedureManager::setWorkOrderId(const QString &workOrderId) { m_workOrderId = workOrderId; } bool ProcedureManager::confirmStep(const QString &procedureId, const QString &stepId) { if (!m_loadedProcedures.contains(procedureId)) { return false; } auto &proc = m_loadedProcedures[procedureId]; for (auto &group : proc.taskGroups) { for (auto &step : group.steps) { if (step.id == stepId) { step.status = StepStatus::Confirmed; step.executedAt = QDateTime::currentDateTime(); emit stepStatusChanged(stepId, StepStatus::Confirmed); return true; } } } return false; } bool ProcedureManager::cancelStepConfirm(const QString &procedureId, const QString &stepId, const QString &reason) { if (!m_loadedProcedures.contains(procedureId)) { return false; } auto &proc = m_loadedProcedures[procedureId]; for (auto &group : proc.taskGroups) { for (auto &step : group.steps) { if (step.id == stepId) { step.status = StepStatus::Pending; step.cancelReason = reason; emit stepStatusChanged(stepId, StepStatus::Pending); return true; } } } return false; } bool ProcedureManager::executeAutoStep(const QString &procedureId, const QString &stepId) { // 模拟自动步骤执行 if (!m_loadedProcedures.contains(procedureId)) { return false; } auto &proc = m_loadedProcedures[procedureId]; for (auto &group : proc.taskGroups) { for (auto &step : group.steps) { if (step.id == stepId && step.type == StepType::Automatic) { step.status = StepStatus::InProgress; emit stepStatusChanged(stepId, StepStatus::InProgress); // 实际应用中这里会执行真正的自动化逻辑 return true; } } } return false; } TableData ProcedureManager::getTableData(const QString &procedureId, const QString &tableId) { if (m_loadedProcedures.contains(procedureId)) { const auto &proc = m_loadedProcedures[procedureId]; for (const auto &table : proc.tables) { if (table.id == tableId) { return table; } } } return TableData(); } bool ProcedureManager::updateTableData(const QString &procedureId, const QString &tableId, const QVector &rows) { if (!m_loadedProcedures.contains(procedureId)) { return false; } auto &proc = m_loadedProcedures[procedureId]; for (auto &table : proc.tables) { if (table.id == tableId) { table.rows = rows; emit tableDataUpdated(tableId); return true; } } return false; } void ProcedureManager::createSampleProcedures() { m_procedureList.clear(); ProcedureSummary p1; p1.id = "KMCIXRCP503"; p1.name = "一回路温度传感器绝缘和连续性检查:TP RCP63"; p1.version = "C4"; p1.description = ""; p1.filePath = "[SAMPLE]"; m_procedureList.append(p1); ProcedureSummary p2; p2.id = "SIMPLE63"; p2.name = "范例 一回路温度传感器绝缘和连续性检查 TP RCP63"; p2.version = "SIMPLE"; p2.description = ""; p2.filePath = "[SAMPLE]"; m_procedureList.append(p2); ProcedureSummary p3; p3.id = "SIMPLE_TEST"; p3.name = "简单测试规程"; p3.version = "1.0"; p3.description = "这是一个简单的测试规程,用于演示 ProcedurePlayer 的基本功能。包含一个测试任务组和结果展示。"; p3.filePath = "[SAMPLE]"; m_procedureList.append(p3); // 也添加异常数据 createMockExceptionData(); } void ProcedureManager::createMockExceptionData() { ProcedureSummary p7; p7.id = "EMPTY_DESC"; p7.name = "空描述测试"; p7.version = "1.0"; p7.description = ""; p7.filePath = "[MOCK_EXCEPTION]"; m_procedureList.append(p7); // Mock 异常数据 - 超长描述 ProcedureSummary p8; p8.id = "LONG_DESC"; p8.name = "超长描述测试"; p8.version = "2.0"; p8.description = "这是一个包含超长描述的测试规程,用于验证当描述文本过长时,界面能否正确处理和显示。" "描述内容可能包含多个段落、技术细节、注意事项等。在实际应用中,规程描述可能会非常详细," "包括前置条件、执行步骤概述、预期结果、风险提示、相关文档引用等信息。这样的长描述需要" "界面能够适当地截断、折叠或提供滚动功能来确保用户体验。"; p8.filePath = "[MOCK_EXCEPTION]"; m_procedureList.append(p8); // Mock 异常数据 - 数字和符号混合的 ID ProcedureSummary p9; p9.id = "TEST-2024-12-31_V1.0"; p9.name = "日期版本号混合 ID"; p9.version = "2024.12.31"; p9.description = "测试复杂的 ID 和版本号格式"; p9.filePath = "[MOCK_EXCEPTION]"; m_procedureList.append(p9); // Mock 异常数据 - 换行符 ProcedureSummary p10; p10.id = "MULTILINE"; p10.name = "多行\n文本\n测试"; p10.version = "1.0"; p10.description = "第一行描述\n第二行描述\n第三行描述"; p10.filePath = "[MOCK_EXCEPTION]"; m_procedureList.append(p10); } StepType ProcedureManager::parseStepType(const QString &typeStr) { if (typeStr == "Manual") return StepType::Manual; if (typeStr == "Automatic") return StepType::Automatic; return StepType::Manual; } StepStatus ProcedureManager::parseStepStatus(const QString &statusStr) { if (statusStr == "Pending") return StepStatus::Pending; if (statusStr == "InProgress") return StepStatus::InProgress; if (statusStr == "Confirmed") return StepStatus::Confirmed; if (statusStr == "Passed") return StepStatus::Passed; if (statusStr == "Failed") return StepStatus::Failed; if (statusStr == "Skipped") return StepStatus::Skipped; return StepStatus::Pending; } ProcedureData ProcedureManager::loadMockProcedureFromJson(const QString &procedureId) { ProcedureData proc; proc.id = procedureId; proc.workOrderId = m_workOrderId; // 尝试多个可能的 JSON 文件路径 QStringList possiblePaths; possiblePaths << m_procedureDirectory + "/mock_procedures.json"; possiblePaths << QDir::currentPath() + "/procedures/mock_procedures.json"; possiblePaths << QCoreApplication::applicationDirPath() + "/procedures/mock_procedures.json"; possiblePaths << QCoreApplication::applicationDirPath() + "/../../../procedures/mock_procedures.json"; QString jsonFilePath; for (const QString &path : possiblePaths) { if (QFile::exists(path)) { jsonFilePath = path; qDebug() << "Found mock procedures JSON at:" << path; break; } } if (jsonFilePath.isEmpty()) { qWarning() << "Mock procedures JSON file not found. Tried paths:" << possiblePaths; return proc; } // 读取 JSON 文件 QFile file(jsonFilePath); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Failed to open mock procedures JSON:" << jsonFilePath; return proc; } QByteArray jsonData = file.readAll(); file.close(); QJsonDocument doc = QJsonDocument::fromJson(jsonData); if (!doc.isObject()) { qWarning() << "Invalid JSON format in mock procedures file"; return proc; } QJsonObject root = doc.object(); QJsonArray mockProcedures = root["mockProcedures"].toArray(); // 查找匹配的规程 for (const QJsonValue &value : mockProcedures) { QJsonObject procObj = value.toObject(); if (procObj["id"].toString() != procedureId) continue; // 解析基本信息 proc.name = procObj["name"].toString(); proc.version = procObj["version"].toString(); proc.description = procObj["description"].toString(); // 解析任务组 QJsonArray taskGroupsArray = procObj["taskGroups"].toArray(); for (const QJsonValue &groupValue : taskGroupsArray) { QJsonObject groupObj = groupValue.toObject(); TaskGroup group; group.id = groupObj["id"].toString(); group.name = groupObj["name"].toString(); // 解析步骤 QJsonArray stepsArray = groupObj["steps"].toArray(); for (const QJsonValue &stepValue : stepsArray) { QJsonObject stepObj = stepValue.toObject(); StepData step; step.id = stepObj["id"].toString(); step.content = stepObj["content"].toString(); step.type = parseStepType(stepObj["type"].toString()); step.status = parseStepStatus(stepObj["status"].toString()); // 解析表格引用 if (stepObj.contains("tableRefs")) { QJsonArray tableRefsArray = stepObj["tableRefs"].toArray(); for (const QJsonValue &refValue : tableRefsArray) { step.tableRefs.append(refValue.toString()); } } // 解析高亮字段 if (stepObj.contains("highlightFields")) { QJsonArray highlightArray = stepObj["highlightFields"].toArray(); for (const QJsonValue &fieldValue : highlightArray) { step.highlightFields.append(fieldValue.toString()); } } group.steps.append(step); } proc.taskGroups.append(group); } // 解析表格 if (procObj.contains("tables")) { QJsonArray tablesArray = procObj["tables"].toArray(); for (const QJsonValue &tableValue : tablesArray) { QJsonObject tableObj = tableValue.toObject(); TableData table; table.id = tableObj["id"].toString(); table.name = tableObj["name"].toString(); if (tableObj.contains("description")) table.description = tableObj["description"].toString(); // 解析列 if (tableObj.contains("columns")) { QJsonArray columnsArray = tableObj["columns"].toArray(); for (const QJsonValue &colValue : columnsArray) { QJsonObject colObj = colValue.toObject(); TableField field; field.id = colObj["id"].toString(); field.name = colObj["name"].toString(); field.type = colObj["type"].toString(); if (colObj.contains("unit")) field.unit = colObj["unit"].toString(); if (colObj.contains("isRequired")) field.isRequired = colObj["isRequired"].toBool(); if (colObj.contains("isHighlighted")) field.isHighlighted = colObj["isHighlighted"].toBool(); table.columns.append(field); } } proc.tables.append(table); } } // 计算步骤统计 if (procObj.contains("totalSteps")) { proc.totalSteps = procObj["totalSteps"].toInt(); } else { proc.totalSteps = 0; for (const TaskGroup &group : proc.taskGroups) { proc.totalSteps += group.steps.size(); } } if (procObj.contains("completedSteps")) { proc.completedSteps = procObj["completedSteps"].toInt(); } else { proc.completedSteps = 0; for (const TaskGroup &group : proc.taskGroups) { for (const StepData &step : group.steps) { if (step.status == StepStatus::Confirmed || step.status == StepStatus::Passed) { proc.completedSteps++; } } } } qDebug() << "Loaded mock procedure from JSON:" << procedureId; break; } return proc; }