first commit

This commit is contained in:
2026-01-02 19:20:35 +09:00
commit a10cb30c4a
94 changed files with 28609 additions and 0 deletions

View File

@@ -0,0 +1,655 @@
#include "proceduremanager.h"
#include "procedureparser.h"
#include <QDir>
#include <QFile>
#include <QDebug>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QCoreApplication>
ProcedureManager::ProcedureManager(QObject *parent)
: QObject(parent)
{
// 不再自动创建示例数据,等待调用 loadProcedureList
}
ProcedureManager::~ProcedureManager()
{
}
QVector<ProcedureSummary> 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<ProcedureSummary> ProcedureManager::searchProcedures(const QString &keyword)
{
if (keyword.isEmpty())
{
return m_procedureList;
}
QVector<ProcedureSummary> 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<QVariantMap> &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;
}