first commit
This commit is contained in:
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);
|
||||
}
|
||||
Reference in New Issue
Block a user