866 lines
21 KiB
C++
866 lines
21 KiB
C++
#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);
|
|
}
|