first commit
This commit is contained in:
479
procedure/functionregistry.cpp
Normal file
479
procedure/functionregistry.cpp
Normal 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 ¶meterNames,
|
||||
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 ¶meters)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
Q_UNUSED(metadata)
|
||||
Q_UNUSED(params)
|
||||
// 簡單驗證 - 可以擴展
|
||||
return true;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 函數執行
|
||||
// =============================================================================
|
||||
|
||||
QVariantList FunctionRegistry::executeFunction(const QString &functionType,
|
||||
const QVariant ¶meters)
|
||||
{
|
||||
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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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() << "個內建函數";
|
||||
}
|
||||
84
procedure/functionregistry.h
Normal file
84
procedure/functionregistry.h
Normal 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 ¶meterNames,
|
||||
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 ¶meters);
|
||||
|
||||
// 查詢
|
||||
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 ¶meters);
|
||||
bool validateParameters(const FunctionMetadata &metadata,
|
||||
const QVariantMap ¶ms);
|
||||
|
||||
void registerBuiltinFunctions();
|
||||
|
||||
QMap<QString, FunctionMetadata> m_functions;
|
||||
static FunctionRegistry *m_instance;
|
||||
};
|
||||
|
||||
#endif // FUNCTIONREGISTRY_H
|
||||
319
procedure/proceduredata.h
Normal file
319
procedure/proceduredata.h
Normal 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
|
||||
865
procedure/procedureengine.cpp
Normal file
865
procedure/procedureengine.cpp
Normal 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
172
procedure/procedureengine.h
Normal 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
|
||||
655
procedure/proceduremanager.cpp
Normal file
655
procedure/proceduremanager.cpp
Normal 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;
|
||||
}
|
||||
58
procedure/proceduremanager.h
Normal file
58
procedure/proceduremanager.h
Normal 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
|
||||
1594
procedure/procedureparser.cpp
Normal file
1594
procedure/procedureparser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
83
procedure/procedureparser.h
Normal file
83
procedure/procedureparser.h
Normal 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);
|
||||
};
|
||||
Reference in New Issue
Block a user