fix(nmNum): 统一压力流量记录默认选择规则

- 区分 Gauge 数据解析状态与参与计算条件
- 压力记录要求至少两个点,流量记录要求至少一相存在非零产量
- 统一首次默认选择、Preferred Code 恢复及包含井筛选规则
- 复用井级非零产量判断,清理重复的局部实现
- 保留全部可解析记录用于预览,并完善 Usable 状态注释
feature/nmNum-multi-gauge-record
lh 2 days ago
parent f81f0aac36
commit ce10b43327

@ -261,8 +261,8 @@ public:
NM_WELL_CATEGORY& eWellCategory);
/**
* @brief
* @note Code
* @brief
* @note Code
*/
static void applyGaugeRecordsToWell(
nmDataWellBase* pWellData,

@ -15,7 +15,7 @@ struct nmPressureGaugeRecord {
QString sGaugeCode; // 对应 ZxDataGaugeP::getCode(),选择持久化键
QString sGaugeName; // 对应 ZxDataGaugeP::getGaugeName(),界面展示用
QString sGaugeTime; // 对应 ZxDataGaugeP::getGaugeTime(),界面展示用
NM_GAUGE_RECORD_STATUS eStatus; // 记录可用状态
NM_GAUGE_RECORD_STATUS eStatus; // 记录解析状态Usable 不代表满足历史计算点数
QVector<QPointF> vecPressurePoints; // 主块解析出的压力曲线点
nmPressureGaugeRecord()
@ -24,6 +24,14 @@ struct nmPressureGaugeRecord {
}
};
/** @brief 压力记录可参与历史计算:数据可解析且至少包含两个压力点。 */
inline bool nmIsSelectablePressureRecord(
const nmPressureGaugeRecord& oRecord)
{
return oRecord.eStatus == NM_GaugeRecord_Usable &&
oRecord.vecPressurePoints.size() >= 2;
}
/**
* @brief
* @note GaugeDataEx2 N []
@ -35,7 +43,7 @@ struct nmFlowGaugeRecord {
QString sGaugeName; // 对应 ZxDataGaugeF::getGaugeName(),界面展示用
QString sGaugeTime; // 对应 ZxDataGaugeF::getGaugeTime(),界面展示用
bool bMultiPhase; // 实际是否采用 GaugeDataEx2 多相表,不依赖框架多相标志
NM_GAUGE_RECORD_STATUS eStatus; // 记录可用状态
NM_GAUGE_RECORD_STATUS eStatus; // 记录解析状态Usable 不代表存在非零产量
QVector<QPointF> vecOilPoints; // 油相流量点,允许首项为 (0,0) 占位点
QVector<QPointF> vecGasPoints; // 气相流量点,允许首项为 (0,0) 占位点
QVector<QPointF> vecWaterPoints; // 水相流量点,允许首项为 (0,0) 占位点
@ -49,6 +57,26 @@ struct nmFlowGaugeRecord {
}
};
/** @brief 一组流量点中是否至少存在一个非零产量。 */
inline bool nmHasNonZeroFlowRate(const QVector<QPointF>& vecPoints)
{
for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) {
if(vecPoints[nIndex].y() != 0.0) {
return true;
}
}
return false;
}
/** @brief 流量记录可参与计算:数据可解析且至少一个相存在非零产量。 */
inline bool nmIsSelectableFlowRecord(const nmFlowGaugeRecord& oRecord)
{
return oRecord.eStatus == NM_GaugeRecord_Usable &&
(nmHasNonZeroFlowRate(oRecord.vecOilPoints) ||
nmHasNonZeroFlowRate(oRecord.vecGasPoints) ||
nmHasNonZeroFlowRate(oRecord.vecWaterPoints));
}
/** @brief 与井角色无关、供历史曲线和求解捕获共同使用的规范化 Gauge 输入。 */
struct nmWellGaugeInputData {
QString sWellInstanceId;

@ -73,9 +73,10 @@ inline bool nmIsValidWellCategory(NM_WELL_CATEGORY eWellCategory)
eWellCategory == NM_WellCategory_Water;
}
// 工程压力/流量测量记录的可用状态。损坏与空记录仍保留在列表中,只是不可被选中。
// 工程压力/流量测量记录的解析状态。损坏与空记录仍保留在列表中;
// 解析成功后,是否可参与计算仍需按记录内容进一步判断。
enum NM_GAUGE_RECORD_STATUS {
NM_GaugeRecord_Usable, // 数据完整,可被选中使用
NM_GaugeRecord_Usable, // 数据解析成功,是否可参与计算需进一步判断
NM_GaugeRecord_Empty, // 主/多相字节块均为空,不算数据损坏
NM_GaugeRecord_Corrupt, // 字节块解析失败或 X/Y 长度不一致
NM_GaugeRecord_InvalidIdentity // Gauge Code 为空或在同一口井内重复

@ -441,50 +441,32 @@ bool nmDataAnalyzeManager::readFrameworkWellCurves(
return true;
}
// 判断某相流量点是否至少包含一个真实流动段,去除可选首个 (0,0) 占位点后非空即为真实。
static bool hasRealFlowSegment(const QVector<QPointF>& 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<nmPressureGaugeRecord>& vecRecords)
{
foreach(const nmPressureGaugeRecord& oRecord, vecRecords) {
if(oRecord.eStatus == NM_GaugeRecord_Usable) {
if(nmIsSelectablePressureRecord(oRecord)) {
return oRecord.sGaugeCode;
}
}
return QString();
}
// 从流量记录列表中选出框架顺序中第一条至少含一个真实相的记录;找不到则返回空 Code
// 从流量记录列表中选出框架顺序中第一条至少含一个非零产量相的记录。
static QString selectDefaultFlowGaugeCode(
const QVector<nmFlowGaugeRecord>& vecRecords)
{
foreach(const nmFlowGaugeRecord& oRecord, vecRecords) {
if(oRecord.eStatus != NM_GaugeRecord_Usable) {
continue;
}
if(hasRealFlowSegment(oRecord.vecOilPoints) ||
hasRealFlowSegment(oRecord.vecGasPoints) ||
hasRealFlowSegment(oRecord.vecWaterPoints)) {
if(nmIsSelectableFlowRecord(oRecord)) {
return oRecord.sGaugeCode;
}
}
return QString();
}
// 把枚举到的记录列表写入数值井;只恢复仍然存在且可用的首选记录。
// 找不到任何可记录时仍会写入(可能为空的)列表,只是选中 Code 为空——不阻止建井。
// 把枚举到的记录列表写入数值井;只恢复仍存在且可参与计算的首选记录。
// 找不到任何可记录时仍会写入(可能为空的)列表,只是选中 Code 为空——不阻止建井。
void nmDataAnalyzeManager::applyGaugeRecordsToWell(
nmDataWellBase* pWellData,
const QVector<nmPressureGaugeRecord>& vecPressureRecords,
@ -504,7 +486,7 @@ void nmDataAnalyzeManager::applyGaugeRecordsToWell(
for(int nIndex = 0; nIndex < vecPressureRecords.size(); ++nIndex) {
if(vecPressureRecords[nIndex].sGaugeCode ==
sPreferredPressureGaugeCode &&
vecPressureRecords[nIndex].eStatus == NM_GaugeRecord_Usable) {
nmIsSelectablePressureRecord(vecPressureRecords[nIndex])) {
sSelectedPressureGaugeCode = sPreferredPressureGaugeCode;
break;
}
@ -515,7 +497,7 @@ void nmDataAnalyzeManager::applyGaugeRecordsToWell(
selectDefaultFlowGaugeCode(vecFlowRecords);
for(int nIndex = 0; nIndex < vecFlowRecords.size(); ++nIndex) {
if(vecFlowRecords[nIndex].sGaugeCode == sPreferredFlowGaugeCode &&
vecFlowRecords[nIndex].eStatus == NM_GaugeRecord_Usable) {
nmIsSelectableFlowRecord(vecFlowRecords[nIndex])) {
sSelectedFlowGaugeCode = sPreferredFlowGaugeCode;
break;
}

@ -51,17 +51,6 @@ static const TRecord* findGaugeRecordByCode(const QVector<TRecord>& vecRecords,
static const QString kManualPressureGaugeCode = QString::fromLatin1("__ManualPressureRecord__");
static const QString kManualFlowGaugeCode = QString::fromLatin1("__ManualFlowRecord__");
// 判断一组流动段中是否存在实际产量;公共 (0,0) 占位点和全零相均不算产量。
static bool hasNonZeroFlowSegment(const QVector<QPointF>& vecPoints)
{
for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) {
if(vecPoints[nIndex].y() != 0.0) {
return true;
}
}
return false;
}
static void appendGaugeUInt32(QByteArray& baData, quint32 nValue)
{
for(int nByte = 0; nByte < 4; ++nByte) {
@ -1237,12 +1226,7 @@ bool nmDataWellBase::hasAvailableFlowRate() const
nRecordIndex < m_vecFlowRecords.size();
++nRecordIndex) {
const nmFlowGaugeRecord& oRecord = m_vecFlowRecords[nRecordIndex];
if(oRecord.eStatus != NM_GaugeRecord_Usable) {
continue;
}
if(hasNonZeroFlowSegment(oRecord.vecOilPoints) ||
hasNonZeroFlowSegment(oRecord.vecGasPoints) ||
hasNonZeroFlowSegment(oRecord.vecWaterPoints)) {
if(nmIsSelectableFlowRecord(oRecord)) {
return true;
}
}
@ -1261,7 +1245,7 @@ NM_PHASE_TYPE nmDataWellBase::getFlowSchedulePhase() const
// 公共流量制度优先使用井别对应且已启用的相;该相被设为“无”时,
// 再按油、气、水顺序选择其他已启用且有数据的相作为参考流量。
const NM_PHASE_TYPE eReferencePhase = getReferenceFlowPhase();
if(isFlowPhaseUsed(eReferencePhase) && hasNonZeroFlowSegment(
if(isFlowPhaseUsed(eReferencePhase) && nmHasNonZeroFlowRate(
getFlowSegmentPoints(eReferencePhase))) {
return eReferencePhase;
}
@ -1274,7 +1258,7 @@ NM_PHASE_TYPE nmDataWellBase::getFlowSchedulePhase() const
}
const QVector<QPointF> vecPoints =
getFlowSegmentPoints(arrPhases[nIndex]);
if(hasNonZeroFlowSegment(vecPoints)) {
if(nmHasNonZeroFlowRate(vecPoints)) {
return arrPhases[nIndex];
}
}

@ -44,25 +44,12 @@ const QVector<QPointF>& phasePoints(
}
}
// “有该相产量”按至少一个非零值判断;只有公共零占位行不算真实产量。
bool hasNonZeroPhaseRate(
const nmFlowGaugeRecord& oRecord, NM_PHASE_TYPE ePhase)
{
const QVector<QPointF>& vecPoints = phasePoints(oRecord, ePhase);
for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) {
if(vecPoints[nIndex].y() != 0.0) {
return true;
}
}
return false;
}
// 下拉项只有在记录可用且该相确有非零产量时才允许选择。
bool isSelectableFlowPhase(
const nmFlowGaugeRecord& oRecord, NM_PHASE_TYPE ePhase)
{
return oRecord.eStatus == NM_GaugeRecord_Usable &&
hasNonZeroPhaseRate(oRecord, ePhase);
nmHasNonZeroFlowRate(phasePoints(oRecord, ePhase));
}
// 候选井至少要有一条可选分相记录;压力记录不是主动井候选的必要条件。
@ -70,9 +57,7 @@ bool hasSelectableRateRecord(const QVector<nmFlowGaugeRecord>& vecRecords)
{
for(int nIndex = 0; nIndex < vecRecords.size(); ++nIndex) {
const nmFlowGaugeRecord& oRecord = vecRecords[nIndex];
if(isSelectableFlowPhase(oRecord, PHASE_Oil) ||
isSelectableFlowPhase(oRecord, PHASE_Gas) ||
isSelectableFlowPhase(oRecord, PHASE_Water)) {
if(nmIsSelectableFlowRecord(oRecord)) {
return true;
}
}
@ -110,13 +95,6 @@ nmFlowGaugeRecord* findMutableFlowRecord(
return nullptr;
}
// 压力曲线至少需要两个点才能用于历史曲线计算。
bool isSelectablePressureRecord(const nmPressureGaugeRecord& oRecord)
{
return oRecord.eStatus == NM_GaugeRecord_Usable &&
oRecord.vecPressurePoints.size() >= 2;
}
// 打开对话框时只恢复仍存在且仍可用的压力记录 Code。
bool containsSelectablePressureCode(
const QVector<nmPressureGaugeRecord>& vecRecords,
@ -124,7 +102,7 @@ bool containsSelectablePressureCode(
{
for(int nIndex = 0; nIndex < vecRecords.size(); ++nIndex) {
if(vecRecords[nIndex].sGaugeCode == sGaugeCode &&
isSelectablePressureRecord(vecRecords[nIndex])) {
nmIsSelectablePressureRecord(vecRecords[nIndex])) {
return true;
}
}
@ -429,7 +407,7 @@ void nmWxIncludeOtherWells::setupTable()
++nRecordIndex) {
const nmFlowGaugeRecord& oRecord =
oWellRow.m_vecFlowRecords[nRecordIndex];
if(oRecord.eStatus != NM_GaugeRecord_Usable) {
if(!nmIsSelectableFlowRecord(oRecord)) {
continue;
}
oWellRow.m_sFlowGaugeCode = oRecord.sGaugeCode;
@ -718,7 +696,7 @@ void nmWxIncludeOtherWells::populatePressureCombo(
pComboBox->setItemData(0, tr("None"), Qt::ToolTipRole);
for(int nIndex = 0; nIndex < data.m_vecPressureRecords.size(); ++nIndex) {
const nmPressureGaugeRecord& oRecord = data.m_vecPressureRecords[nIndex];
if(!isSelectablePressureRecord(oRecord)) {
if(!nmIsSelectablePressureRecord(oRecord)) {
continue;
}
appendGaugeRecordItem(pComboBox,

Loading…
Cancel
Save