#include "nmWellEditorSession.h" #include "nmDataHorizontalFracturedWell.h" #include "nmDataPerforation.h" #include "nmDataVerticalFracturedWell.h" #include "nmDataVerticalWell.h" #include #include namespace { /* Qt 4.8的lupdate需要显式标记间接翻译字符串。 */ static const char* const s_pSessionTranslationMarkers[] = { QT_TRANSLATE_NOOP("nmWellEditorSession", "The original well data is unavailable."), QT_TRANSLATE_NOOP("nmWellEditorSession", "The well type is not supported."), QT_TRANSLATE_NOOP("nmWellEditorSession", "Failed to create the well working copy."), QT_TRANSLATE_NOOP("nmWellEditorSession", "The well editing session is unavailable."), QT_TRANSLATE_NOOP("nmWellEditorSession", "Failed to create the selected well type."), QT_TRANSLATE_NOOP("nmWellEditorSession", "Failed to preserve the well identity.") }; QString sessionText(const char* pText) { Q_UNUSED(s_pSessionTranslationMarkers); return QCoreApplication::translate("nmWellEditorSession", pText); } bool isSupportedWellType(NM_WELL_MODEL eWellType) { // 首期编辑器只开放已完成字段目录和轨迹交互的三种井型。 return eWellType == Vertical_Well || eWellType == Vertical_Fractured_Well || eWellType == Horizontal_Fractured_Well; } bool isVerticalWellType(NM_WELL_MODEL eWellType) { return eWellType == Vertical_Well || eWellType == Vertical_Fractured_Well; } bool arePerforationTypesCompatible(NM_WELL_MODEL eSourceType, NM_WELL_MODEL eTargetType) { // 直井和压裂直井的射孔MD语义一致,多段压裂水平井使用独立水平段。 return isVerticalWellType(eSourceType) && isVerticalWellType(eTargetType); } void ensureRequiredPerforations(nmDataWellBase* pWell) { nmDataHorizontalFracturedWell* pHorizontal = dynamic_cast(pWell); if (pHorizontal != nullptr) { pHorizontal->ensureBasePerforation(); } } double wellboreTopMd(nmDataWellBase* pWell) { if (pWell == nullptr) { return 0.0; } // 水平井的有效井筒从第一段射孔起点开始,其余井型使用井底MD。 if (dynamic_cast(pWell) != nullptr && pWell->getPerforationCount() > 0) { nmDataPerforation* pFirstPerforation = pWell->getPerforation(0); if (pFirstPerforation != nullptr) { return pFirstPerforation->getMdStart().getValue().toDouble(); } } return pWell->getBottomholeMD().getValue().toDouble(); } double mapPerforationMd(double dSourceMd, double dSourceTop, double dSourceLength, double dTargetTop, double dTargetLength) { // 异常旧数据无法按比例换算时只平移井筒起点,避免除零或产生非数值。 if (dSourceLength <= 0.0 || dTargetLength <= 0.0) { return dSourceMd + dTargetTop - dSourceTop; } double dRelativePosition = (dSourceMd - dSourceTop) / dSourceLength; return dTargetTop + dRelativePosition * dTargetLength; } } nmWellEditorSession::nmWellEditorSession( const nmWellEditContext& oContext) : m_oContext(oContext), m_mapWorkingWells(), m_nCurrentWellType(static_cast(Unknow_Well)), m_bInitialized(false) { } nmWellEditorSession::~nmWellEditorSession() { // 会话独占所有缓存;已由takeModifiedWell()转移的对象不再位于映射中。 qDeleteAll(m_mapWorkingWells); m_mapWorkingWells.clear(); m_oContext.m_pFrameworkWell = nullptr; m_oContext.m_pOriginalWell = nullptr; } bool nmWellEditorSession::initialize(QString& sError) { if (m_oContext.m_pOriginalWell == nullptr) { sError = sessionText("The original well data is unavailable."); return false; } NM_WELL_MODEL eWellType = m_oContext.m_pOriginalWell->getWellType(); if (!isSupportedWellType(eWellType)) { sError = sessionText("The well type is not supported."); return false; } // 原始井只作为克隆源,后续界面和子窗口均操作独立工作副本。 nmDataWellBase* pWorkingWell = m_oContext.m_pOriginalWell->clone(); if (pWorkingWell == nullptr) { sError = sessionText("Failed to create the well working copy."); return false; } // 异常旧工程可能包含零射孔水平井,进入编辑会话前恢复业务不变量。 ensureRequiredPerforations(pWorkingWell); m_nCurrentWellType = static_cast(eWellType); m_mapWorkingWells.insert(m_nCurrentWellType, pWorkingWell); m_bInitialized = true; sError.clear(); return true; } nmDataWellBase* nmWellEditorSession::currentWell() const { return m_mapWorkingWells.value(m_nCurrentWellType, nullptr); } NM_WELL_MODEL nmWellEditorSession::currentWellType() const { return static_cast(m_nCurrentWellType); } bool nmWellEditorSession::switchWellType(NM_WELL_MODEL eWellType, QString& sError) { if (!m_bInitialized) { sError = sessionText("The well editing session is unavailable."); return false; } if (!isSupportedWellType(eWellType)) { sError = sessionText("The well type is not supported."); return false; } int nTargetType = static_cast(eWellType); if (nTargetType == m_nCurrentWellType) { sError.clear(); return true; } // 第一步:已有井型直接恢复缓存,避免往返切换丢失输入。 if (m_mapWorkingWells.contains(nTargetType)) { m_nCurrentWellType = nTargetType; sError.clear(); return true; } // 第二步:首次进入井型时只迁移共享数据,保留目标井型默认几何。 nmDataWellBase* pNewWell = createWell(eWellType); if (pNewWell == nullptr) { sError = sessionText("Failed to create the selected well type."); return false; } nmDataWellBase* pSharedSource = currentWell(); nmDataWellBase* pPerforationSource = findCompatiblePerforationSource(eWellType); if (!initializeWorkingWell(pSharedSource, pPerforationSource, pNewWell)) { delete pNewWell; sError = sessionText("Failed to preserve the well identity."); return false; } m_mapWorkingWells.insert(nTargetType, pNewWell); m_nCurrentWellType = nTargetType; sError.clear(); return true; } void nmWellEditorSession::setWellName(const QString& sWellName) { // 井名属于所有井型共有信息,必须同步缓存以保证往返切换后一致。 QMap::iterator oIt = m_mapWorkingWells.begin(); for (; oIt != m_mapWorkingWells.end(); ++oIt) { if (oIt.value() != nullptr) { oIt.value()->setWellName(sWellName); } } } void nmWellEditorSession::setTimeDependentSkin(bool bEnabled) { // 详情数据属于射孔,只能与当前井型工作副本保持一致。 nmDataWellBase* pWell = currentWell(); if (pWell != nullptr) { pWell->setTimeDependentSkin(bEnabled); } } QStringList nmWellEditorSession::otherWellNames() const { return m_oContext.m_listOtherWellNames; } nmDataWellBase* nmWellEditorSession::takeModifiedWell() { if (!m_bInitialized) { return nullptr; } // 第一步:从映射摘除最终副本,使会话不再拥有其所有权。 nmDataWellBase* pModifiedWell = m_mapWorkingWells.take(m_nCurrentWellType); // 第二步:销毁未选中的井型缓存,避免临时对象泄漏到提交层。 qDeleteAll(m_mapWorkingWells); m_mapWorkingWells.clear(); m_bInitialized = false; return pModifiedWell; } nmDataWellBase* nmWellEditorSession::createWell( NM_WELL_MODEL eWellType) const { // 必须调用各派生类默认构造,目标井型的井长和裂缝默认值由此保留。 switch (eWellType) { case Vertical_Well: return new nmDataVerticalWell(); case Vertical_Fractured_Well: return new nmDataVerticalFracturedWell(); case Horizontal_Fractured_Well: return new nmDataHorizontalFracturedWell(); default: return nullptr; } } nmDataWellBase* nmWellEditorSession::findCompatiblePerforationSource( NM_WELL_MODEL eTargetType) const { // 第一步:优先使用当前工作副本,保持连续切换时的最新输入。 nmDataWellBase* pCurrentWell = currentWell(); if (pCurrentWell != nullptr && arePerforationTypesCompatible( pCurrentWell->getWellType(), eTargetType)) { return pCurrentWell; } // 第二步:切换经过水平井时,从已有垂直井缓存恢复兼容射孔来源。 QMap::const_iterator oIt = m_mapWorkingWells.constBegin(); for (; oIt != m_mapWorkingWells.constEnd(); ++oIt) { nmDataWellBase* pCandidate = oIt.value(); if (pCandidate != nullptr && arePerforationTypesCompatible( pCandidate->getWellType(), eTargetType)) { return pCandidate; } } return nullptr; } bool nmWellEditorSession::initializeWorkingWell( nmDataWellBase* pSharedSource, nmDataWellBase* pPerforationSource, nmDataWellBase* pTarget) const { if (pSharedSource == nullptr || pTarget == nullptr) { return false; } // 第一步:白名单复制跨井型仍具有相同含义的数据。 if (!copySharedWellData(pSharedSource, pTarget)) { return false; } // 第二步:只在垂直井族内迁移射孔;跨井族保留目标构造默认值。 if (pPerforationSource != nullptr) { pTarget->setTimeDependentSkin( pPerforationSource->isTimeDependentSkin()); migratePerforationData(pPerforationSource, pTarget); } ensureRequiredPerforations(pTarget); // 第三步:目标对象完全初始化后统一重建派生几何。 rebuildDerivedGeometry(pTarget); return true; } bool nmWellEditorSession::copySharedWellData( nmDataWellBase* pSource, nmDataWellBase* pTarget) const { if (pSource == nullptr || pTarget == nullptr) { return false; } // 只复制跨井型语义完全一致的字段,严禁使用基类整体赋值覆盖目标默认值。 // 第一步:复制井身份、位置和通用井筒配置。 if (!pTarget->restoreWellInstanceId(pSource->getWellInstanceId())) { return false; } pTarget->setWellName(pSource->getWellName()); pTarget->setWellCode(pSource->getWellCode()); // 井型切换只改变几何模型,工程井井别和流量相别必须保持不变。 pTarget->setWellCategory(pSource->getWellCategory()); pTarget->setX(pSource->getX()); pTarget->setY(pSource->getY()); pTarget->setRadius(pSource->getRadius()); pTarget->setDrillFloorElevation(pSource->getDrillFloorElevation()); pTarget->setZw(pSource->getZw()); pTarget->setRateDependentSkin(pSource->getRateDependentSkin()); pTarget->setdSdQ(pSource->getdSdQ()); pTarget->setWellboreModel(pSource->getWellboreModel()); pTarget->setWellboreStorage(pSource->getWellboreStorage()); pTarget->setFinalWellboreStorage(pSource->getFinalWellboreStorage()); pTarget->setInputWellHead(pSource->getInputWellHead()); pTarget->setWellHeadX(pSource->getWellHeadX()); pTarget->setWellHeadY(pSource->getWellHeadY()); pTarget->setCInitialCFinal(pSource->getCInitialCFinal()); pTarget->setDtChangingStorage(pSource->getDtChangingStorage()); pTarget->setLeakSkin(pSource->getLeakSkin()); // 第二步:保留输入历史和已有计算结果,切换井型不应丢失曲线数据。 // 必须整体拷贝记录列表和选中 Code,不能用 setPressurePoints/setFlowPoints(phase) // 逐点写入——目标对象是刚构造的新井,尚未选中任何记录,逐点写入会按阶段一的 // 兼容语义各自新建 1 条匿名占位记录,丢失全部真实计量记录和已选中的 Gauge Code。 pTarget->setPressureRecords(pSource->getPressureRecords()); pTarget->setFlowRecords(pSource->getFlowRecords()); pTarget->selectPressureGaugeCode(pSource->getSelectedPressureGaugeCode()); pTarget->selectFlowGaugeCode(pSource->getSelectedFlowGaugeCode()); // 井型切换只能改变几何模型,不能重置用户选择的分相产量。 pTarget->setUseOilRate(pSource->getUseOilRate()); pTarget->setUseGasRate(pSource->getUseGasRate()); pTarget->setUseWaterRate(pSource->getUseWaterRate()); pTarget->setIndexF(pSource->getIndexF()); pTarget->setHistoryData(pSource->getHistoryPressure(), pSource->getHistoryLogLog(), pSource->getHistorySemiLog(), pSource->getHistoryGaugeInputSha1()); // 第三步:保留图元显示属性和跨井型开关。 pTarget->setPlotVisible(pSource->getPlotVisible()); pTarget->setDotStyle(pSource->getDotStyle()); pTarget->setDotColor(pSource->getDotColorR(), pSource->getDotColorG(), pSource->getDotColorB()); pTarget->setDotRadius(pSource->getDotRadius()); pTarget->setDotFilling(pSource->getDotFilling()); pTarget->setShowSubObjs(pSource->getShowSubObjs()); return true; } void nmWellEditorSession::migratePerforationData( nmDataWellBase* pSource, nmDataWellBase* pTarget) const { if (pSource == nullptr || pTarget == nullptr) { return; } // 第一步:清空目标默认射孔前保存两种井型各自的井筒坐标范围。 double dSourceTop = wellboreTopMd(pSource); double dSourceLength = pSource->getWellLength().getValue().toDouble(); double dTargetTop = wellboreTopMd(pTarget); double dTargetLength = pTarget->getWellLength().getValue().toDouble(); int nSourceCount = pSource->getPerforationCount(); pTarget->clearPerforations(); // 第二步:拷贝构造保留表皮、流动段、时间状态和图表位置等完整数据。 for (int nIndex = 0; nIndex < nSourceCount; ++nIndex) { nmDataPerforation* pSourcePerforation = pSource->getPerforation(nIndex); if (pSourcePerforation == nullptr) { continue; } nmDataPerforation* pTargetPerforation = new nmDataPerforation(*pSourcePerforation); double dTargetStart = mapPerforationMd( pSourcePerforation->getMdStart().getValue().toDouble(), dSourceTop, dSourceLength, dTargetTop, dTargetLength); double dTargetEnd = mapPerforationMd( pSourcePerforation->getMdEnd().getValue().toDouble(), dSourceTop, dSourceLength, dTargetTop, dTargetLength); pTargetPerforation->getMdStart().setValue(dTargetStart); pTargetPerforation->getMdEnd().setValue(dTargetEnd); pTarget->addPerforation(pTargetPerforation); } } void nmWellEditorSession::rebuildDerivedGeometry( nmDataWellBase* pWell) const { // 裂缝集合是参数派生数据,必须在共享字段迁移完成后统一重建。 if (nmDataVerticalFracturedWell* pVertical = dynamic_cast(pWell)) { pVertical->setFracs(); } if (nmDataHorizontalFracturedWell* pHorizontal = dynamic_cast(pWell)) { pHorizontal->setFracs(); } }