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,479 @@
#include "functionregistry.h"
#include <QDebug>
#include <QtMath>
#include <QRandomGenerator>
#include <QThread>
// =============================================================================
// 單例實現
// =============================================================================
FunctionRegistry *FunctionRegistry::m_instance = nullptr;
FunctionRegistry *FunctionRegistry::instance()
{
if (!m_instance)
{
m_instance = new FunctionRegistry();
}
return m_instance;
}
FunctionRegistry::FunctionRegistry(QObject *parent)
: QObject(parent)
{
registerBuiltinFunctions();
}
FunctionRegistry::~FunctionRegistry()
{
}
// =============================================================================
// 函數註冊
// =============================================================================
bool FunctionRegistry::registerFunction(const QString &functionType,
const QStringList &parameterNames,
FunctionCallback callback,
const QString &displayName,
const QString &description,
const QString &category)
{
FunctionMetadata metadata;
metadata.functionType = functionType;
metadata.parameterNames = parameterNames;
metadata.callback = callback;
metadata.displayName = displayName.isEmpty() ? functionType : displayName;
metadata.description = description;
metadata.category = category;
return registerFunction(metadata);
}
bool FunctionRegistry::registerFunction(const FunctionMetadata &metadata)
{
if (metadata.functionType.isEmpty())
{
qWarning() << "無法註冊空函數類型";
return false;
}
if (m_functions.contains(metadata.functionType))
{
qWarning() << "函數已存在,將被覆蓋:" << metadata.functionType;
}
m_functions[metadata.functionType] = metadata;
qDebug() << "註冊函數:" << metadata.functionType << "分類:" << metadata.category;
emit functionRegistered(metadata.functionType);
return true;
}
bool FunctionRegistry::unregisterFunction(const QString &functionType)
{
if (!m_functions.contains(functionType))
{
return false;
}
m_functions.remove(functionType);
emit functionUnregistered(functionType);
return true;
}
bool FunctionRegistry::hasFunction(const QString &functionType) const
{
return m_functions.contains(functionType);
}
QStringList FunctionRegistry::getAllFunctionTypes() const
{
return m_functions.keys();
}
QStringList FunctionRegistry::getFunctionsByCategory(const QString &category) const
{
QStringList result;
for (auto it = m_functions.begin(); it != m_functions.end(); ++it)
{
if (it.value().category == category)
{
result.append(it.key());
}
}
return result;
}
FunctionRegistry::FunctionMetadata FunctionRegistry::getFunctionMetadata(const QString &functionType) const
{
if (m_functions.contains(functionType))
{
return m_functions[functionType];
}
return FunctionMetadata();
}
// =============================================================================
// 參數處理
// =============================================================================
QVariantMap FunctionRegistry::normalizeParameters(const FunctionMetadata &metadata,
const QVariant &parameters)
{
QVariantMap result;
if (parameters.type() == QVariant::Map)
{
result = parameters.toMap();
}
else if (parameters.type() == QVariant::List)
{
QVariantList list = parameters.toList();
for (int i = 0; i < qMin(list.size(), metadata.parameterNames.size()); i++)
{
result[metadata.parameterNames[i]] = list[i];
}
}
// 應用默認值
for (auto it = metadata.defaultParameters.begin(); it != metadata.defaultParameters.end(); ++it)
{
if (!result.contains(it.key()))
{
result[it.key()] = it.value();
}
}
return result;
}
bool FunctionRegistry::validateParameters(const FunctionMetadata &metadata,
const QVariantMap &params)
{
Q_UNUSED(metadata)
Q_UNUSED(params)
// 簡單驗證 - 可以擴展
return true;
}
// =============================================================================
// 函數執行
// =============================================================================
QVariantList FunctionRegistry::executeFunction(const QString &functionType,
const QVariant &parameters)
{
if (!m_functions.contains(functionType))
{
qWarning() << "找不到函數:" << functionType;
emit functionExecuted(functionType, false);
return QVariantList();
}
const FunctionMetadata &metadata = m_functions[functionType];
QVariantMap params = normalizeParameters(metadata, parameters);
if (!validateParameters(metadata, params))
{
qWarning() << "參數驗證失敗:" << functionType;
emit functionExecuted(functionType, false);
return QVariantList();
}
try
{
QVariantList result = metadata.callback(params);
emit functionExecuted(functionType, true);
return result;
}
catch (const std::exception &e)
{
qWarning() << "函數執行異常:" << functionType << "-" << e.what();
emit functionExecuted(functionType, false);
return QVariantList();
}
catch (...)
{
qWarning() << "函數執行未知異常:" << functionType;
emit functionExecuted(functionType, false);
return QVariantList();
}
}
// =============================================================================
// 內建函數註冊
// =============================================================================
void FunctionRegistry::registerBuiltinFunctions()
{
// --------------------------------------------------
// 電阻測量函數
// --------------------------------------------------
registerFunction("RESISTANCE_4WIRE", {"channel", "range", "samples"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
double range = params.value("range", 1000.0).toDouble();
int samples = params.value("samples", 10).toInt();
Q_UNUSED(samples)
qDebug() << "四線電阻測量: 通道=" << channel << "量程=" << range;
// 模擬數據
double resistance = 100.0 + QRandomGenerator::global()->bounded(1.0);
double temperature = (resistance - 100.0) / 0.385;
QVariantList result;
result << resistance << temperature;
return result; }, "四線電阻測量", "使用四線法測量電阻,消除導線電阻影響", "measurement");
registerFunction("RESISTANCE_2WIRE", {"channel", "range", "samples"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
double range = params.value("range", 1000.0).toDouble();
Q_UNUSED(range)
qDebug() << "二線電阻測量: 通道=" << channel;
double resistance = 100.0 + QRandomGenerator::global()->bounded(2.0);
QVariantList result;
result << resistance;
return result; }, "二線電阻測量", "使用二線法測量電阻", "measurement");
// --------------------------------------------------
// 電壓測量函數
// --------------------------------------------------
registerFunction("VOLTAGE_V", {"channel", "range", "samples"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
double range = params.value("range", 10.0).toDouble();
Q_UNUSED(range)
qDebug() << "電壓測量: 通道=" << channel;
double voltage = 5.0 + QRandomGenerator::global()->bounded(0.1);
QVariantList result;
result << voltage;
return result; }, "電壓測量", "測量直流電壓", "measurement");
registerFunction("VOLTAGE_MV", {"channel", "range", "samples"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
Q_UNUSED(channel)
double voltage_mv = 10.0 + QRandomGenerator::global()->bounded(1.0);
QVariantList result;
result << voltage_mv;
return result; }, "毫伏測量", "測量毫伏級電壓", "measurement");
// --------------------------------------------------
// 電流測量函數
// --------------------------------------------------
registerFunction("CURRENT_MA", {"channel", "range", "samples"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
Q_UNUSED(channel)
double current_ma = 12.0 + QRandomGenerator::global()->bounded(0.5);
QVariantList result;
result << current_ma;
return result; }, "毫安電流測量", "測量毫安級電流如4-20mA信號", "measurement");
registerFunction("CURRENT_A", {"channel", "range", "samples"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
Q_UNUSED(channel)
double current_a = 1.0 + QRandomGenerator::global()->bounded(0.1);
QVariantList result;
result << current_a;
return result; }, "安培電流測量", "測量安培級電流", "measurement");
// --------------------------------------------------
// 絕緣測量函數
// --------------------------------------------------
registerFunction("INSULATION", {"channel", "voltage", "duration"}, [](const QVariantMap &params) -> QVariantList
{
double voltage = params.value("voltage", 500.0).toDouble();
double insulation_resistance = 500.0 + QRandomGenerator::global()->bounded(100.0);
double leakage_current = voltage / (insulation_resistance * 1e6) * 1e6;
QVariantList result;
result << insulation_resistance << leakage_current;
return result; }, "絕緣電阻測量", "測量絕緣電阻和漏電流", "measurement");
// --------------------------------------------------
// 溫度測量函數
// --------------------------------------------------
registerFunction("TEMPERATURE_RTD", {"channel", "rtd_type", "wire_mode"}, [](const QVariantMap &params) -> QVariantList
{
QString rtdType = params.value("rtd_type", "PT100").toString();
Q_UNUSED(rtdType)
double resistance = 100.0 + QRandomGenerator::global()->bounded(10.0);
double temperature = (resistance - 100.0) / 0.385;
QVariantList result;
result << temperature << resistance;
return result; }, "RTD溫度測量", "使用RTD如PT100測量溫度", "measurement");
registerFunction("TEMPERATURE_TC", {"channel", "tc_type", "cjc_channel"}, [](const QVariantMap &params) -> QVariantList
{
QString tcType = params.value("tc_type", "K").toString();
Q_UNUSED(tcType)
double voltage_mv = 4.0 + QRandomGenerator::global()->bounded(1.0);
double temperature = voltage_mv / 0.040;
QVariantList result;
result << temperature << voltage_mv;
return result; }, "熱電偶溫度測量", "使用熱電偶測量溫度", "measurement");
// --------------------------------------------------
// 數據採集函數
// --------------------------------------------------
registerFunction("DATA_ACQUISITION", {"channels", "sample_rate", "duration"}, [](const QVariantMap &params) -> QVariantList
{
QVariantList channels = params.value("channels").toList();
double sampleRate = params.value("sample_rate", 1000.0).toDouble();
double duration = params.value("duration", 1.0).toDouble();
int sampleCount = static_cast<int>(sampleRate * duration);
QVariantList dataArray;
QVariantList timestamps;
for (int i = 0; i < qMin(sampleCount, 100); i++) { // 限制最大樣本數
QVariantList sample;
for (int ch = 0; ch < channels.size(); ch++) {
sample << QRandomGenerator::global()->bounded(10.0);
}
dataArray << QVariant(sample);
timestamps << (i / sampleRate);
}
QVariantList result;
result << QVariant(dataArray) << QVariant(timestamps);
return result; }, "數據採集", "多通道數據採集", "acquisition");
// --------------------------------------------------
// 輸出控制函數
// --------------------------------------------------
registerFunction("SET_VOLTAGE", {"channel", "voltage"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
double voltage = params.value("voltage", 0.0).toDouble();
qDebug() << "設置電壓輸出: 通道=" << channel << "電壓=" << voltage;
QVariantList result;
result << true;
return result; }, "設置電壓輸出", "設置指定通道的電壓輸出", "output");
registerFunction("SET_CURRENT", {"channel", "current_ma"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
double current_ma = params.value("current_ma", 0.0).toDouble();
qDebug() << "設置電流輸出: 通道=" << channel << "電流=" << current_ma << "mA";
QVariantList result;
result << true;
return result; }, "設置電流輸出", "設置指定通道的電流輸出", "output");
registerFunction("SET_RESISTANCE", {"channel", "resistance"}, [](const QVariantMap &params) -> QVariantList
{
int channel = params.value("channel", 1).toInt();
double resistance = params.value("resistance", 100.0).toDouble();
qDebug() << "設置電阻輸出: 通道=" << channel << "電阻=" << resistance << "Ω";
QVariantList result;
result << true;
return result; }, "設置電阻輸出", "設置指定通道的模擬電阻輸出", "output");
// --------------------------------------------------
// 計算函數
// --------------------------------------------------
registerFunction("CALCULATE_ERROR", {"measured", "standard"}, [](const QVariantMap &params) -> QVariantList
{
double measured = params.value("measured", 0.0).toDouble();
double standard = params.value("standard", 1.0).toDouble();
double error = measured - standard;
double errorPercent = (standard != 0.0) ? (error / standard * 100.0) : 0.0;
QVariantList result;
result << error << errorPercent;
return result; }, "計算誤差", "計算測量值與標準值的誤差", "calculation");
registerFunction("CALCULATE_AVERAGE", {"values"}, [](const QVariantMap &params) -> QVariantList
{
QVariantList values = params.value("values").toList();
if (values.isEmpty()) {
QVariantList result;
result << 0.0 << 0.0 << 0.0 << 0.0;
return result;
}
double sum = 0.0;
double minVal = values[0].toDouble();
double maxVal = values[0].toDouble();
for (const QVariant &v : values) {
double val = v.toDouble();
sum += val;
minVal = qMin(minVal, val);
maxVal = qMax(maxVal, val);
}
double average = sum / values.size();
double sumSquares = 0.0;
for (const QVariant &v : values) {
double diff = v.toDouble() - average;
sumSquares += diff * diff;
}
double stdDev = qSqrt(sumSquares / values.size());
QVariantList result;
result << average << stdDev << minVal << maxVal;
return result; }, "計算平均值", "計算數值的平均值、標準差、最小值和最大值", "calculation");
// --------------------------------------------------
// 控制函數
// --------------------------------------------------
registerFunction("DELAY", {"duration_ms"}, [](const QVariantMap &params) -> QVariantList
{
int duration_ms = params.value("duration_ms", 1000).toInt();
qDebug() << "延遲:" << duration_ms << "ms";
QThread::msleep(duration_ms);
QVariantList result;
result << true;
return result; }, "延遲", "等待指定時間", "control");
registerFunction("WAIT_STABLE", {"channel", "threshold", "timeout"}, [](const QVariantMap &params) -> QVariantList
{
int timeout = params.value("timeout", 30000).toInt();
QThread::msleep(qMin(timeout, 2000));
double finalValue = 100.0 + QRandomGenerator::global()->bounded(0.05);
QVariantList result;
result << true << finalValue;
return result; }, "等待穩定", "等待測量值穩定在閾值範圍內", "control");
qDebug() << "已註冊" << m_functions.size() << "個內建函數";
}

View File

@@ -0,0 +1,84 @@
#ifndef FUNCTIONREGISTRY_H
#define FUNCTIONREGISTRY_H
#include <QObject>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QVariant>
#include <QVariantMap>
#include <QVariantList>
#include <functional>
/**
* @brief 功能函數註冊表
*
* 管理測量和輸出功能的註冊和執行。
* 每個功能函數接受參數映射,返回結果數組。
*/
class FunctionRegistry : public QObject
{
Q_OBJECT
public:
// 函數回調類型:接受 QVariantMap 參數,返回 QVariantList 結果
using FunctionCallback = std::function<QVariantList(const QVariantMap &)>;
struct FunctionMetadata
{
QString functionType; // 函數類型標識
QString displayName; // 顯示名稱
QString description; // 功能描述
QString category; // 類別 (measurement, output, etc.)
QStringList parameterNames; // 參數名稱列表
QVariantMap defaultParameters; // 默認參數值
FunctionCallback callback; // 回調函數
};
static FunctionRegistry *instance();
// 註冊功能函數
bool registerFunction(const QString &functionType,
const QStringList &parameterNames,
FunctionCallback callback,
const QString &displayName = QString(),
const QString &description = QString(),
const QString &category = QString());
bool registerFunction(const FunctionMetadata &metadata);
// 註銷功能函數
bool unregisterFunction(const QString &functionType);
// 執行功能函數
QVariantList executeFunction(const QString &functionType,
const QVariant &parameters);
// 查詢
bool hasFunction(const QString &functionType) const;
FunctionMetadata getFunctionMetadata(const QString &functionType) const;
QStringList getAllFunctionTypes() const;
QStringList getFunctionsByCategory(const QString &category) const;
signals:
void functionRegistered(const QString &functionType);
void functionUnregistered(const QString &functionType);
void functionExecuted(const QString &functionType, bool success);
void functionProgress(const QString &functionType, int progress, const QString &message);
private:
explicit FunctionRegistry(QObject *parent = nullptr);
~FunctionRegistry();
QVariantMap normalizeParameters(const FunctionMetadata &metadata,
const QVariant &parameters);
bool validateParameters(const FunctionMetadata &metadata,
const QVariantMap &params);
void registerBuiltinFunctions();
QMap<QString, FunctionMetadata> m_functions;
static FunctionRegistry *m_instance;
};
#endif // FUNCTIONREGISTRY_H

319
procedure/proceduredata.h Normal file
View File

@@ -0,0 +1,319 @@
#ifndef PROCEDUREDATA_H
#define PROCEDUREDATA_H
#include <QString>
#include <QStringList>
#include <QVector>
#include <QList>
#include <QVariantMap>
#include <QDateTime>
#include <QPair>
// =====================================================
// 基本枚舉類型
// =====================================================
// 步驟類型
enum class StepType
{
Manual, // 手動步驟
Automatic // 自動步驟
};
// 步驟狀態
enum class StepStatus
{
Pending, // 待執行
InProgress, // 執行中
Passed, // 已通過
Failed, // 未通過
Skipped, // 已跳過
Confirmed // 已確認(手動)
};
// 活動類型
enum class ActivityType
{
TEST_TASK_GROUP, // 測試任務組
RESULT_DISPLAY // 結果顯示
};
// 活動步驟
struct ActivityStep
{
ActivityType type = ActivityType::TEST_TASK_GROUP;
QString ref; // 引用ID
QString name; // 顯示名稱
};
// =====================================================
// 表格相關結構
// =====================================================
// 字段定義
struct FieldDefinition
{
QString id;
QString name;
QString label;
QString type; // numeric, text, selection, boolean, datetime, calculated
QVariant defaultValue;
QStringList options; // 用於 selection 類型
QString formula; // 用於 calculated 類型
QVariantMap validationRules;
bool isRequired = false;
bool isReadOnly = false;
bool editable = false;
bool visible = true;
QString unit;
QString description;
QString alignment;
int width = 100;
int decimals = 2;
bool uploadImmediately = false;
QVariantMap layoutConfig;
};
// 靜態單元格
struct StaticCell
{
int row = 0;
int column = 0;
QString field; // 字段ID用於form類型表格
QVariant value; // 靜態值
QString content; // 靜態文本內容
QString style;
};
// 表格定義
struct TableDefinition
{
QString tableId;
QString id;
QString name;
QString description;
QString tableType; // grid, form, series
int rowCount = 0;
int columnCount = 0;
QVariantMap layoutConfig;
bool isShared = false;
QString uploadStrategy; // immediate, grouped, byRow, onComplete
QList<QVariantMap> uploadGroups;
QList<FieldDefinition> columnHeaders; // 用於grid類型表格
QList<QPair<QString, QString>> rowHeaders; // 用於grid類型表格 (id, name)
QList<FieldDefinition> fields; // 用於form和series類型表格
QList<StaticCell> staticCells; // 靜態內容
};
// =====================================================
// 測試動作相關結構
// =====================================================
// 字段選擇器
struct FieldSelector
{
QString tableRef;
int row = -1;
int column = -1;
QString fieldName;
QStringList fields; // 用於form類型表格
QList<QPair<QString, QString>> cells; // 用於grid類型表格 (row, column)
bool ignore = false;
};
// 測試動作
struct TestAction
{
QString actionId;
QString id;
QString name;
QString actionType;
QString functionName;
QString document; // HTML格式的說明文檔
QString mode; // manual 或 auto
QString sequence;
QString functionType; // RESISTANCE_4WIRE, VOLTAGE_V, DATA_ACQUISITION 等
QString channel; // input1, input2, output1, output2
QVariantMap params; // 函數參數
QVariant functionParameters;
QVariantMap metadata;
QMap<QString, FieldSelector> inputs;
QMap<QString, FieldSelector> outputs;
QList<FieldSelector> dataFields;
QVariantMap validationCriteria;
QString uploadStrategy;
QStringList uploadFields;
int timeout = 30000;
int retryCount = 0;
// 運行時狀態
bool isCompleted = false;
bool isWaitingConfirm = false;
QVariantList results;
};
// 測試活動組(階段)
struct TestActivityGroup
{
QString groupId;
QString id;
QString name;
QString description;
QString document;
QString skipCondition;
QVariantMap metadata;
QStringList tableRefs;
QList<TestAction> actions;
// 運行時狀態
bool isCompleted = false;
bool isActionsParsed = false;
};
// 測試任務組
struct TestTaskGroup
{
QString taskGroupId;
QString id;
QString name;
QString description;
QString executionMode;
int loopCount = 1;
QVariantMap metadata;
QStringList tableRefs;
QList<TestActivityGroup> stages;
// 運行時狀態
bool isCompleted = false;
bool isParsed = false;
};
// 結果顯示
struct ResultDisplay
{
QString displayId;
QString id;
QString name;
QString displayType;
QString document;
QString tableRef;
QVariantMap passCondition;
QStringList tableRefs;
};
// =====================================================
// 程序配置結構
// =====================================================
struct ProcedureConfig
{
QString procedureId;
QString procedureName;
QString version;
QString description;
QString author;
QString document;
QVariantMap metadata;
// 活動序列 (旧格式,用于 ProcedureEngine)
QList<ActivityStep> activitySequence;
// 活動序列 (新格式stores QVariant of TestTaskGroup or ResultDisplay用于新解析器)
QList<QVariant> activityVariants;
// 定義存儲
QMap<QString, TestTaskGroup> testTaskGroups;
QMap<QString, TestActivityGroup> testActivityGroups;
QMap<QString, TestAction> testActions;
QMap<QString, ResultDisplay> resultDisplays;
QMap<QString, TableDefinition> tables;
// 舊版兼容
QList<TestTaskGroup> taskGroups;
};
// =====================================================
// 舊版兼容結構用於ProcedureManager
// =====================================================
// 表格字段定義(舊版)
struct TableField
{
QString id;
QString name;
QString type; // numeric, text, boolean
QString unit;
bool isRequired = false;
bool isHighlighted = false;
};
// 表格數據(舊版)
struct TableData
{
QString id;
QString name;
QString description;
QVector<TableField> columns;
QVector<QVariantMap> rows;
};
// 步驟定義(舊版)
struct StepData
{
QString id;
QString content;
StepType type = StepType::Manual;
StepStatus status = StepStatus::Pending;
QStringList tableRefs;
QStringList highlightFields;
QString result;
QDateTime executedAt;
QString executedBy;
QString cancelReason;
};
// 任務組定義(舊版)
struct TaskGroup
{
QString id;
QString name;
QVector<StepData> steps;
};
// 程序定義(舊版)
struct ProcedureData
{
QString id;
QString name;
QString version;
QString description;
QVector<TaskGroup> taskGroups;
QVector<TableData> tables;
QString workOrderId;
int completedSteps = 0;
int totalSteps = 0;
};
// 程序摘要(用於列表顯示)
struct ProcedureSummary
{
QString id;
QString name;
QString version;
QString description;
QString filePath;
};
// Qt Meta-Type Declarations for QVariant support
Q_DECLARE_METATYPE(FieldDefinition)
Q_DECLARE_METATYPE(StaticCell)
Q_DECLARE_METATYPE(TableDefinition)
Q_DECLARE_METATYPE(FieldSelector)
Q_DECLARE_METATYPE(TestAction)
Q_DECLARE_METATYPE(TestActivityGroup)
Q_DECLARE_METATYPE(TestTaskGroup)
Q_DECLARE_METATYPE(ResultDisplay)
Q_DECLARE_METATYPE(ProcedureConfig)
Q_DECLARE_METATYPE(ActivityStep)
#endif // PROCEDUREDATA_H

View File

@@ -0,0 +1,865 @@
#include "procedureengine.h"
#include "procedureparser.h"
#include <QDebug>
#include <QFile>
#include <QTimer>
#include <QRegularExpression>
// =============================================================================
// 單例實現
// =============================================================================
ProcedureEngine *ProcedureEngine::s_instance = nullptr;
ProcedureEngine *ProcedureEngine::instance()
{
if (!s_instance)
{
s_instance = new ProcedureEngine();
}
return s_instance;
}
ProcedureEngine::ProcedureEngine(QObject *parent)
: QObject(parent), m_parser(new ProcedureParser(this)), m_status(ProcedureStatus::Idle), m_executionTimer(new QTimer(this))
{
connect(m_executionTimer, &QTimer::timeout, this, &ProcedureEngine::onExecutionTimeout);
}
ProcedureEngine::~ProcedureEngine()
{
}
// =============================================================================
// 配置載入
// =============================================================================
bool ProcedureEngine::loadProcedure(const QString &configPath)
{
if (m_status == ProcedureStatus::Running || m_status == ProcedureStatus::Paused)
{
qWarning() << "程序正在執行中,無法載入新配置";
return false;
}
// 檢查文件是否存在
if (!QFile::exists(configPath))
{
emit errorOccurred(QString("配置文件不存在: %1").arg(configPath));
return false;
}
// 解析配置
if (!m_parser->loadConfig(configPath))
{
emit errorOccurred(QString("配置文件解析失敗: %1").arg(configPath));
return false;
}
m_config = m_parser->parseProcedureConfig();
// 驗證配置
if (!m_parser->validateConfig())
{
QStringList errors = m_parser->getValidationErrors();
emit errorOccurred(QString("配置驗證失敗:\n%1").arg(errors.join("\n")));
return false;
}
// 初始化執行上下文
resetExecutionContext();
m_configPath = configPath;
setStatus(ProcedureStatus::Loaded);
emit procedureLoaded(m_config.procedureName);
qDebug() << "程序載入成功:" << m_config.procedureName;
qDebug() << " - 任務組數量:" << m_config.testTaskGroups.size();
qDebug() << " - 活動序列數量:" << m_config.activitySequence.size();
return true;
}
void ProcedureEngine::resetExecutionContext()
{
m_context = ExecutionContext();
m_context.currentActivityIndex = -1;
m_context.currentStageIndex = -1;
m_context.currentActionIndex = -1;
m_context.loopIteration = 0;
m_context.startTime = QDateTime();
}
// =============================================================================
// 程序控制
// =============================================================================
void ProcedureEngine::startProcedure()
{
if (m_status != ProcedureStatus::Loaded && m_status != ProcedureStatus::Completed)
{
qWarning() << "程序狀態不正確,無法啟動";
return;
}
resetExecutionContext();
m_context.startTime = QDateTime::currentDateTime();
setStatus(ProcedureStatus::Running);
emit procedureStarted();
// 移動到第一個活動
moveToFirstActivity();
}
void ProcedureEngine::pauseProcedure()
{
if (m_status != ProcedureStatus::Running)
{
return;
}
m_executionTimer->stop();
setStatus(ProcedureStatus::Paused);
emit procedurePaused();
}
void ProcedureEngine::resumeProcedure()
{
if (m_status != ProcedureStatus::Paused)
{
return;
}
setStatus(ProcedureStatus::Running);
emit procedureResumed();
// 繼續執行當前動作
executeCurrentAction();
}
void ProcedureEngine::stopProcedure()
{
if (m_status == ProcedureStatus::Idle || m_status == ProcedureStatus::Loaded)
{
return;
}
m_executionTimer->stop();
setStatus(ProcedureStatus::Stopped);
emit procedureStopped();
}
void ProcedureEngine::resetProcedure()
{
stopProcedure();
resetExecutionContext();
setStatus(ProcedureStatus::Loaded);
// 清除所有表格數據
m_tableData.clear();
}
// =============================================================================
// 活動導航
// =============================================================================
void ProcedureEngine::moveToFirstActivity()
{
if (m_config.activitySequence.isEmpty())
{
completeProcedure();
return;
}
m_context.currentActivityIndex = 0;
m_context.currentStageIndex = -1;
m_context.currentActionIndex = -1;
enterCurrentActivity();
}
bool ProcedureEngine::moveToNextActivity()
{
if (m_status != ProcedureStatus::Running)
{
return false;
}
int nextIndex = m_context.currentActivityIndex + 1;
if (nextIndex >= m_config.activitySequence.size())
{
// 沒有更多活動
completeProcedure();
return false;
}
m_context.currentActivityIndex = nextIndex;
m_context.currentStageIndex = -1;
m_context.currentActionIndex = -1;
enterCurrentActivity();
return true;
}
bool ProcedureEngine::moveToPreviousActivity()
{
if (m_status != ProcedureStatus::Running && m_status != ProcedureStatus::Paused)
{
return false;
}
int prevIndex = m_context.currentActivityIndex - 1;
if (prevIndex < 0)
{
return false;
}
m_context.currentActivityIndex = prevIndex;
m_context.currentStageIndex = -1;
m_context.currentActionIndex = -1;
enterCurrentActivity();
return true;
}
bool ProcedureEngine::jumpToActivity(int index)
{
if (index < 0 || index >= m_config.activitySequence.size())
{
return false;
}
m_context.currentActivityIndex = index;
m_context.currentStageIndex = -1;
m_context.currentActionIndex = -1;
enterCurrentActivity();
return true;
}
void ProcedureEngine::enterCurrentActivity()
{
if (m_context.currentActivityIndex < 0 ||
m_context.currentActivityIndex >= m_config.activitySequence.size())
{
return;
}
const ActivityStep &step = m_config.activitySequence[m_context.currentActivityIndex];
qDebug() << "進入活動:" << step.name << "(" << step.ref << ")";
emit activityStarted(m_context.currentActivityIndex, step);
if (step.type == ActivityType::TEST_TASK_GROUP)
{
// 開始執行任務組
startTaskGroupExecution(step.ref);
}
else if (step.type == ActivityType::RESULT_DISPLAY)
{
// 顯示結果
displayResult(step.ref);
}
}
// =============================================================================
// 任務組執行
// =============================================================================
void ProcedureEngine::startTaskGroupExecution(const QString &taskGroupRef)
{
if (!m_config.testTaskGroups.contains(taskGroupRef))
{
emit errorOccurred(QString("找不到任務組: %1").arg(taskGroupRef));
return;
}
const TestTaskGroup &taskGroup = m_config.testTaskGroups[taskGroupRef];
qDebug() << "開始執行任務組:" << taskGroup.name;
qDebug() << " - 階段數量:" << taskGroup.stages.size();
emit taskGroupStarted(taskGroupRef, taskGroup.name);
// 初始化表格
for (const QString &tableRef : taskGroup.tableRefs)
{
initializeTable(tableRef);
}
// 移動到第一個階段
if (!taskGroup.stages.isEmpty())
{
m_context.currentStageIndex = 0;
m_context.currentActionIndex = -1;
enterCurrentStage(taskGroupRef);
}
else
{
// 沒有階段,完成任務組
completeTaskGroup(taskGroupRef);
}
}
void ProcedureEngine::enterCurrentStage(const QString &taskGroupRef)
{
if (!m_config.testTaskGroups.contains(taskGroupRef))
{
return;
}
const TestTaskGroup &taskGroup = m_config.testTaskGroups[taskGroupRef];
if (m_context.currentStageIndex < 0 ||
m_context.currentStageIndex >= taskGroup.stages.size())
{
completeTaskGroup(taskGroupRef);
return;
}
const TestActivityGroup &stage = taskGroup.stages[m_context.currentStageIndex];
qDebug() << "進入階段:" << stage.name;
emit stageStarted(m_context.currentStageIndex, stage.name);
// 檢查跳過條件
if (!stage.skipCondition.isEmpty() && evaluateCondition(stage.skipCondition))
{
qDebug() << "階段被跳過:" << stage.name;
moveToNextStage(taskGroupRef);
return;
}
// 移動到第一個動作
if (!stage.actions.isEmpty())
{
m_context.currentActionIndex = 0;
executeCurrentAction();
}
else
{
// 沒有動作,移動到下一個階段
moveToNextStage(taskGroupRef);
}
}
void ProcedureEngine::moveToNextStage(const QString &taskGroupRef)
{
if (!m_config.testTaskGroups.contains(taskGroupRef))
{
return;
}
const TestTaskGroup &taskGroup = m_config.testTaskGroups[taskGroupRef];
int nextStageIndex = m_context.currentStageIndex + 1;
if (nextStageIndex >= taskGroup.stages.size())
{
// 檢查循環
m_context.loopIteration++;
if (m_context.loopIteration < taskGroup.loopCount)
{
// 繼續下一輪
m_context.currentStageIndex = 0;
m_context.currentActionIndex = -1;
enterCurrentStage(taskGroupRef);
}
else
{
// 完成任務組
completeTaskGroup(taskGroupRef);
}
return;
}
m_context.currentStageIndex = nextStageIndex;
m_context.currentActionIndex = -1;
enterCurrentStage(taskGroupRef);
}
void ProcedureEngine::completeTaskGroup(const QString &taskGroupRef)
{
qDebug() << "任務組完成:" << taskGroupRef;
m_context.loopIteration = 0;
emit taskGroupCompleted(taskGroupRef);
// 自動移動到下一個活動(如果處於運行狀態)
if (m_status == ProcedureStatus::Running)
{
// 使用延遲以允許UI更新
QTimer::singleShot(100, this, [this]()
{ moveToNextActivity(); });
}
}
// =============================================================================
// 動作執行
// =============================================================================
void ProcedureEngine::executeCurrentAction()
{
if (m_status != ProcedureStatus::Running)
{
return;
}
const ActivityStep &step = m_config.activitySequence[m_context.currentActivityIndex];
if (step.type != ActivityType::TEST_TASK_GROUP)
{
return;
}
if (!m_config.testTaskGroups.contains(step.ref))
{
return;
}
const TestTaskGroup &taskGroup = m_config.testTaskGroups[step.ref];
if (m_context.currentStageIndex < 0 ||
m_context.currentStageIndex >= taskGroup.stages.size())
{
return;
}
const TestActivityGroup &stage = taskGroup.stages[m_context.currentStageIndex];
if (m_context.currentActionIndex < 0 ||
m_context.currentActionIndex >= stage.actions.size())
{
// 階段動作執行完畢,移動到下一個階段
emit stageCompleted(m_context.currentStageIndex);
moveToNextStage(step.ref);
return;
}
const TestAction &action = stage.actions[m_context.currentActionIndex];
qDebug() << "執行動作:" << action.actionId << "-" << action.functionName;
emit actionStarted(m_context.currentActionIndex, action.actionId);
// 設置超時
if (action.timeout > 0)
{
m_executionTimer->start(action.timeout);
}
// 請求執行動作(通過信號通知外部執行)
emit executeActionRequested(action);
}
void ProcedureEngine::onActionCompleted(const QString &actionId, const QVariantMap &results)
{
m_executionTimer->stop();
qDebug() << "動作完成:" << actionId;
emit actionCompleted(m_context.currentActionIndex, actionId, results);
// 處理結果(寫入表格等)
processActionResults(actionId, results);
// 移動到下一個動作
moveToNextAction();
}
void ProcedureEngine::onActionFailed(const QString &actionId, const QString &error)
{
m_executionTimer->stop();
qWarning() << "動作執行失敗:" << actionId << "-" << error;
emit actionFailed(m_context.currentActionIndex, actionId, error);
// 根據配置決定是否繼續
// TODO: 可配置的錯誤處理策略
moveToNextAction();
}
void ProcedureEngine::moveToNextAction()
{
if (m_status != ProcedureStatus::Running)
{
return;
}
m_context.currentActionIndex++;
executeCurrentAction();
}
void ProcedureEngine::onExecutionTimeout()
{
qWarning() << "動作執行超時";
const ActivityStep &step = m_config.activitySequence[m_context.currentActivityIndex];
if (step.type == ActivityType::TEST_TASK_GROUP &&
m_config.testTaskGroups.contains(step.ref))
{
const TestTaskGroup &taskGroup = m_config.testTaskGroups[step.ref];
if (m_context.currentStageIndex >= 0 &&
m_context.currentStageIndex < taskGroup.stages.size())
{
const TestActivityGroup &stage = taskGroup.stages[m_context.currentStageIndex];
if (m_context.currentActionIndex >= 0 &&
m_context.currentActionIndex < stage.actions.size())
{
const TestAction &action = stage.actions[m_context.currentActionIndex];
onActionFailed(action.actionId, "執行超時");
return;
}
}
}
// 如果無法確定當前動作,直接移動到下一個
moveToNextAction();
}
// =============================================================================
// 結果處理
// =============================================================================
void ProcedureEngine::processActionResults(const QString &actionId, const QVariantMap &results)
{
// 獲取當前動作定義
const ActivityStep &step = m_config.activitySequence[m_context.currentActivityIndex];
if (step.type != ActivityType::TEST_TASK_GROUP)
{
return;
}
if (!m_config.testTaskGroups.contains(step.ref))
{
return;
}
const TestTaskGroup &taskGroup = m_config.testTaskGroups[step.ref];
if (m_context.currentStageIndex < 0 ||
m_context.currentStageIndex >= taskGroup.stages.size())
{
return;
}
const TestActivityGroup &stage = taskGroup.stages[m_context.currentStageIndex];
if (m_context.currentActionIndex < 0 ||
m_context.currentActionIndex >= stage.actions.size())
{
return;
}
const TestAction &action = stage.actions[m_context.currentActionIndex];
// 根據輸出映射寫入表格
for (auto it = action.outputs.begin(); it != action.outputs.end(); ++it)
{
const QString &outputKey = it.key();
const FieldSelector &selector = it.value();
if (results.contains(outputKey))
{
QVariant value = results[outputKey];
setTableCellValue(selector.tableRef, selector.row, selector.column, value);
}
}
}
void ProcedureEngine::displayResult(const QString &displayRef)
{
if (!m_config.resultDisplays.contains(displayRef))
{
emit errorOccurred(QString("找不到結果顯示: %1").arg(displayRef));
return;
}
const ResultDisplay &display = m_config.resultDisplays[displayRef];
qDebug() << "顯示結果:" << display.name;
emit resultDisplayRequested(displayRef, display);
}
void ProcedureEngine::completeProcedure()
{
qDebug() << "程序執行完成";
setStatus(ProcedureStatus::Completed);
emit procedureCompleted();
}
// =============================================================================
// 表格數據管理
// =============================================================================
void ProcedureEngine::initializeTable(const QString &tableRef)
{
if (!m_config.tables.contains(tableRef))
{
qWarning() << "找不到表格定義:" << tableRef;
return;
}
const TableDefinition &tableDef = m_config.tables[tableRef];
// 初始化表格數據
QVector<QVector<QVariant>> data;
int rowCount = tableDef.rowCount > 0 ? tableDef.rowCount : 10;
int colCount = tableDef.fields.size() > 0 ? tableDef.fields.size() : tableDef.columnCount;
for (int row = 0; row < rowCount; row++)
{
QVector<QVariant> rowData;
for (int col = 0; col < colCount; col++)
{
if (col < tableDef.fields.size())
{
rowData.append(tableDef.fields[col].defaultValue);
}
else
{
rowData.append(QVariant());
}
}
data.append(rowData);
}
// 應用靜態單元格
for (const StaticCell &cell : tableDef.staticCells)
{
if (cell.row < data.size() && cell.column < data[cell.row].size())
{
data[cell.row][cell.column] = cell.value;
}
}
m_tableData[tableRef] = data;
emit tableInitialized(tableRef, tableDef);
}
QVariant ProcedureEngine::getTableCellValue(const QString &tableRef, int row, int column) const
{
if (!m_tableData.contains(tableRef))
{
return QVariant();
}
const auto &data = m_tableData[tableRef];
if (row < 0 || row >= data.size())
{
return QVariant();
}
if (column < 0 || column >= data[row].size())
{
return QVariant();
}
return data[row][column];
}
void ProcedureEngine::setTableCellValue(const QString &tableRef, int row, int column, const QVariant &value)
{
if (!m_tableData.contains(tableRef))
{
return;
}
auto &data = m_tableData[tableRef];
if (row < 0 || row >= data.size())
{
return;
}
// 自動擴展列
while (column >= data[row].size())
{
data[row].append(QVariant());
}
data[row][column] = value;
emit tableDataChanged(tableRef, row, column, value);
}
QVector<QVector<QVariant>> ProcedureEngine::getTableData(const QString &tableRef) const
{
if (m_tableData.contains(tableRef))
{
return m_tableData[tableRef];
}
return QVector<QVector<QVariant>>();
}
// =============================================================================
// 條件評估
// =============================================================================
bool ProcedureEngine::evaluateCondition(const QString &condition)
{
// 簡單的條件評估
// TODO: 實現完整的表達式解析器
if (condition.isEmpty() || condition == "true")
{
return true;
}
if (condition == "false")
{
return false;
}
// 支持簡單的表格引用條件
// 格式: table[row][col] == value
static QRegularExpression tableRefRe(R"((\w+)\[(\d+)\]\[(\d+)\]\s*(==|!=|>|<|>=|<=)\s*(.+))");
QRegularExpressionMatch match = tableRefRe.match(condition);
if (match.hasMatch())
{
QString tableRef = match.captured(1);
int row = match.captured(2).toInt();
int col = match.captured(3).toInt();
QString op = match.captured(4);
QString valueStr = match.captured(5).trimmed();
QVariant cellValue = getTableCellValue(tableRef, row, col);
QVariant compareValue = valueStr;
// 簡單比較
if (op == "==")
{
return cellValue == compareValue;
}
else if (op == "!=")
{
return cellValue != compareValue;
}
else if (op == ">")
{
return cellValue.toDouble() > compareValue.toDouble();
}
else if (op == "<")
{
return cellValue.toDouble() < compareValue.toDouble();
}
else if (op == ">=")
{
return cellValue.toDouble() >= compareValue.toDouble();
}
else if (op == "<=")
{
return cellValue.toDouble() <= compareValue.toDouble();
}
}
qWarning() << "無法評估條件:" << condition;
return false;
}
// =============================================================================
// 狀態查詢
// =============================================================================
void ProcedureEngine::setStatus(ProcedureStatus status)
{
if (m_status != status)
{
m_status = status;
emit statusChanged(status);
}
}
ProcedureStatus ProcedureEngine::status() const
{
return m_status;
}
ProcedureConfig ProcedureEngine::config() const
{
return m_config;
}
ExecutionContext ProcedureEngine::executionContext() const
{
return m_context;
}
ActivityStep ProcedureEngine::getCurrentActivity() const
{
if (m_context.currentActivityIndex >= 0 &&
m_context.currentActivityIndex < m_config.activitySequence.size())
{
return m_config.activitySequence[m_context.currentActivityIndex];
}
return ActivityStep();
}
TestTaskGroup ProcedureEngine::getCurrentTaskGroup() const
{
ActivityStep step = getCurrentActivity();
if (step.type == ActivityType::TEST_TASK_GROUP &&
m_config.testTaskGroups.contains(step.ref))
{
return m_config.testTaskGroups[step.ref];
}
return TestTaskGroup();
}
TestActivityGroup ProcedureEngine::getCurrentStage() const
{
TestTaskGroup taskGroup = getCurrentTaskGroup();
if (m_context.currentStageIndex >= 0 &&
m_context.currentStageIndex < taskGroup.stages.size())
{
return taskGroup.stages[m_context.currentStageIndex];
}
return TestActivityGroup();
}
TestAction ProcedureEngine::getCurrentAction() const
{
TestActivityGroup stage = getCurrentStage();
if (m_context.currentActionIndex >= 0 &&
m_context.currentActionIndex < stage.actions.size())
{
return stage.actions[m_context.currentActionIndex];
}
return TestAction();
}
int ProcedureEngine::getActivityCount() const
{
return m_config.activitySequence.size();
}
int ProcedureEngine::getCurrentActivityIndex() const
{
return m_context.currentActivityIndex;
}
double ProcedureEngine::getProgress() const
{
if (m_config.activitySequence.isEmpty())
{
return 0.0;
}
// 基於活動數量計算簡單進度
double activityProgress = static_cast<double>(m_context.currentActivityIndex) / m_config.activitySequence.size();
// 可以進一步細化到階段和動作級別
// TODO: 更精確的進度計算
return qBound(0.0, activityProgress * 100.0, 100.0);
}

172
procedure/procedureengine.h Normal file
View File

@@ -0,0 +1,172 @@
#ifndef PROCEDUREENGINE_H
#define PROCEDUREENGINE_H
#include "proceduredata.h"
#include <QObject>
#include <QDateTime>
#include <QMap>
#include <QVariant>
#include <QVector>
class ProcedureParser;
class FunctionRegistry;
class QTimer;
/**
* @brief 程序執行狀態
*/
enum class ProcedureStatus
{
Idle, // 空閒
Loaded, // 已載入
Running, // 運行中
Paused, // 已暫停
Stopped, // 已停止
Completed, // 已完成
Error // 錯誤
};
/**
* @brief 執行上下文
*/
struct ExecutionContext
{
int currentActivityIndex = -1;
int currentStageIndex = -1;
int currentActionIndex = -1;
int loopIteration = 0;
QDateTime startTime;
QDateTime endTime;
};
/**
* @brief 程序執行引擎
*
* 負責程序的載入、執行、暫停、恢復和停止。
* 管理執行上下文、活動序列和表格數據。
*/
class ProcedureEngine : public QObject
{
Q_OBJECT
public:
static ProcedureEngine *instance();
// 配置載入
bool loadProcedure(const QString &configPath);
// 程序控制
void startProcedure();
void pauseProcedure();
void resumeProcedure();
void stopProcedure();
void resetProcedure();
// 活動導航
bool moveToNextActivity();
bool moveToPreviousActivity();
bool jumpToActivity(int index);
// 狀態查詢
ProcedureStatus status() const;
ProcedureConfig config() const;
ExecutionContext executionContext() const;
// 獲取當前執行項
ActivityStep getCurrentActivity() const;
TestTaskGroup getCurrentTaskGroup() const;
TestActivityGroup getCurrentStage() const;
TestAction getCurrentAction() const;
// 進度
int getActivityCount() const;
int getCurrentActivityIndex() const;
double getProgress() const;
// 表格數據管理
QVariant getTableCellValue(const QString &tableRef, int row, int column) const;
void setTableCellValue(const QString &tableRef, int row, int column, const QVariant &value);
QVector<QVector<QVariant>> getTableData(const QString &tableRef) const;
public slots:
void onActionCompleted(const QString &actionId, const QVariantMap &results);
void onActionFailed(const QString &actionId, const QString &error);
signals:
// 程序狀態信號
void procedureLoaded(const QString &name);
void procedureStarted();
void procedurePaused();
void procedureResumed();
void procedureStopped();
void procedureCompleted();
void statusChanged(ProcedureStatus status);
void errorOccurred(const QString &error);
// 活動信號
void activityStarted(int index, const ActivityStep &step);
void taskGroupStarted(const QString &ref, const QString &name);
void taskGroupCompleted(const QString &ref);
void stageStarted(int index, const QString &name);
void stageCompleted(int index);
void actionStarted(int index, const QString &actionId);
void actionCompleted(int index, const QString &actionId, const QVariantMap &results);
void actionFailed(int index, const QString &actionId, const QString &error);
// 執行請求
void executeActionRequested(const TestAction &action);
void resultDisplayRequested(const QString &displayRef, const ResultDisplay &display);
// 表格信號
void tableInitialized(const QString &tableRef, const TableDefinition &definition);
void tableDataChanged(const QString &tableRef, int row, int column, const QVariant &value);
private:
explicit ProcedureEngine(QObject *parent = nullptr);
~ProcedureEngine();
// 執行控制
void resetExecutionContext();
void setStatus(ProcedureStatus status);
// 活動執行
void moveToFirstActivity();
void enterCurrentActivity();
void startTaskGroupExecution(const QString &taskGroupRef);
void enterCurrentStage(const QString &taskGroupRef);
void moveToNextStage(const QString &taskGroupRef);
void completeTaskGroup(const QString &taskGroupRef);
// 動作執行
void executeCurrentAction();
void moveToNextAction();
void processActionResults(const QString &actionId, const QVariantMap &results);
// 結果顯示
void displayResult(const QString &displayRef);
void completeProcedure();
// 表格管理
void initializeTable(const QString &tableRef);
// 條件評估
bool evaluateCondition(const QString &condition);
private slots:
void onExecutionTimeout();
private:
static ProcedureEngine *s_instance;
ProcedureParser *m_parser;
ProcedureStatus m_status;
ProcedureConfig m_config;
ExecutionContext m_context;
QString m_configPath;
QTimer *m_executionTimer;
QMap<QString, QVector<QVector<QVariant>>> m_tableData;
};
#endif // PROCEDUREENGINE_H

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;
}

View File

@@ -0,0 +1,58 @@
#ifndef PROCEDUREMANAGER_H
#define PROCEDUREMANAGER_H
#include <QObject>
#include <QVector>
#include <QMap>
#include "proceduredata.h"
class ProcedureManager : public QObject
{
Q_OBJECT
public:
explicit ProcedureManager(QObject *parent = nullptr);
~ProcedureManager();
// 加载规程列表
QVector<ProcedureSummary> loadProcedureList(const QString &directory);
// 搜索规程
QVector<ProcedureSummary> searchProcedures(const QString &keyword);
// 加载规程详情
ProcedureData loadProcedure(const QString &procedureId);
// 设置工单号
void setWorkOrderId(const QString &workOrderId);
QString workOrderId() const { return m_workOrderId; }
// 步骤操作
bool confirmStep(const QString &procedureId, const QString &stepId);
bool cancelStepConfirm(const QString &procedureId, const QString &stepId, const QString &reason);
bool executeAutoStep(const QString &procedureId, const QString &stepId);
// 获取表格数据
TableData getTableData(const QString &procedureId, const QString &tableId);
bool updateTableData(const QString &procedureId, const QString &tableId, const QVector<QVariantMap> &rows);
signals:
void procedureLoaded(const ProcedureData &procedure);
void stepStatusChanged(const QString &stepId, StepStatus status);
void tableDataUpdated(const QString &tableId);
private:
ProcedureData parseProcedureYaml(const QString &filePath);
void createSampleProcedures();
void createMockExceptionData();
ProcedureData loadMockProcedureFromJson(const QString &procedureId);
StepType parseStepType(const QString &typeStr);
StepStatus parseStepStatus(const QString &statusStr);
QString m_workOrderId;
QString m_procedureDirectory;
QVector<ProcedureSummary> m_procedureList;
QMap<QString, ProcedureData> m_loadedProcedures;
};
#endif // PROCEDUREMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
#pragma once
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
#include <memory>
#include <ryml.hpp>
// Include existing data structures
#include "proceduredata.h"
class ProcedureParser : public QObject
{
Q_OBJECT
public:
explicit ProcedureParser(QObject *parent = nullptr);
~ProcedureParser();
// Configuration file loading and parsing
bool loadConfig(const QString &filePath);
ProcedureConfig parseProcedureConfig();
// Configuration validation
bool validateConfig();
QStringList getValidationErrors() const;
// Table definition extraction
TableDefinition getTableDefinition(const QString &tableRef);
QMap<QString, TableDefinition> getAllTableDefinitions();
// Lazy parsing - parse TestTaskGroup stages on demand (Stage 1: load stage
// metadata)
bool parseTestTaskGroupStages(TestTaskGroup &group, const QString &groupId);
// Lazy parsing - parse TestActivityGroup actions on demand (Stage 2: load
// actions)
bool parseTestActivityGroupActions(TestActivityGroup &group, const QString &groupId);
signals:
void configLoaded(const QString &filePath);
void configError(const QString &error, int line, int column);
private:
std::unique_ptr<ryml::Tree> tree;
QMap<QString, ryml::ConstNodeRef> referenceCache;
QMap<QString, ryml::ConstNodeRef> stageNodeCache;
QMap<QString, ryml::ConstNodeRef> activityNodeCache; // Cache for lazy parsing of TestTaskGroups
QStringList validationErrors;
QString configFilePath;
std::string yamlContent; // Store YAML content for in-place parsing
// File parsing methods
bool parseFile(const QString &filePath);
// Reference handling
void buildReferenceCache();
ryml::ConstNodeRef resolveReference(const QString &ref);
QString extractRefPath(const ryml::ConstNodeRef &node);
// Validation methods
bool validateActivitySequence();
bool validateTableDefinitions();
// Parsing helper methods
QString nodeToQString(const ryml::ConstNodeRef &node);
QVariant nodeToQVariant(const ryml::ConstNodeRef &node);
QVariantMap nodeToQVariantMap(const ryml::ConstNodeRef &node);
QVariantList nodeToQVariantList(const ryml::ConstNodeRef &node);
// Structure parsing methods
FieldDefinition parseFieldDefinition(const ryml::ConstNodeRef &node);
TableDefinition parseTableDefinition(const ryml::ConstNodeRef &node);
StaticCell parseStaticCell(const ryml::ConstNodeRef &node);
FieldSelector parseFieldSelector(const ryml::ConstNodeRef &node);
TestAction parseTestAction(const ryml::ConstNodeRef &node);
TestActivityGroup parseTestActivityGroup(const ryml::ConstNodeRef &node);
TestTaskGroup parseTestTaskGroup(const ryml::ConstNodeRef &node);
ResultDisplay parseResultDisplay(const ryml::ConstNodeRef &node);
};