feat(numerical):避免建网未完成时求解重复生成网格

- 增加 DataManager 建网状态和版本跟踪
- 求解前等待当前网格完成主线程提交
- 支持等待建网期间立即停止求解
- 完善连续刷新、失败及窗口关闭时的状态清理
- 更新求解等待提示的中文翻译
feature/grid-solver-workflow-20260827
lh 2 weeks ago
parent 0821718c52
commit 98a63a9ee3

Binary file not shown.

@ -2830,6 +2830,10 @@ Reason: %1</source>
<source>Preparing input snapshot...</source>
<translation>...</translation>
</message>
<message>
<source>Waiting for current grid generation...</source>
<translation>...</translation>
</message>
<message>
<source>Stop</source>
<translation></translation>

@ -497,6 +497,12 @@ public:
quint64 nGridInputRevision,
const QVector<nmSolverWellRef>& vecSolverWellOrder,
vtkSmartPointer<vtkUnstructuredGrid> pGrid);
/** @brief 主线程登记当前分析正在生成指定输入版本的 PEBI 网格。 */
void beginPebiGridGeneration(quint64 nGridInputRevision);
/** @brief 主线程结束当前建网活动,并通知等待同一分析的求解流程。 */
void endPebiGridGeneration(bool bSucceeded);
/** @brief 判断当前分析是否仍处于防抖等待、后台建网或待提交阶段。 */
bool isPebiGridGenerationActive() const;
/** @brief 将当前输入版本登记为已成功生成网格。 */
void markPebiGridBuilt();
/** @brief 仅在后台划分期间输入未变化时登记网格成功。 */
@ -745,6 +751,9 @@ public:
void sigWellRemoved(QString wellCode, QString wellName);
/// @brief 参数面板中的对象列表或对象名称发生变化
void sigGeometryListChanged();
/** @brief 当前分析最后一次排队的 PEBI 建网流程已经结束。 */
void sigPebiGridGenerationFinished(quint64 nGridInputRevision,
bool bSucceeded);
private:
/** @brief 创建本数据管理器的成果窗口;只用于读取本成果上下文,不拥有对象。 */
@ -754,6 +763,9 @@ public:
/** @brief DataManager 析构时等待全部后台任务停止访问。 */
QWaitCondition m_oNoBackgroundUseCondition;
int m_nBackgroundUseCount; ///< 当前持有 DataManager 使用权的后台任务数量。
/** @brief 仅由主线程维护;覆盖建网防抖、后台计算和结果提交整个阶段。 */
bool m_bPebiGridGenerationActive;
quint64 m_nPebiGridGenerationRevision; ///< 当前建网活动对应的输入版本。
// 存储井的数据
QVector<nmDataWellBase*> m_vWellData;

@ -68,6 +68,13 @@ class NM_SUB_WND_EXPORT nmSubWndGrid : public iSubWndBaseFit {
void showGridFailure();
/** @brief 销毁当前 VTK 页,防止切换分析后继续显示旧分析网格。 */
void clearGridResultView();
/** @brief 从防抖开始登记建网活动,使同一分析的求解等待最终提交。 */
void beginGridGenerationState(nmDataAnalyzeManager* pDataManager);
/** @brief 结束本窗口登记的建网活动,并通知等待中的求解流程。 */
void endGridGenerationState(nmDataAnalyzeManager* pDataManager,
bool bSucceeded);
/** @brief 判断当前任务之后是否仍有防抖或合并后的网格更新。 */
bool hasPendingGridUpdate() const;
QStackedLayout* m_mainLayout; ///< 在加载背景和 VTK 结果之间切换的主布局。
nmWxGridVTKContainerWidget* m_vtkWidget; ///< 显示所属分析网格的 VTK 控件。
@ -76,6 +83,7 @@ class NM_SUB_WND_EXPORT nmSubWndGrid : public iSubWndBaseFit {
QPointer<nmDataAnalyzeManager> m_pDataManager; ///< 窗口所属数据管理器,销毁后自动置空。
QPointer<nmCalculationPebiGridTask> m_pGridTask; ///< 当前后台网格任务,完成后自动回收。
QPointer<nmDataAnalyzeManager> m_pGridTaskDataManager; ///< 当前网格任务创建时绑定的数据管理器。
QPointer<nmDataAnalyzeManager> m_pGridGenerationDataManager; ///< 本窗口登记建网状态的管理器。
bool m_bGridUpdatePending; ///< 任务运行期间是否又收到网格更新请求。
// PEBI GridControl控制参数对应HX_NWTM_GRID_INPUT::GridControl

@ -148,6 +148,9 @@ class NM_SUB_WND_EXPORT nmSubWndMain : public iSubWndBaseFit {
void onSolverStageChanged(QString sStage, int nCurrent, int nTotal);
/** @brief 请求当前手工任务协作式停止。 */
void onSolverCancelRequested();
/** @brief 当前分析的建网提交结束后,恢复待启动或待捕获的求解流程。 */
void onPebiGridGenerationFinished(quint64 nGridInputRevision,
bool bSucceeded);
/** @brief 按 WellCode 切换结果曲线,不改变主分析井和参数编辑井。 */
void onWellSelected(const QString& sWellCode);
@ -236,6 +239,10 @@ class NM_SUB_WND_EXPORT nmSubWndMain : public iSubWndBaseFit {
//void calculationLogData(nmDataWellBase* pWellData,QVector<QVector<double>>& vvecHistoryData,QVector<QVector<double>>& vvecLogPreData,QVector<QVector<double>>& vvecSemiLogPreData);
private:
/** @brief 建网尚未提交时更新进度并订阅完成信号,不阻塞主线程。 */
bool waitForPebiGridGeneration();
/** @brief 取消当前求解上下文对建网完成信号的订阅。 */
void disconnectPebiGridGenerationWait();
// 绘图相关
nmGuiPlot* m_pWxPlot;

@ -510,6 +510,8 @@ ZX_DEFINE_DYNAMIC(DataAnalyzeManager, nmDataAnalyzeManager)
{
m_pOwnerFitting = nullptr;
m_nBackgroundUseCount = 0;
m_bPebiGridGenerationActive = false;
m_nPebiGridGenerationRevision = 0;
m_reservoirData = nullptr;
m_outlineData = nullptr;
m_pMeasuringScaleData = nullptr;
@ -3619,6 +3621,35 @@ bool nmDataAnalyzeManager::commitPebiGridResult(
return true;
}
void nmDataAnalyzeManager::beginPebiGridGeneration(
quint64 nGridInputRevision)
{
if(nGridInputRevision == 0) {
return;
}
// 该状态只协调同一主线程上的窗口流程;后台任务不得直接读写 DataManager。
m_bPebiGridGenerationActive = true;
m_nPebiGridGenerationRevision = nGridInputRevision;
}
void nmDataAnalyzeManager::endPebiGridGeneration(bool bSucceeded)
{
if(!m_bPebiGridGenerationActive) {
return;
}
const quint64 nGridInputRevision = m_nPebiGridGenerationRevision;
m_bPebiGridGenerationActive = false;
m_nPebiGridGenerationRevision = 0;
emit sigPebiGridGenerationFinished(nGridInputRevision, bSucceeded);
}
bool nmDataAnalyzeManager::isPebiGridGenerationActive() const
{
return m_bPebiGridGenerationActive;
}
void nmDataAnalyzeManager::markPebiGridBuilt()
{
m_oNumericalAnalysisCase.markGridBuilt();

@ -67,6 +67,7 @@ nmSubWndGrid::nmSubWndGrid(QWidget *parent,
m_pDataManager(nullptr),
m_pGridTask(nullptr),
m_pGridTaskDataManager(nullptr),
m_pGridGenerationDataManager(nullptr),
m_bGridUpdatePending(false),
m_dPebiGridControl(pebiGridControl),
m_bGeneratePebiGrid(bGeneratePebiGrid)
@ -79,11 +80,16 @@ nmSubWndGrid::nmSubWndGrid(QWidget *parent,
nmSubWndGrid::~nmSubWndGrid()
{
if(m_pDebounceTimer != nullptr) {
m_pDebounceTimer->stop();
}
// 外部 DLL 不支持安全中止;窗口析构前等待自己的任务退出,避免销毁运行中的 QThread。
if(!m_pGridTask.isNull() && m_pGridTask->isRunning()) {
disconnect(m_pGridTask, nullptr, this, nullptr);
m_pGridTask->wait();
}
// 窗口已经不再提交局部结果,必须结束登记,不能让求解永久等待。
endGridGenerationState(m_pGridGenerationDataManager.data(), false);
}
void nmSubWndGrid::setDataManager(nmDataAnalyzeManager* pDataManager)
@ -97,6 +103,8 @@ void nmSubWndGrid::setDataManager(nmDataAnalyzeManager* pDataManager)
if(pOldDataManager != nullptr) {
disconnect(pOldDataManager, SIGNAL(dataChanged()),
this, SLOT(onDataManagerChanged()));
// 切换后旧任务只允许自然结束并丢弃结果,不再阻塞旧分析的求解入口。
endGridGenerationState(pOldDataManager, false);
}
m_pDataManager = pDataManager;
@ -135,6 +143,7 @@ void nmSubWndGrid::setDataManager(nmDataAnalyzeManager* pDataManager)
}
showGridLoading();
beginGridGenerationState(pDataManager);
if(!m_pGridTask.isNull()) {
// 外部 DLL 不能安全中止;旧任务自然结束后只补做当前分析的最后一次更新。
m_bGridUpdatePending = true;
@ -189,6 +198,7 @@ bool nmSubWndGrid::onConfirmClosing()
// 当前窗口选择清除网格缓存后,分析方案也必须同步失效。
pDataManager->invalidatePebiGrid();
pDataManager->clearUnstructuredGrid();
endGridGenerationState(pDataManager, false);
}
return true;
@ -261,6 +271,7 @@ bool nmSubWndGrid::generatePebiGrid()
// 首次手工生成无需等待防抖窗口。此时 VTK 背景已经完成初始化,任务启动
// 后界面可以立即绘制;连续 dataChanged 仍由 updateGrid() 合并处理。
showGridLoading();
beginGridGenerationState(pDataManager);
onDebounceTimeout();
return true;
}
@ -283,6 +294,7 @@ void nmSubWndGrid::updateGrid()
}
showGridLoading();
beginGridGenerationState(pDataManager);
// dataChanged可能连续触发使用当前窗口自己的定时器合并为一次网格重建
if(!m_pDebounceTimer) {
@ -310,6 +322,8 @@ void nmSubWndGrid::onDebounceTimeout()
// 第二步:把窗口参数写回所属分析方案,再启动绑定同一管理器的后台任务。
nmCalculationPebiGrid::getInstance()->setGridControl(
m_dPebiGridControl, pDataManager);
// GridControl 写回可能更新输入版本,启动任务前同步本轮最终版本。
beginGridGenerationState(pDataManager);
m_pGridTaskDataManager = pDataManager;
m_pGridTask = new nmCalculationPebiGridTask(pDataManager, this);
connect(m_pGridTask, SIGNAL(pebiGridGenerated(bool)),
@ -347,9 +361,14 @@ void nmSubWndGrid::onPebiGridGenerated(bool success)
// 没有排队中的更新,则恢复旧网格或显示失败状态,不能永久停留在加载文字。
if(success) {
showGridResult();
} else if(!m_bGridUpdatePending) {
} else if(!hasPendingGridUpdate()) {
showGridFailure();
}
// 连续变化尚未完成时保持活动状态;求解只能在最后一次提交之后继续。
if(!hasPendingGridUpdate()) {
endGridGenerationState(pTaskDataManager, success);
}
}
void nmSubWndGrid::onPebiGridTaskFinished()
@ -365,10 +384,11 @@ void nmSubWndGrid::onPebiGridTaskFinished()
if(m_bGridUpdatePending) {
m_bGridUpdatePending = false;
nmDataAnalyzeManager* pDataManager = m_pDataManager.data();
if(pDataManager != nullptr &&
pDataManager->isPebiGridValid() &&
m_vtkWidget == nullptr) {
showGridResult();
if(pDataManager != nullptr && pDataManager->isPebiGridValid()) {
if(m_vtkWidget == nullptr) {
showGridResult();
}
endGridGenerationState(pDataManager, true);
return;
}
updateGrid();
@ -431,3 +451,46 @@ void nmSubWndGrid::showGridFailure()
m_mainLayout->setCurrentWidget(m_pGridLoadingLabel);
}
}
void nmSubWndGrid::beginGridGenerationState(
nmDataAnalyzeManager* pDataManager)
{
if(pDataManager == nullptr) {
return;
}
if(!m_pGridGenerationDataManager.isNull() &&
m_pGridGenerationDataManager.data() != pDataManager) {
endGridGenerationState(m_pGridGenerationDataManager.data(), false);
}
const nmDataNumericalAnalysisCase* pAnalysisCase =
pDataManager->getNumericalAnalysisCase();
if(pAnalysisCase == nullptr) {
return;
}
// 只在主线程保存状态和版本,不把窗口或 DataManager 指针交给后台线程。
m_pGridGenerationDataManager = pDataManager;
pDataManager->beginPebiGridGeneration(
pAnalysisCase->getGridInputRevision());
}
void nmSubWndGrid::endGridGenerationState(
nmDataAnalyzeManager* pDataManager,
bool bSucceeded)
{
if(pDataManager == nullptr ||
m_pGridGenerationDataManager.data() != pDataManager) {
return;
}
m_pGridGenerationDataManager = nullptr;
pDataManager->endPebiGridGeneration(bSucceeded);
}
bool nmSubWndGrid::hasPendingGridUpdate() const
{
return m_bGridUpdatePending ||
(m_pDebounceTimer != nullptr && m_pDebounceTimer->isActive());
}

@ -175,6 +175,7 @@ nmSubWndMain::~nmSubWndMain()
m_bSolverStartPending = false;
disconnectPebiGridGenerationWait();
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
@ -1691,6 +1692,10 @@ void nmSubWndMain::startSolverTask()
return;
}
if(waitForPebiGridGeneration()) {
return;
}
// 先创建不自动捕获的任务,使停止按钮在第一批井输入开始前就有明确接收者。
m_pSolverTask = new nmCalculationDllPebiSolverTask(
QString(),
@ -1713,6 +1718,10 @@ void nmSubWndMain::continueSolverInputCapture()
return;
}
if(waitForPebiGridGeneration()) {
return;
}
bool bFinished = false;
const bool bSucceeded =
m_pSolverTask->captureManualInputStep(1, bFinished);
@ -1727,6 +1736,7 @@ void nmSubWndMain::continueSolverInputCapture()
s_pPendingSolverWindow = nullptr;
}
m_bSolverStartPending = false;
disconnectPebiGridGenerationWait();
disconnect(pTask, nullptr, this, nullptr);
pTask->deleteLater();
@ -1748,6 +1758,7 @@ void nmSubWndMain::continueSolverInputCapture()
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
disconnectPebiGridGenerationWait();
m_pSolverTask->start();
return;
}
@ -1776,12 +1787,24 @@ void nmSubWndMain::onSolverStageChanged(
void nmSubWndMain::onSolverCancelRequested()
{
if(m_bSolverStartPending && m_pSolverTask == nullptr) {
// 尚未创建任务时可立即撤销DataManager 也尚未登记后台使用权。
if(m_bSolverStartPending &&
(m_pSolverTask == nullptr || !m_pSolverTask->isRunning())) {
// 等待建网或分批捕获期间尚未进入工作线程,可以立即撤销并释放值快照。
nmCalculationDllPebiSolverTask* pTask = m_pSolverTask;
m_pSolverTask = nullptr;
m_bSolverStartPending = false;
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
disconnectPebiGridGenerationWait();
if(pTask != nullptr) {
pTask->requestCancel();
if(pTask == s_pRunningSolverTask) {
s_pRunningSolverTask = nullptr;
}
disconnect(pTask, nullptr, this, nullptr);
pTask->deleteLater();
}
// canceled() 由进度框自身发出,延迟释放避免在信号调用栈内销毁发送者。
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->deleteLater();
@ -1812,6 +1835,70 @@ void nmSubWndMain::onSolverCancelRequested()
}
}
bool nmSubWndMain::waitForPebiGridGeneration()
{
nmDataAnalyzeManager* pDataManager = m_pSolverDataManager.data();
if(!m_bSolverStartPending || pDataManager == nullptr ||
!pDataManager->isPebiGridGenerationActive()) {
disconnectPebiGridGenerationWait();
return false;
}
// 这里只等待主线程提交信号,不获取网格锁;进度框和停止按钮保持可响应。
connect(pDataManager,
SIGNAL(sigPebiGridGenerationFinished(quint64,bool)),
this,
SLOT(onPebiGridGenerationFinished(quint64,bool)),
Qt::UniqueConnection);
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->setLabelText(
tr("Waiting for current grid generation..."));
m_pProgressDlg->setRange(0, 0);
}
return true;
}
void nmSubWndMain::disconnectPebiGridGenerationWait()
{
nmDataAnalyzeManager* pDataManager = m_pSolverDataManager.data();
if(pDataManager == nullptr) {
return;
}
disconnect(pDataManager,
SIGNAL(sigPebiGridGenerationFinished(quint64,bool)),
this,
SLOT(onPebiGridGenerationFinished(quint64,bool)));
}
void nmSubWndMain::onPebiGridGenerationFinished(
quint64 nGridInputRevision,
bool bSucceeded)
{
Q_UNUSED(nGridInputRevision);
Q_UNUSED(bSucceeded);
nmDataAnalyzeManager* pDataManager = m_pSolverDataManager.data();
if(sender() != pDataManager || !m_bSolverStartPending ||
pDataManager == nullptr ||
pDataManager->isPebiGridGenerationActive()) {
return;
}
disconnectPebiGridGenerationWait();
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->setLabelText(tr("Preparing input snapshot..."));
m_pProgressDlg->setRange(0, 0);
}
// 失败或版本过期也恢复原流程,由快照缓存检查决定是否重新生成一次网格。
if(m_pSolverTask == nullptr) {
QTimer::singleShot(0, this, SLOT(startSolverTask()));
} else {
QTimer::singleShot(0, this, SLOT(continueSolverInputCapture()));
}
}
void nmSubWndMain::triggerToolBarAction(int index)
{
// 获取多边形边界的绘制,并模拟触发点击操作

Loading…
Cancel
Save