diff --git a/Include/nmNum/nmData/nmDataAnalyzeManager.h b/Include/nmNum/nmData/nmDataAnalyzeManager.h index 603049ae..57409d12 100644 --- a/Include/nmNum/nmData/nmDataAnalyzeManager.h +++ b/Include/nmNum/nmData/nmDataAnalyzeManager.h @@ -12,6 +12,7 @@ #include "nmDataHorizontalFracturedWell.h" #include "nmDataNumericalAnalysisCase.h" #include "nmNumericalResultPersistence.h" +#include "nmDataGaugeRecord.h" #include "nmGridDefine.h" @@ -230,6 +231,26 @@ public: // 获取当前分析窗体下的指针 static nmDataAnalyzeManager* getCurrentInstance(); + /** + * @brief 枚举工程井下全部压力、流量测量记录并逐条判定可用状态。 + * @note 仅当工程井对象为空或井别无效时返回 false;单条记录损坏/为空 + * 不影响返回值,只会体现为该记录的 eStatus。 + */ + static bool readFrameworkWellCurves( + ZxDataWell* pWellData, + QVector& vecPressureRecords, + QVector& vecFlowRecords, + NM_WELL_CATEGORY& eWellCategory); + + /** + * @brief 将枚举到的记录列表写入数值井,并按默认规则选中压力/流量记录。 + * @note 找不到任何可用记录时仍会写入列表,只是选中 Code 为空,不阻止建井。 + */ + static void applyGaugeRecordsToWell( + nmDataWellBase* pWellData, + const QVector& vecPressureRecords, + const QVector& vecFlowRecords); + // 井的接口 // 添加井 nmDataWellBase* createWell(NM_WELL_MODEL eWellType); diff --git a/Include/nmNum/nmData/nmDataGaugeRecord.h b/Include/nmNum/nmData/nmDataGaugeRecord.h new file mode 100644 index 00000000..798869af --- /dev/null +++ b/Include/nmNum/nmData/nmDataGaugeRecord.h @@ -0,0 +1,49 @@ +#pragma once + +#include "nmDefines.h" + +#include +#include +#include + +/** + * @brief 工程井下一条压力测量记录的只读快照。 + * @note 记录本身不持久化到数值项目文件,每次都从工程井节点重新读取; + * 损坏或为空的记录仍保留在列表中,只是不可被选中。 + */ +struct nmPressureGaugeRecord { + QString sGaugeCode; // 对应 ZxDataGaugeP::getCode(),选择持久化键 + QString sGaugeName; // 对应 ZxDataGaugeP::getGaugeName(),界面展示用 + QString sGaugeTime; // 对应 ZxDataGaugeP::getGaugeTime(),界面展示用 + NM_GAUGE_RECORD_STATUS eStatus; // 记录可用状态 + QVector vecPressurePoints; // 主块解析出的压力曲线点 + + nmPressureGaugeRecord() + : eStatus(NM_GaugeRecord_Empty) + { + } +}; + +/** + * @brief 工程井下一条流量测量记录的只读快照。 + * @note 单相记录只解析主块并按井别归入对应相,忽略可能残留的 Ex2/Ex3; + * 多相记录分别解析主块、Ex2、Ex3 为油、气、水三相。 + */ +struct nmFlowGaugeRecord { + QString sGaugeCode; // 对应 ZxDataGaugeF::getCode(),选择持久化键 + QString sGaugeName; // 对应 ZxDataGaugeF::getGaugeName(),界面展示用 + QString sGaugeTime; // 对应 ZxDataGaugeF::getGaugeTime(),界面展示用 + bool bMultiPhase; // 对应 ZxDataGaugeF::getMultiPhase() + NM_GAUGE_RECORD_STATUS eStatus; // 记录可用状态 + QVector vecOilPoints; // 油相流量点,允许首项为 (0,0) 占位点 + QVector vecGasPoints; // 气相流量点,允许首项为 (0,0) 占位点 + QVector vecWaterPoints; // 水相流量点,允许首项为 (0,0) 占位点 + int nIndexF; // 该记录自身的一基流动段索引,供后续阶段切换记录时恢复 + + nmFlowGaugeRecord() + : bMultiPhase(false) + , eStatus(NM_GaugeRecord_Empty) + , nIndexF(0) + { + } +}; diff --git a/Include/nmNum/nmData/nmDataWellBase.h b/Include/nmNum/nmData/nmDataWellBase.h index ccfc297e..0afc9029 100644 --- a/Include/nmNum/nmData/nmDataWellBase.h +++ b/Include/nmNum/nmData/nmDataWellBase.h @@ -3,6 +3,7 @@ #include "nmData_global.h" #include "nmDefines.h" #include "nmDataAttribute.h" +#include "nmDataGaugeRecord.h" #include "ZxDataObjectDbl.h" #include "nmDataPerforation.h" @@ -63,7 +64,7 @@ public: /** @brief 保存工程井的油井、气井或水井井别;该值不由数值井编辑器修改。 */ void setWellCategory(NM_WELL_CATEGORY eWellCategory); - /** @brief 返回工程井井别,用于选择流量大字段及求解器相别数组。 */ + /** @brief 返回工程井井别,用于选择历史曲线的参考流量相。 */ NM_WELL_CATEGORY getWellCategory() const; /** @brief 返回井对象的稳定实例标识,与可编辑井编码相互独立。 */ @@ -134,17 +135,48 @@ public: QVector getPressurePoints() const; // 设置压力曲线点数据 void setPressurePoints(const QVector& points); - // 获取流量曲线点数据 + // 获取井别对应相的流量曲线点数据,供现有历史曲线逻辑使用 QVector getFlowPoints() const; - // 设置流量曲线点数据 + // 设置井别对应相的流量曲线点数据 void setFlowPoints(const QVector& points); - /** @brief 返回去除可选 (0,0) 占位点后的真实流动段。 */ + /** @brief 返回工程井别对应的历史曲线参考相。 */ + NM_PHASE_TYPE getReferenceFlowPhase() const; + /** @brief 获取或设置指定油、气、水相的原始流量点。 */ + QVector getFlowPoints(NM_PHASE_TYPE eFlowPhase) const; + void setFlowPoints(NM_PHASE_TYPE eFlowPhase, + const QVector& points); + /** @brief 返回指定相去除可选 (0,0) 占位点后的真实流动段。 */ + QVector getFlowSegmentPoints(NM_PHASE_TYPE eFlowPhase) const; + /** @brief 返回指定相的真实流动段数量。 */ + int getFlowSegmentCount(NM_PHASE_TYPE eFlowPhase) const; + /** @brief 指定相是否至少包含一个真实流动段。 */ + bool hasFlowPhaseData(NM_PHASE_TYPE eFlowPhase) const; + /** @brief 油、气、水中是否至少有一相包含真实流动段。 */ + bool hasAnyFlowData() const; + /** @brief 返回井别对应相去除可选 (0,0) 占位点后的真实流动段。 */ QVector getFlowSegmentPoints() const; - /** @brief 返回真实流动段数量,不把首个 (0,0) 占位点计入。 */ + /** @brief 返回井别对应相的真实流动段数量。 */ int getFlowSegmentCount() const; /** @brief 校验当前流动段索引是否位于一基的真实流动段范围内。 */ bool hasValidFlowSectionIndex() const; + /** @brief 设置全部压力记录列表(不改变当前选中 Code)。 */ + void setPressureRecords(const QVector& vecRecords); + /** @brief 设置全部流量记录列表(不改变当前选中 Code)。 */ + void setFlowRecords(const QVector& vecRecords); + /** @brief 按 Gauge Code 选中一条压力记录;传入空字符串表示无选中。 */ + void selectPressureGaugeCode(const QString& sGaugeCode); + /** @brief 按 Gauge Code 选中一条流量记录;传入空字符串表示无选中。 */ + void selectFlowGaugeCode(const QString& sGaugeCode); + /** @brief 返回当前选中的压力记录 Gauge Code,未选中时为空。 */ + QString getSelectedPressureGaugeCode() const; + /** @brief 返回当前选中的流量记录 Gauge Code,未选中时为空。 */ + QString getSelectedFlowGaugeCode() const; + /** @brief 返回全部压力记录(包含损坏/空/身份无效记录)。 */ + QVector getPressureRecords() const; + /** @brief 返回全部流量记录(包含损坏/空/身份无效记录)。 */ + QVector getFlowRecords() const; + int getIndexF() const; void setIndexF(const int newIndex); @@ -276,8 +308,10 @@ protected: // Bottomhole conditions---井底条件 nmDataAttribute m_bottomholeMD; // 井底测量深度 - QVector m_vecPtsP; // 压力曲线点数据 - QVector m_vecPtsF; // 流量曲线点数据,允许首项为 (0,0) 占位点 + QVector m_vecPressureRecords; // 全部压力测量记录(可能包含损坏/空/身份无效记录) + QVector m_vecFlowRecords; // 全部流量测量记录(可能包含损坏/空/身份无效记录) + QString m_sSelectedPressureGaugeCode; // 当前选中的压力记录 Gauge Code,为空表示未选中 + QString m_sSelectedFlowGaugeCode; // 当前选中的流量记录 Gauge Code,为空表示未选中 int m_nIndexF; // 当前流量段的索引 diff --git a/Include/nmNum/nmData/nmDefines.h b/Include/nmNum/nmData/nmDefines.h index 53230289..11d0a2ad 100644 --- a/Include/nmNum/nmData/nmDefines.h +++ b/Include/nmNum/nmData/nmDefines.h @@ -57,8 +57,8 @@ enum NM_PHASE_TYPE { PHASE_Oil_Water // 油水 }; -// 工程井井别。数值模块只保存工程井井别的只读副本,流量相别由该值唯一派生。 -// Oil/Gas/Water 的数值与 ZxDataWell::getWellTypeIndex() 及流量大字段索引保持一致。 +// 工程井井别。数值模块保存其只读副本,并将井别对应相作为历史曲线参考相。 +// Oil/Gas/Water 的数值与 ZxDataWell::getWellTypeIndex() 保持一致;真实多相流量独立保存。 enum NM_WELL_CATEGORY { NM_WellCategory_Unknown = -1, NM_WellCategory_Oil = 0, @@ -72,6 +72,14 @@ inline bool nmIsValidWellCategory(NM_WELL_CATEGORY eWellCategory) eWellCategory == NM_WellCategory_Gas || eWellCategory == NM_WellCategory_Water; } + +// 工程压力/流量测量记录的可用状态。损坏与空记录仍保留在列表中,只是不可被选中。 +enum NM_GAUGE_RECORD_STATUS { + NM_GaugeRecord_Usable, // 数据完整,可被选中使用 + NM_GaugeRecord_Empty, // 主/多相字节块均为空,不算数据损坏 + NM_GaugeRecord_Corrupt, // 字节块解析失败或 X/Y 长度不一致 + NM_GaugeRecord_InvalidIdentity // Gauge Code 为空或在同一口井内重复 +}; // 结构树节点类型 enum NM_TREE_TYPE { TREE_OilReservoir, // 油藏节点 diff --git a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp index d7b6af65..02a61a5a 100644 --- a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp +++ b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp @@ -42,6 +42,7 @@ #include "ZxDataGaugeBase.h" #include "ZxDataGaugeP.h" #include "ZxDataGaugeF.h" +#include "ZxBaHelper.h" #include "zxSysUtils.h" #include "ZxDataProject.h" @@ -152,72 +153,226 @@ static NM_WELL_CATEGORY frameworkWellCategory(const ZxDataWell* pWellData) ? eWellCategory : NM_WellCategory_Unknown; } -// 从一条测量数据中复制二维点。流量调用方必须显式传入油、气、水井别索引。 -static bool readGaugePoints(ZxDataGaugeBase* pGaugeData, - int nObjectIndex, - QVector& vecPoints) +// DLL 的带索引 getter 实际始终返回主块,压力和多相流量都必须直接解析指定字节块。 +// 空块不算数据损坏,只有解析失败或 X/Y 长度不一致才算损坏。 +static bool readFlowGaugeBlock( + const QByteArray& baGaugeData, + QVector& vecPoints) { vecPoints.clear(); - if(pGaugeData == nullptr) { - return false; + if(baGaugeData.isEmpty()) { + return true; } + QByteArray baData = baGaugeData; VecDouble vecX; VecDouble vecY; - if(!pGaugeData->getDataVecXY(vecX, vecY, nObjectIndex)) { + if(!ZxBaHelper::convertBa2VecXY(vecX, vecY, baData) || + vecX.size() != vecY.size()) { return false; } - const int nPointCount = qMin( - static_cast(vecX.size()), - static_cast(vecY.size())); - vecPoints.reserve(nPointCount); - for(int nIndex = 0; nIndex < nPointCount; ++nIndex) { + vecPoints.reserve(static_cast(vecX.size())); + for(int nIndex = 0; nIndex < static_cast(vecX.size()); ++nIndex) { vecPoints.append(QPointF(vecX[nIndex], vecY[nIndex])); } return true; } -// 读取工程井的第一条压力和流量数据;井别是流量相别的唯一事实来源。 -static bool readFrameworkWellCurves( +// 解析单个字节块并据此判定该块自身的可用状态:空块为 Empty,解析失败为 Corrupt。 +static NM_GAUGE_RECORD_STATUS readBlockAndClassify( + const QByteArray& baGaugeData, + QVector& vecPoints) +{ + if(baGaugeData.isEmpty()) { + vecPoints.clear(); + return NM_GaugeRecord_Empty; + } + return readFlowGaugeBlock(baGaugeData, vecPoints) + ? NM_GaugeRecord_Usable : NM_GaugeRecord_Corrupt; +} + +// 多相流量记录:主块=油、Ex2=气、Ex3=水;任一非空块损坏则整条记录损坏,三块全空才是空记录。 +static NM_GAUGE_RECORD_STATUS classifyMultiPhaseFlowRecord( + ZxDataGaugeF* pGaugeF, + QVector& vecOilPoints, + QVector& vecGasPoints, + QVector& vecWaterPoints) +{ + const QByteArray baOil = pGaugeF->getGaugeData(); + const QByteArray baGas = pGaugeF->getGaugeDataEx2(); + const QByteArray baWater = pGaugeF->getGaugeDataEx3(); + + const bool bOilOk = readFlowGaugeBlock(baOil, vecOilPoints); + const bool bGasOk = readFlowGaugeBlock(baGas, vecGasPoints); + const bool bWaterOk = readFlowGaugeBlock(baWater, vecWaterPoints); + if(!bOilOk || !bGasOk || !bWaterOk) { + return NM_GaugeRecord_Corrupt; + } + if(baOil.isEmpty() && baGas.isEmpty() && baWater.isEmpty()) { + return NM_GaugeRecord_Empty; + } + return NM_GaugeRecord_Usable; +} + +// 单相流量记录:只解析主块,并按井别把数据归入油、气或水相;忽略可能残留的 Ex2/Ex3。 +static NM_GAUGE_RECORD_STATUS classifySinglePhaseFlowRecord( + ZxDataGaugeF* pGaugeF, + NM_WELL_CATEGORY eWellCategory, + QVector& vecOilPoints, + QVector& vecGasPoints, + QVector& vecWaterPoints) +{ + switch(eWellCategory) { + case NM_WellCategory_Oil: + return readBlockAndClassify(pGaugeF->getGaugeData(), vecOilPoints); + case NM_WellCategory_Gas: + return readBlockAndClassify(pGaugeF->getGaugeData(), vecGasPoints); + case NM_WellCategory_Water: + return readBlockAndClassify(pGaugeF->getGaugeData(), vecWaterPoints); + default: + // 调用方在枚举记录前已校验过井别有效性,这里不应该被触达。 + return NM_GaugeRecord_Corrupt; + } +} + +bool nmDataAnalyzeManager::readFrameworkWellCurves( ZxDataWell* pWellData, - QVector& vecPressurePoints, - QVector& vecFlowPoints, + QVector& vecPressureRecords, + QVector& vecFlowRecords, NM_WELL_CATEGORY& eWellCategory) { - vecPressurePoints.clear(); - vecFlowPoints.clear(); + vecPressureRecords.clear(); + vecFlowRecords.clear(); eWellCategory = frameworkWellCategory(pWellData); if(pWellData == nullptr || !nmIsValidWellCategory(eWellCategory)) { return false; } - ZxDataGaugeP* pGaugeP = nullptr; + // 第一步:枚举全部压力记录,不再只取第一条。 const ZxDataObjectList listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP); + QSet setSeenPressureCodes; for(int nIndex = 0; nIndex < listGaugeP.size(); ++nIndex) { - pGaugeP = dynamic_cast(listGaugeP[nIndex]); - if(pGaugeP != nullptr) { - break; + ZxDataGaugeP* pGaugeP = dynamic_cast(listGaugeP[nIndex]); + if(pGaugeP == nullptr) { + continue; } + + nmPressureGaugeRecord oRecord; + oRecord.sGaugeCode = pGaugeP->getCode(); + oRecord.sGaugeName = pGaugeP->getGaugeName(); + oRecord.sGaugeTime = pGaugeP->getGaugeTime(); + oRecord.eStatus = readBlockAndClassify( + pGaugeP->getGaugeData(), oRecord.vecPressurePoints); + + // 空 Code 或同井重复 Code 属于身份问题,覆盖内容判定结果。 + if(oRecord.sGaugeCode.isEmpty() || + setSeenPressureCodes.contains(oRecord.sGaugeCode)) { + oRecord.eStatus = NM_GaugeRecord_InvalidIdentity; + } else { + setSeenPressureCodes.insert(oRecord.sGaugeCode); + } + vecPressureRecords.append(oRecord); } - ZxDataGaugeF* pGaugeF = nullptr; + // 第二步:枚举全部流量记录,不再只取第一条。 const ZxDataObjectList listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF); + QSet setSeenFlowCodes; for(int nIndex = 0; nIndex < listGaugeF.size(); ++nIndex) { - pGaugeF = dynamic_cast(listGaugeF[nIndex]); - if(pGaugeF != nullptr) { - break; + ZxDataGaugeF* pGaugeF = dynamic_cast(listGaugeF[nIndex]); + if(pGaugeF == nullptr) { + continue; + } + + nmFlowGaugeRecord oRecord; + oRecord.sGaugeCode = pGaugeF->getCode(); + oRecord.sGaugeName = pGaugeF->getGaugeName(); + oRecord.sGaugeTime = pGaugeF->getGaugeTime(); + oRecord.bMultiPhase = pGaugeF->getMultiPhase(); + oRecord.eStatus = oRecord.bMultiPhase + ? classifyMultiPhaseFlowRecord( + pGaugeF, oRecord.vecOilPoints, + oRecord.vecGasPoints, oRecord.vecWaterPoints) + : classifySinglePhaseFlowRecord( + pGaugeF, eWellCategory, + oRecord.vecOilPoints, + oRecord.vecGasPoints, oRecord.vecWaterPoints); + + // 空 Code 或同井重复 Code 属于身份问题,覆盖内容判定结果。 + if(oRecord.sGaugeCode.isEmpty() || + setSeenFlowCodes.contains(oRecord.sGaugeCode)) { + oRecord.eStatus = NM_GaugeRecord_InvalidIdentity; + } else { + setSeenFlowCodes.insert(oRecord.sGaugeCode); } + vecFlowRecords.append(oRecord); } - // 压力数据仍读取二维主对象;流量必须使用井别对应的 0/1/2 大字段索引。 - readGaugePoints(pGaugeP, 0, vecPressurePoints); - readGaugePoints(pGaugeF, static_cast(eWellCategory), vecFlowPoints); return true; } +// 判断某相流量点是否至少包含一个真实流动段,去除可选首个 (0,0) 占位点后非空即为真实。 +static bool hasRealFlowSegment(const QVector& vecPoints) +{ + if(vecPoints.isEmpty()) { + return false; + } + if(vecPoints.size() == 1 && + vecPoints.first().x() == 0.0 && vecPoints.first().y() == 0.0) { + return false; + } + return true; +} + +// 从压力记录列表中选出框架顺序中第一条 Usable 记录;找不到则返回空 Code。 +static QString selectDefaultPressureGaugeCode( + const QVector& vecRecords) +{ + foreach(const nmPressureGaugeRecord& oRecord, vecRecords) { + if(oRecord.eStatus == NM_GaugeRecord_Usable) { + return oRecord.sGaugeCode; + } + } + return QString(); +} + +// 从流量记录列表中选出框架顺序中第一条至少含一个真实相的记录;找不到则返回空 Code。 +static QString selectDefaultFlowGaugeCode( + const QVector& vecRecords) +{ + foreach(const nmFlowGaugeRecord& oRecord, vecRecords) { + if(oRecord.eStatus != NM_GaugeRecord_Usable) { + continue; + } + if(hasRealFlowSegment(oRecord.vecOilPoints) || + hasRealFlowSegment(oRecord.vecGasPoints) || + hasRealFlowSegment(oRecord.vecWaterPoints)) { + return oRecord.sGaugeCode; + } + } + return QString(); +} + +// 把枚举到的记录列表写入数值井,并按默认规则选中压力/流量记录。 +// 找不到任何可用记录时仍会写入(可能为空的)列表,只是选中 Code 为空——不阻止建井。 +void nmDataAnalyzeManager::applyGaugeRecordsToWell( + nmDataWellBase* pWellData, + const QVector& vecPressureRecords, + const QVector& vecFlowRecords) +{ + if(pWellData == nullptr) { + return; + } + pWellData->setPressureRecords(vecPressureRecords); + pWellData->setFlowRecords(vecFlowRecords); + pWellData->selectPressureGaugeCode( + selectDefaultPressureGaugeCode(vecPressureRecords)); + pWellData->selectFlowGaugeCode( + selectDefaultFlowGaugeCode(vecFlowRecords)); +} + static bool isWellParaAttrName(const QString& name) { for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i) { @@ -1447,11 +1602,11 @@ void nmDataAnalyzeManager::initCurWellData() // 判断是哪一种井类型,初始化对应的参数 QString wellClass = pWellData->getWellClassEn(); - QVector vecPtsP; - QVector vecPtsF; + QVector vecPressureRecords; + QVector vecFlowRecords; NM_WELL_CATEGORY eWellCategory = NM_WellCategory_Unknown; - if(!readFrameworkWellCurves(pWellData, vecPtsP, vecPtsF, - eWellCategory)) { + if(!readFrameworkWellCurves(pWellData, vecPressureRecords, + vecFlowRecords, eWellCategory)) { qWarning() << "Cannot import numerical well with invalid category:" << pWellData->getCode(); return; @@ -1478,9 +1633,8 @@ void nmDataAnalyzeManager::initCurWellData() tempAttr.setValue(pWellData->getWellRadius()); pVerticalWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - pVerticalWell->setPressurePoints(vecPtsP); - pVerticalWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + applyGaugeRecordsToWell(pVerticalWell, vecPressureRecords, vecFlowRecords); QVector> vvecHistoryPressureData; //压力历史数据 QVector> vvecHistoryLogData; // 历史双对数曲线数据 @@ -1517,9 +1671,8 @@ void nmDataAnalyzeManager::initCurWellData() tempAttr.setValue(pWellData->getWellRadius()); pVFracturedWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - pVFracturedWell->setPressurePoints(vecPtsP); - pVFracturedWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + applyGaugeRecordsToWell(pVFracturedWell, vecPressureRecords, vecFlowRecords); QVector> vvecHistoryPressureData; //压力历史数据 QVector> vvecHistoryLogData; // 历史双对数曲线数据 @@ -1559,9 +1712,8 @@ void nmDataAnalyzeManager::initCurWellData() tempAttr.setValue(pWellData->getWellRadius()); pHFracturedWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - pHFracturedWell->setPressurePoints(vecPtsP); - pHFracturedWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + applyGaugeRecordsToWell(pHFracturedWell, vecPressureRecords, vecFlowRecords); QVector> vvecHistoryPressureData; //压力历史数据 QVector> vvecHistoryLogData; // 历史双对数曲线数据 @@ -1703,13 +1855,13 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData) return pExistingWell; } - // 井别决定流量大字段;压力仍读取第一条压力数据的主对象。 + // 压力及流量记录统一由工程井读取入口解析。 const QString sWellClass = pWellData->getWellClassEn(); - QVector vecPtsP; - QVector vecPtsF; + QVector vecPressureRecords; + QVector vecFlowRecords; NM_WELL_CATEGORY eWellCategory = NM_WellCategory_Unknown; - if(!readFrameworkWellCurves(pWellData, vecPtsP, vecPtsF, - eWellCategory)) { + if(!readFrameworkWellCurves(pWellData, vecPressureRecords, + vecFlowRecords, eWellCategory)) { qWarning() << "Cannot append numerical well with invalid category:" << pWellData->getCode(); return nullptr; @@ -1738,9 +1890,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData) tempAttr.setValue(pWellData->getWellRadius()); pVerticalWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - pVerticalWell->setPressurePoints(vecPtsP); - pVerticalWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + applyGaugeRecordsToWell(pVerticalWell, vecPressureRecords, vecFlowRecords); // 其他井默认选择最后一个真实流动段,索引保持一基。 pVerticalWell->setIndexF(pVerticalWell->getFlowSegmentCount()); @@ -1767,9 +1918,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData) tempAttr.setValue(pWellData->getWellRadius()); pVFracturedWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - pVFracturedWell->setPressurePoints(vecPtsP); - pVFracturedWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + applyGaugeRecordsToWell(pVFracturedWell, vecPressureRecords, vecFlowRecords); // 更新裂缝位置信息 pVFracturedWell->setFracs(); @@ -1798,9 +1948,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData) tempAttr.setValue(pWellData->getWellRadius()); pHFracturedWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - pHFracturedWell->setPressurePoints(vecPtsP); - pHFracturedWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + applyGaugeRecordsToWell(pHFracturedWell, vecPressureRecords, vecFlowRecords); // 计算裂缝数据 pHFracturedWell->setFracs(); @@ -6151,12 +6300,12 @@ bool nmDataAnalyzeManager::loadWellPreAndFlow() ZxDataWell* pFrameworkWell = mapFrameworkWells.value(pWell->getWellCode(), nullptr); - QVector vecPressurePoints; - QVector vecFlowPoints; + QVector vecPressureRecords; + QVector vecFlowRecords; NM_WELL_CATEGORY eFrameworkCategory = NM_WellCategory_Unknown; if(!readFrameworkWellCurves(pFrameworkWell, - vecPressurePoints, - vecFlowPoints, + vecPressureRecords, + vecFlowRecords, eFrameworkCategory) || pWell->getWellCategory() != eFrameworkCategory) { qWarning() << "Numerical well category does not match framework well:" @@ -6164,8 +6313,7 @@ bool nmDataAnalyzeManager::loadWellPreAndFlow() return false; } - pWell->setPressurePoints(vecPressurePoints); - pWell->setFlowPoints(vecFlowPoints); + applyGaugeRecordsToWell(pWell, vecPressureRecords, vecFlowRecords); } return true; } diff --git a/Src/nmNum/nmData/nmDataWellBase.cpp b/Src/nmNum/nmData/nmDataWellBase.cpp index 9bbc63b6..aa91bb53 100644 --- a/Src/nmNum/nmData/nmDataWellBase.cpp +++ b/Src/nmNum/nmData/nmDataWellBase.cpp @@ -11,6 +11,43 @@ #include #include +/** + * @brief 按 Gauge Code 在记录列表中查找可写记录指针;Code 为空或未命中时返回 nullptr。 + * @note 阶段一内部使用,不对外暴露;空 Code 统一表示“未选中”,因此不参与查找。 + */ +template +static TRecord* findGaugeRecordByCode(QVector& vecRecords, const QString& sGaugeCode) +{ + if(sGaugeCode.isEmpty()) { + return nullptr; + } + for(int i = 0; i < vecRecords.size(); ++i) { + if(vecRecords[i].sGaugeCode == sGaugeCode) { + return &vecRecords[i]; + } + } + return nullptr; +} + +template +static const TRecord* findGaugeRecordByCode(const QVector& vecRecords, const QString& sGaugeCode) +{ + if(sGaugeCode.isEmpty()) { + return nullptr; + } + for(int i = 0; i < vecRecords.size(); ++i) { + if(vecRecords[i].sGaugeCode == sGaugeCode) { + return &vecRecords[i]; + } + } + return nullptr; +} + +// setPressurePoints/setFlowPoints 等旧接口在没有选中记录时用来创建内部占位记录的 Code。 +// 使用非空占位符,避免与“空 Code = 未选中”的约定冲突;阶段二引入选择 UI 前不会对外暴露该记录。 +static const QString kManualPressureGaugeCode = QString::fromLatin1("__ManualPressureRecord__"); +static const QString kManualFlowGaugeCode = QString::fromLatin1("__ManualFlowRecord__"); + nmDataWellBase::nmDataWellBase() : m_eWellCategory(NM_WellCategory_Unknown) , m_nIndexF(0) @@ -124,8 +161,10 @@ nmDataWellBase& nmDataWellBase::operator=(const nmDataWellBase& other) m_wellboreModel = other.m_wellboreModel; m_wellboreStorage = other.m_wellboreStorage; m_bottomholeMD = other.m_bottomholeMD; - m_vecPtsP = other.m_vecPtsP; - m_vecPtsF = other.m_vecPtsF; + m_vecPressureRecords = other.m_vecPressureRecords; + m_vecFlowRecords = other.m_vecFlowRecords; + m_sSelectedPressureGaugeCode = other.m_sSelectedPressureGaugeCode; + m_sSelectedFlowGaugeCode = other.m_sSelectedFlowGaugeCode; m_nIndexF = other.m_nIndexF; m_finalWellboreStorage = other.m_finalWellboreStorage; m_inputWellHead = other.m_inputWellHead; @@ -667,33 +706,162 @@ void nmDataWellBase::setTimeDependentSkin(bool enabled) m_bTimeDependentSkin = enabled; } -// 获取压力曲线点数据 +// 获取压力曲线点数据:委托到当前选中的压力记录,未选中或未命中时返回空。 QVector nmDataWellBase::getPressurePoints() const { - return m_vecPtsP; + const nmPressureGaugeRecord* pRecord = + findGaugeRecordByCode(m_vecPressureRecords, m_sSelectedPressureGaugeCode); + return pRecord == nullptr ? QVector() : pRecord->vecPressurePoints; } -// 设置压力曲线点数据 +// 设置压力曲线点数据:写入当前选中记录;若无选中记录,新建一条内部占位记录承载数据, +// 兼容井型切换等历史调用方对该 setter 的既有假设。 void nmDataWellBase::setPressurePoints(const QVector& points) { - m_vecPtsP = points; + nmPressureGaugeRecord* pRecord = + findGaugeRecordByCode(m_vecPressureRecords, m_sSelectedPressureGaugeCode); + if(pRecord == nullptr) { + nmPressureGaugeRecord newRecord; + newRecord.sGaugeCode = kManualPressureGaugeCode; + m_vecPressureRecords.append(newRecord); + m_sSelectedPressureGaugeCode = kManualPressureGaugeCode; + pRecord = &m_vecPressureRecords.last(); + } + pRecord->vecPressurePoints = points; + pRecord->eStatus = points.isEmpty() ? NM_GaugeRecord_Empty : NM_GaugeRecord_Usable; } -// 获取流量曲线点数据 +// 获取井别对应相的流量曲线点数据 QVector nmDataWellBase::getFlowPoints() const { - return m_vecPtsF; + return getFlowPoints(getReferenceFlowPhase()); } -// 设置流量曲线点数据 +// 设置井别对应相的流量曲线点数据 void nmDataWellBase::setFlowPoints(const QVector& points) { - m_vecPtsF = points; + setFlowPoints(getReferenceFlowPhase(), points); } -QVector nmDataWellBase::getFlowSegmentPoints() const +NM_PHASE_TYPE nmDataWellBase::getReferenceFlowPhase() const +{ + switch(m_eWellCategory) { + case NM_WellCategory_Oil: + return PHASE_Oil; + case NM_WellCategory_Gas: + return PHASE_Gas; + case NM_WellCategory_Water: + return PHASE_Water; + default: + return PHASE_UNKNOWN; + } +} + +// 获取指定相流量点数据:委托到当前选中的流量记录,未选中或未命中时返回空。 +QVector nmDataWellBase::getFlowPoints( + NM_PHASE_TYPE eFlowPhase) const +{ + const nmFlowGaugeRecord* pRecord = + findGaugeRecordByCode(m_vecFlowRecords, m_sSelectedFlowGaugeCode); + if(pRecord == nullptr) { + return QVector(); + } + switch(eFlowPhase) { + case PHASE_Oil: + return pRecord->vecOilPoints; + case PHASE_Gas: + return pRecord->vecGasPoints; + case PHASE_Water: + return pRecord->vecWaterPoints; + default: + return QVector(); + } +} + +// 设置指定相流量点数据:写入当前选中记录;若无选中记录,新建一条内部占位记录承载数据, +// 兼容井型切换等历史调用方对该 setter 的既有假设。 +void nmDataWellBase::setFlowPoints( + NM_PHASE_TYPE eFlowPhase, + const QVector& points) +{ + nmFlowGaugeRecord* pRecord = + findGaugeRecordByCode(m_vecFlowRecords, m_sSelectedFlowGaugeCode); + if(pRecord == nullptr) { + nmFlowGaugeRecord newRecord; + newRecord.sGaugeCode = kManualFlowGaugeCode; + m_vecFlowRecords.append(newRecord); + m_sSelectedFlowGaugeCode = kManualFlowGaugeCode; + pRecord = &m_vecFlowRecords.last(); + } + + switch(eFlowPhase) { + case PHASE_Oil: + pRecord->vecOilPoints = points; + break; + case PHASE_Gas: + pRecord->vecGasPoints = points; + break; + case PHASE_Water: + pRecord->vecWaterPoints = points; + break; + default: + break; + } + + pRecord->eStatus = (pRecord->vecOilPoints.isEmpty() + && pRecord->vecGasPoints.isEmpty() + && pRecord->vecWaterPoints.isEmpty()) + ? NM_GaugeRecord_Empty : NM_GaugeRecord_Usable; +} + +// 设置全部压力记录列表(不改变当前选中 Code)。 +void nmDataWellBase::setPressureRecords(const QVector& vecRecords) +{ + m_vecPressureRecords = vecRecords; +} + +// 设置全部流量记录列表(不改变当前选中 Code)。 +void nmDataWellBase::setFlowRecords(const QVector& vecRecords) +{ + m_vecFlowRecords = vecRecords; +} + +// 按 Gauge Code 选中一条压力记录;传入空字符串表示无选中。 +void nmDataWellBase::selectPressureGaugeCode(const QString& sGaugeCode) +{ + m_sSelectedPressureGaugeCode = sGaugeCode; +} + +// 按 Gauge Code 选中一条流量记录;传入空字符串表示无选中。 +void nmDataWellBase::selectFlowGaugeCode(const QString& sGaugeCode) +{ + m_sSelectedFlowGaugeCode = sGaugeCode; +} + +QString nmDataWellBase::getSelectedPressureGaugeCode() const +{ + return m_sSelectedPressureGaugeCode; +} + +QString nmDataWellBase::getSelectedFlowGaugeCode() const +{ + return m_sSelectedFlowGaugeCode; +} + +QVector nmDataWellBase::getPressureRecords() const { - QVector vecSegments = m_vecPtsF; + return m_vecPressureRecords; +} + +QVector nmDataWellBase::getFlowRecords() const +{ + return m_vecFlowRecords; +} + +QVector nmDataWellBase::getFlowSegmentPoints( + NM_PHASE_TYPE eFlowPhase) const +{ + QVector vecSegments = getFlowPoints(eFlowPhase); // 工程流量数据通常以 (0,0) 作为非业务占位点;真实数据没有占位点时不得误删首段。 if(!vecSegments.isEmpty() && vecSegments.first().x() == 0.0 && @@ -703,14 +871,31 @@ QVector nmDataWellBase::getFlowSegmentPoints() const return vecSegments; } +int nmDataWellBase::getFlowSegmentCount(NM_PHASE_TYPE eFlowPhase) const +{ + return getFlowSegmentPoints(eFlowPhase).size(); +} + +bool nmDataWellBase::hasFlowPhaseData(NM_PHASE_TYPE eFlowPhase) const +{ + return getFlowSegmentCount(eFlowPhase) > 0; +} + +bool nmDataWellBase::hasAnyFlowData() const +{ + return hasFlowPhaseData(PHASE_Oil) || + hasFlowPhaseData(PHASE_Gas) || + hasFlowPhaseData(PHASE_Water); +} + +QVector nmDataWellBase::getFlowSegmentPoints() const +{ + return getFlowSegmentPoints(getReferenceFlowPhase()); +} + int nmDataWellBase::getFlowSegmentCount() const { - if(m_vecPtsF.isEmpty()) { - return 0; - } - return m_vecPtsF.first().x() == 0.0 && - m_vecPtsF.first().y() == 0.0 - ? m_vecPtsF.size() - 1 : m_vecPtsF.size(); + return getFlowSegmentCount(getReferenceFlowPhase()); } bool nmDataWellBase::hasValidFlowSectionIndex() const diff --git a/Src/nmNum/nmPlot/nmGuiPlot.cpp b/Src/nmNum/nmPlot/nmGuiPlot.cpp index 37b6d994..83f43f02 100644 --- a/Src/nmNum/nmPlot/nmGuiPlot.cpp +++ b/Src/nmNum/nmPlot/nmGuiPlot.cpp @@ -252,71 +252,19 @@ void nmGuiPlot::initDefultGeoObj() // 判断是哪一种井类型,初始化对应的参数 QString wellClass = pWellData->getWellClassEn(); - const NM_WELL_CATEGORY eWellCategory = - static_cast( - pWellData->getWellTypeIndex()); - if(!nmIsValidWellCategory(eWellCategory)) { + + QVector vecPressureRecords; + QVector vecFlowRecords; + NM_WELL_CATEGORY eWellCategory = NM_WellCategory_Unknown; + if(!nmDataAnalyzeManager::readFrameworkWellCurves( + pWellData, + vecPressureRecords, + vecFlowRecords, + eWellCategory)) { this->setPlotsByDataManger(pDataManager); return; } - // 获取当前井的压力、流量数据 - ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP); - ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF); - - ZxDataGaugeP* pGaugeP = nullptr; - ZxDataGaugeF* pGaugeF = nullptr; - - // 遍历压力数据列表 - for(int i = 0; i < m_listGaugeP.size(); ++i) { - if(pGaugeP = dynamic_cast(m_listGaugeP[i])) { // 拿到第一条压力数据 - break; - } - } - - // 遍历流量数据列表 - for(int i = 0; i < m_listGaugeF.size(); ++i) { - if(pGaugeF = dynamic_cast(m_listGaugeF[i])) { // 拿到第一条流量数据 - break; - } - } - - // 获取的压力、流量数据 - QVector vecPtsP, vecPtsF; - vecPtsP.clear(); - vecPtsF.clear(); - // 临时存储x,y坐标 - VecDouble vecX, vecY; - - if(pGaugeP != nullptr) { - // 获取压力数据 - if(pGaugeP->getDataVecXY(vecX, vecY, 0)) { - for(int i = 0; i < vecX.size(); ++i) { - if(i < vecY.size()) { - QPointF pt(vecX[i], vecY[i]); - vecPtsP.append(pt); - } - } - } - } - - if(pGaugeF != nullptr) { - vecX.clear(); - vecY.clear(); - - // 获取流量数据 - // 井别是流量大字段索引,单相井也不能固定读取默认的油相字段。 - if(pGaugeF->getDataVecXY( - vecX, vecY, static_cast(eWellCategory))) { - for(int i = 0; i < vecX.size(); ++i) { - if(i < vecY.size()) { - QPointF pt(vecX[i], vecY[i]); - vecPtsF.append(pt); - } - } - } - } - // 历史数据 QVector> vvecHistoryPressureData; QVector> vvecHistoryLogData; @@ -345,9 +293,9 @@ void nmGuiPlot::initDefultGeoObj() tempAttr.setValue(pWellData->getWellRadius()); m_VerticalWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - m_VerticalWell->setPressurePoints(vecPtsP); - m_VerticalWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + nmDataAnalyzeManager::applyGaugeRecordsToWell( + m_VerticalWell, vecPressureRecords, vecFlowRecords); // TODO: 计算井的历史双对数/半对数数据 pDataManager->calculationLogData(m_VerticalWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData); @@ -383,9 +331,9 @@ void nmGuiPlot::initDefultGeoObj() tempAttr.setValue(pWellData->getWellRadius()); m_VFracturedWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - m_VFracturedWell->setPressurePoints(vecPtsP); - m_VFracturedWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + nmDataAnalyzeManager::applyGaugeRecordsToWell( + m_VFracturedWell, vecPressureRecords, vecFlowRecords); // TODO: 计算井的历史双对数/半对数数据 pDataManager->calculationLogData(m_VFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData); @@ -424,9 +372,9 @@ void nmGuiPlot::initDefultGeoObj() tempAttr.setValue(pWellData->getWellRadius()); m_HFracturedWell->setRadius(tempAttr); - // 设置井的压力数据、流量数据 - m_HFracturedWell->setPressurePoints(vecPtsP); - m_HFracturedWell->setFlowPoints(vecPtsF); + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + nmDataAnalyzeManager::applyGaugeRecordsToWell( + m_HFracturedWell, vecPressureRecords, vecFlowRecords); // TODO: 计算井的历史双对数/半对数数据 pDataManager->calculationLogData(m_HFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData); diff --git a/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp b/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp index 60354984..87deaf42 100644 --- a/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp +++ b/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp @@ -1747,9 +1747,14 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic, return false; } - const NM_WELL_CATEGORY eWellCategory = - static_cast(pWellData->getWellTypeIndex()); - if(!nmIsValidWellCategory(eWellCategory)) { + QVector vecPressureRecords; + QVector vecFlowRecords; + NM_WELL_CATEGORY eWellCategory = NM_WellCategory_Unknown; + if(!nmDataAnalyzeManager::readFrameworkWellCurves( + pWellData, + vecPressureRecords, + vecFlowRecords, + eWellCategory)) { return false; } @@ -1772,62 +1777,13 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic, tempAttr.setValue(pWellData->getWellRadius()); pData->setRadius(tempAttr); - ZxDataObjectList listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP); - ZxDataObjectList listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF); - ZxDataGaugeP* pGaugeP = nullptr; - ZxDataGaugeF* pGaugeF = nullptr; + // 设置井的压力、流量记录列表,并按默认规则选中当前记录 + nmDataAnalyzeManager::applyGaugeRecordsToWell( + pData, vecPressureRecords, vecFlowRecords); - // 从工程树井节点下取第一条压力/流量数据,转换成数值井需要的曲线点。 - for(int i = 0; i < listGaugeP.size(); ++i) { - if(pGaugeP = dynamic_cast(listGaugeP[i])) { - break; - } - } - - for(int i = 0; i < listGaugeF.size(); ++i) { - if(pGaugeF = dynamic_cast(listGaugeF[i])) { - break; - } - } - - QVector pressurePoints, flowPoints; - VecDouble vecX, vecY; - if(pGaugeP != nullptr && pGaugeP->getDataVecXY(vecX, vecY, 0)) { - for(int i = 0; i < vecX.size(); ++i) { - if(i < vecY.size()) { - pressurePoints.append(QPointF(vecX[i], vecY[i])); - } - } - } - - vecX.clear(); - vecY.clear(); - // 流量大字段的 0/1/2 分别对应油井、气井和水井,禁止使用默认索引猜测。 - if(pGaugeF != nullptr && - pGaugeF->getDataVecXY(vecX, vecY, - static_cast(eWellCategory))) { - for(int i = 0; i < vecX.size(); ++i) { - if(i < vecY.size()) { - flowPoints.append(QPointF(vecX[i], vecY[i])); - } - } - } - - int nFlowSectionIndex = -2; if(pWellData != zxCurWell) { - // 非当前查看井在设置流量点后选择最后一个真实段,索引保持一基。 - nFlowSectionIndex = flowPoints.size(); - if(!flowPoints.isEmpty() && flowPoints.first().x() == 0.0 && - flowPoints.first().y() == 0.0) { - --nFlowSectionIndex; - } - } - - pData->setPressurePoints(pressurePoints); - pData->setFlowPoints(flowPoints); - - if(nFlowSectionIndex >= -1) { - pData->setIndexF(nFlowSectionIndex); + // 非当前查看井仍默认选择井别参考相的最后一个真实段,索引保持一基。 + pData->setIndexF(pData->getFlowSegmentCount()); } if (wellType == NM_WELL_MODEL::Vertical_Fractured_Well) { diff --git a/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp b/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp index ffb1d761..66224077 100644 --- a/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp +++ b/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp @@ -376,7 +376,12 @@ bool nmWellEditorSession::copySharedWellData( // 第二步:保留输入历史和已有计算结果,切换井型不应丢失曲线数据。 pTarget->setPressurePoints(pSource->getPressurePoints()); - pTarget->setFlowPoints(pSource->getFlowPoints()); + pTarget->setFlowPoints(PHASE_Oil, + pSource->getFlowPoints(PHASE_Oil)); + pTarget->setFlowPoints(PHASE_Gas, + pSource->getFlowPoints(PHASE_Gas)); + pTarget->setFlowPoints(PHASE_Water, + pSource->getFlowPoints(PHASE_Water)); pTarget->setIndexF(pSource->getIndexF()); pTarget->setHistoryPressure(pSource->getHistoryPressure()); pTarget->setHistoryLogLog(pSource->getHistoryLogLog()); diff --git a/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp b/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp index c6116831..84fc80a1 100644 --- a/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp +++ b/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp @@ -786,8 +786,16 @@ void nmWxEditWellPlot::onWellTypeChanged(int index) // 历史数据 pNewWellData->setPressurePoints(pOldWellData->getPressurePoints()); - pNewWellData->setFlowPoints(pOldWellData->getFlowPoints()); pNewWellData->setWellCategory(pOldWellData->getWellCategory()); + pNewWellData->setFlowPoints( + PHASE_Oil, + pOldWellData->getFlowPoints(PHASE_Oil)); + pNewWellData->setFlowPoints( + PHASE_Gas, + pOldWellData->getFlowPoints(PHASE_Gas)); + pNewWellData->setFlowPoints( + PHASE_Water, + pOldWellData->getFlowPoints(PHASE_Water)); pNewWellData->setIndexF(pOldWellData->getIndexF()); // 结果数据