You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWellPressureFlowPage.cpp

1388 lines
44 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmWellPressureFlowPage.h"
#include "iUnitGroup.h"
#include "iUnitHelper.h"
#include "iUnitItem.h"
#include "nmDataWellBase.h"
#include "nmWxPressFlowChartWidget.h"
#include "ZxBaseUtil.h"
#include "ZxTableHeaderViewUnit.h"
#include <QAbstractItemView>
#include <QComboBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QList>
#include <QPalette>
#include <QSplitter>
#include <QStackedWidget>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QStringList>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
namespace
{
QString preferredTimeUnit()
{
return QLatin1String("h");
}
QString preferredFlowUnit()
{
return QString::fromUtf8("m\xC2\xB3/d");
}
iUnitGroup* resolveUnitGroup(const QStringList& listCandidates,
QString& sResolvedUnit)
{
for (int nIndex = 0; nIndex < listCandidates.size(); ++nIndex)
{
const QString& sCandidate = listCandidates[nIndex];
iUnitGroup* pUnitGroup =
iUnitHelper::getUnitGroupByUnit(sCandidate);
if (pUnitGroup == nullptr)
{
continue;
}
int nUnitIndex = pUnitGroup->indexOf(sCandidate);
QStringList listUnitNames = pUnitGroup->getAllUnitNames();
if (nUnitIndex < 0 || nUnitIndex >= listUnitNames.size())
{
continue;
}
sResolvedUnit = listUnitNames[nUnitIndex];
return pUnitGroup;
}
sResolvedUnit = listCandidates.isEmpty()
? QString() : listCandidates.first();
return nullptr;
}
QLabel* createEmptyLabel(const QString& sText, QWidget* pParent)
{
QLabel* pLabel = new QLabel(sText, pParent);
pLabel->setAlignment(Qt::AlignCenter);
QPalette oPalette = pLabel->palette();
oPalette.setColor(QPalette::WindowText,
pParent->palette().color(QPalette::Mid));
pLabel->setPalette(oPalette);
return pLabel;
}
QString displayValueText(double dValue, int nDigit)
{
if (nDigit <= 0)
{
return QString::number(dValue, 'f', 0);
}
return ZxBaseUtil::getValidStr(dValue, nDigit);
}
// 显示相下拉框只列出实际出现过非零流量的相;多相表中的固定全零列
// 仍保留在井记录和求解输入中,但不应显示成该井存在对应相产量。
bool hasRealPhaseData(const QVector<QPointF>& vecPoints)
{
int nStartIndex = 0;
if (!vecPoints.isEmpty() &&
vecPoints.first().x() == 0.0 &&
vecPoints.first().y() == 0.0)
{
nStartIndex = 1;
}
for (int nIndex = nStartIndex; nIndex < vecPoints.size(); ++nIndex)
{
if (vecPoints[nIndex].y() != 0.0)
{
return true;
}
}
return false;
}
// 压力/流量记录下拉框只显示稳定的 Gauge Code避免顶部工具栏过宽。
// 异常旧数据没有 Code 时,才回退到框架名称。
QString formatGaugeRecordLabel(const QString& sGaugeName,
const QString& sGaugeCode,
const QString& sGaugeTime)
{
Q_UNUSED(sGaugeTime);
return sGaugeCode.isEmpty() ? sGaugeName : sGaugeCode;
}
QString gaugeMetadataText(const QString& sText)
{
return sText.trimmed().isEmpty()
? nmWellPressureFlowPage::tr("(none)") : sText;
}
QString gaugeNameText(const QString& sGaugeName)
{
return sGaugeName.trimmed().isEmpty()
? nmWellPressureFlowPage::tr("(unnamed)") : sGaugeName;
}
QString gaugeRecordToolTip(const QString& sGaugeName,
const QString& sGaugeCode,
const QString& sGaugeTime)
{
QStringList listLines;
listLines << nmWellPressureFlowPage::tr("Curve name: %1")
.arg(gaugeNameText(sGaugeName));
listLines << nmWellPressureFlowPage::tr("Gauge Code: %1")
.arg(gaugeMetadataText(sGaugeCode));
listLines << nmWellPressureFlowPage::tr("Measurement time: %1")
.arg(gaugeMetadataText(sGaugeTime));
return listLines.join(QLatin1String("\n"));
}
void disableGaugeComboItem(QComboBox* pCombo, int nItemIndex)
{
QStandardItemModel* pModel =
qobject_cast<QStandardItemModel*>(pCombo->model());
if (pModel == nullptr)
{
return;
}
QStandardItem* pItem = pModel->item(nItemIndex);
if (pItem != nullptr)
{
pItem->setFlags(pItem->flags() & ~Qt::ItemIsEnabled);
}
}
void updatePreviewComboToolTip(QComboBox* pCombo)
{
if (pCombo == nullptr || pCombo->currentIndex() < 0 ||
pCombo->itemData(pCombo->currentIndex()).toString().isEmpty())
{
if (pCombo != nullptr)
{
pCombo->setToolTip(QString());
}
return;
}
QString sToolTip = pCombo->itemData(
pCombo->currentIndex(), Qt::ToolTipRole).toString();
if (!sToolTip.isEmpty())
{
sToolTip += QLatin1Char('\n');
}
sToolTip += nmWellPressureFlowPage::tr(
"Preview only; calculation settings are unchanged.");
pCombo->setToolTip(sToolTip);
}
// 统一向压力/流量记录下拉框追加一项;异常记录保留状态提示但不可选。
void appendGaugeComboItem(QComboBox* pCombo,
const QString& sGaugeName,
const QString& sGaugeCode,
const QString& sGaugeTime,
NM_GAUGE_RECORD_STATUS eStatus)
{
QString sLabel = formatGaugeRecordLabel(sGaugeName, sGaugeCode, sGaugeTime);
switch (eStatus)
{
case NM_GaugeRecord_InvalidIdentity:
sLabel += nmWellPressureFlowPage::tr(" (Invalid)");
break;
case NM_GaugeRecord_Empty:
sLabel += nmWellPressureFlowPage::tr(" (Empty)");
break;
case NM_GaugeRecord_Corrupt:
sLabel += nmWellPressureFlowPage::tr(" (Corrupt)");
break;
default:
break;
}
pCombo->addItem(sLabel, sGaugeCode);
const int nItemIndex = pCombo->count() - 1;
pCombo->setItemData(nItemIndex,
gaugeRecordToolTip(sGaugeName, sGaugeCode, sGaugeTime),
Qt::ToolTipRole);
if (eStatus != NM_GaugeRecord_Usable)
{
disableGaugeComboItem(pCombo, nItemIndex);
}
}
}
nmWellPressureFlowPage::nmWellPressureFlowPage(QWidget* pParent)
: QWidget(pParent),
m_pWell(nullptr),
m_pContentStack(nullptr),
m_pHorizontalSplitter(nullptr),
m_pChartSplitter(nullptr),
m_pPressureChartStack(nullptr),
m_pFlowChartStack(nullptr),
m_pFlowTableStack(nullptr),
m_pPressureChart(nullptr),
m_pFlowChart(nullptr),
m_pFlowTable(nullptr),
m_pFlowHeader(nullptr),
m_pTimeUnitGroup(nullptr),
m_pFlowUnitGroup(nullptr),
m_sTimeBaseUnit(preferredTimeUnit()),
m_sFlowBaseUnit(preferredFlowUnit()),
m_sTimeUnit(preferredTimeUnit()),
m_sFlowUnit(preferredFlowUnit()),
m_nTimeDigit(6),
m_nFlowDigit(6),
m_nSelectedFlowSegment(-1),
m_pRecordSelectionBar(nullptr),
m_pPressureGaugeCombo(nullptr),
m_pFlowGaugeCombo(nullptr),
m_pDisplayPhaseCombo(nullptr),
m_pDisplayPhaseContainer(nullptr),
m_eDisplayPhase(PHASE_UNKNOWN)
{
createUi();
initializeUnitSupport();
resetDataViews();
}
void nmWellPressureFlowPage::setWell(nmDataWellBase* pWell)
{
if (m_pWell == pWell)
{
return;
}
m_pWell = pWell;
m_sPreviewPressureGaugeCode = pWell == nullptr
? QString() : pWell->getSelectedPressureGaugeCode();
m_sPreviewFlowGaugeCode = pWell == nullptr
? QString() : pWell->getSelectedFlowGaugeCode();
resetDataViews();
}
void nmWellPressureFlowPage::refreshData()
{
if (m_pWell == nullptr)
{
resetDataViews();
return;
}
// 第一步:恢复页面自己的预览记录,不修改井对象保存的计算记录选择。
populatePressureGaugeCombo();
populateFlowGaugeCombo();
populateDisplayPhaseCombo(currentFlowRecord());
// 第二步:直接从预览记录复制数据,后续记录、相别和单位切换都不回写井对象。
const nmPressureGaugeRecord* pPressureRecord = currentPressureRecord();
const nmFlowGaugeRecord* pFlowRecord = currentFlowRecord();
QVector<QPointF> vecRawPressurePoints = pPressureRecord == nullptr
? QVector<QPointF>() : pPressureRecord->vecPressurePoints;
QVector<QPointF> vecRawFlowPoints;
if (pFlowRecord != nullptr)
{
switch (m_eDisplayPhase)
{
case PHASE_Oil:
vecRawFlowPoints = pFlowRecord->vecOilPoints;
break;
case PHASE_Gas:
vecRawFlowPoints = pFlowRecord->vecGasPoints;
break;
case PHASE_Water:
vecRawFlowPoints = pFlowRecord->vecWaterPoints;
break;
default:
break;
}
}
// 第三步:单位服务可能晚于页面构造完成,再刷新一次可用单位组。
initializeUnitSupport();
m_vecRawPressurePoints = vecRawPressurePoints;
m_vecRawFlowPoints = vecRawFlowPoints;
QVector<QPointF> vecTableFlowPoints;
int nTimeDigit = m_nTimeDigit;
int nFlowDigit = m_nFlowDigit;
if (!buildFlowTableData(m_sTimeUnit,
m_sFlowUnit,
vecTableFlowPoints,
nTimeDigit,
nFlowDigit))
{
// 当前显示单位不可用时回退基准单位,禁止继续展示旧井快照。
m_sTimeUnit = m_sTimeBaseUnit;
m_sFlowUnit = m_sFlowBaseUnit;
if (!buildFlowTableData(m_sTimeUnit,
m_sFlowUnit,
vecTableFlowPoints,
nTimeDigit,
nFlowDigit))
{
resetDataViews();
updateUnitHeader();
return;
}
}
// 第三步:换算与精度读取全部成功后再提交,曲线始终读取原始数据。
m_vecTableFlowPoints = vecTableFlowPoints;
m_nTimeDigit = nTimeDigit;
m_nFlowDigit = nFlowDigit;
updateUnitHeader();
updateViews();
}
void nmWellPressureFlowPage::clearData()
{
m_pWell = nullptr;
m_sPreviewPressureGaugeCode.clear();
m_sPreviewFlowGaugeCode.clear();
resetDataViews();
}
void nmWellPressureFlowPage::slotUnitChanged(
int nColumn,
const iUnitItem* pSourceUnit,
const iUnitItem* pDestinationUnit)
{
Q_UNUSED(pSourceUnit);
if (pDestinationUnit == nullptr ||
(nColumn != 0 && nColumn != 1))
{
updateUnitHeader();
return;
}
QString sTimeUnit = m_sTimeUnit;
QString sFlowUnit = m_sFlowUnit;
if (nColumn == 0)
{
if (m_pTimeUnitGroup == nullptr)
{
updateUnitHeader();
return;
}
sTimeUnit = pDestinationUnit->m_sName;
}
else
{
if (m_pFlowUnitGroup == nullptr)
{
updateUnitHeader();
return;
}
sFlowUnit = pDestinationUnit->m_sName;
}
// 每次都从基准副本换算,禁止使用当前显示值继续换算。
QVector<QPointF> vecTableFlowPoints;
int nTimeDigit = m_nTimeDigit;
int nFlowDigit = m_nFlowDigit;
if (!buildFlowTableData(sTimeUnit,
sFlowUnit,
vecTableFlowPoints,
nTimeDigit,
nFlowDigit))
{
updateUnitHeader();
return;
}
m_sTimeUnit = sTimeUnit;
m_sFlowUnit = sFlowUnit;
m_vecTableFlowPoints = vecTableFlowPoints;
m_nTimeDigit = nTimeDigit;
m_nFlowDigit = nFlowDigit;
updateUnitHeader();
updateFlowTable();
}
void nmWellPressureFlowPage::slotFlowRowChanged(
int nCurrentRow,
int nCurrentColumn,
int nPreviousRow,
int nPreviousColumn)
{
Q_UNUSED(nCurrentColumn);
Q_UNUSED(nPreviousRow);
Q_UNUSED(nPreviousColumn);
applyFlowSegmentSelection(nCurrentRow);
}
void nmWellPressureFlowPage::slotFlowChartPointClicked(double dTimePoint)
{
// 曲线保持基准单位,点击时间先换算为表格当前单位再定位行。
double dTableTimePoint = 0.0;
if (!convertValue(m_pTimeUnitGroup,
m_sTimeBaseUnit,
m_sTimeUnit,
dTimePoint,
dTableTimePoint))
{
applyFlowSegmentSelection(-1);
return;
}
applyFlowSegmentSelection(
findFlowSegmentByTableTime(dTableTimePoint));
}
void nmWellPressureFlowPage::slotPressureGaugePreviewChanged(int nIndex)
{
if (m_pWell == nullptr || nIndex < 0)
{
return;
}
const QString sGaugeCode =
m_pPressureGaugeCombo->itemData(nIndex).toString();
if (sGaugeCode.isEmpty() ||
sGaugeCode == m_sPreviewPressureGaugeCode)
{
updatePreviewComboToolTip(m_pPressureGaugeCombo);
return;
}
m_sPreviewPressureGaugeCode = sGaugeCode;
refreshData();
}
void nmWellPressureFlowPage::slotFlowGaugePreviewChanged(int nIndex)
{
if (m_pWell == nullptr || nIndex < 0)
{
return;
}
const QString sGaugeCode =
m_pFlowGaugeCombo->itemData(nIndex).toString();
if (sGaugeCode.isEmpty() || sGaugeCode == m_sPreviewFlowGaugeCode)
{
updatePreviewComboToolTip(m_pFlowGaugeCombo);
return;
}
m_sPreviewFlowGaugeCode = sGaugeCode;
m_eDisplayPhase = defaultDisplayPhase(currentFlowRecord());
m_nSelectedFlowSegment = -1;
refreshData();
}
void nmWellPressureFlowPage::slotDisplayPhaseChanged(int nIndex)
{
if (m_pWell == nullptr || nIndex < 0)
{
return;
}
NM_PHASE_TYPE eNewPhase = static_cast<NM_PHASE_TYPE>(
m_pDisplayPhaseCombo->itemData(nIndex).toInt());
if (eNewPhase == m_eDisplayPhase)
{
return;
}
// 仅重新按新相取点刷新视图不改变记录内容、井别、m_nIndexF 或求解器输入。
m_eDisplayPhase = eNewPhase;
refreshData();
}
void nmWellPressureFlowPage::createUi()
{
QVBoxLayout* pMainLayout = new QVBoxLayout(this);
pMainLayout->setContentsMargins(8, 6, 8, 8);
pMainLayout->setSpacing(0);
m_pContentStack = new QStackedWidget(this);
m_pHorizontalSplitter = new QSplitter(
Qt::Horizontal, m_pContentStack);
m_pHorizontalSplitter->setChildrenCollapsible(false);
// 左侧上下两图共享时间范围,但各自保留独立空状态。
m_pChartSplitter = new QSplitter(
Qt::Vertical, m_pHorizontalSplitter);
m_pChartSplitter->setChildrenCollapsible(false);
m_pPressureChartStack = new QStackedWidget(m_pChartSplitter);
m_pPressureChart = new nmWxPressFlowChartWidget(
m_pPressureChartStack);
m_pPressureChart->setReadOnly(true);
m_pPressureChart->setTimeSelectionEnabled(false);
m_pPressureChart->setAxisLabels(tr("Time"), tr("Pressure"));
m_pPressureChart->setAxisUnits(
m_sTimeBaseUnit, tr("MPa"));
m_pPressureChart->setLineColor(QColor(50, 200, 50));
m_pPressureChart->setChartType(CHART_TYPE_LINE);
m_pPressureChartStack->addWidget(m_pPressureChart);
m_pPressureChartStack->addWidget(createEmptyLabel(
tr("No pressure data"), m_pPressureChartStack));
m_pFlowChartStack = new QStackedWidget(m_pChartSplitter);
m_pFlowChart = new nmWxPressFlowChartWidget(m_pFlowChartStack);
m_pFlowChart->setReadOnly(true);
m_pFlowChart->setTimeSelectionEnabled(true);
m_pFlowChart->setAxisLabels(tr("Time"), tr("Flow"));
m_pFlowChart->setAxisUnits(m_sTimeBaseUnit, m_sFlowBaseUnit);
m_pFlowChart->setLineColor(QColor(255, 0, 0));
m_pFlowChart->setChartType(CHART_TYPE_STEP);
m_pFlowChartStack->addWidget(m_pFlowChart);
m_pFlowChartStack->addWidget(createEmptyLabel(
tr("No flow data"), m_pFlowChartStack));
m_pChartSplitter->addWidget(m_pPressureChartStack);
m_pChartSplitter->addWidget(m_pFlowChartStack);
m_pChartSplitter->setStretchFactor(0, 1);
m_pChartSplitter->setStretchFactor(1, 1);
// 右侧仅显示原始流量段对应的时间和流量,不提供任何编辑入口。
m_pFlowTableStack = new QStackedWidget(m_pHorizontalSplitter);
m_pFlowTable = new QTableWidget(m_pFlowTableStack);
m_pFlowTable->setColumnCount(2);
m_pFlowTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_pFlowTable->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pFlowTable->setSelectionMode(QAbstractItemView::SingleSelection);
m_pFlowTable->setSortingEnabled(false);
m_pFlowTable->setDragEnabled(false);
m_pFlowTable->setAcceptDrops(false);
m_pFlowTable->setDropIndicatorShown(false);
m_pFlowTable->setAlternatingRowColors(true);
m_pFlowTable->setStyleSheet(
"QTableWidget {"
" background-color: palette(base);"
" alternate-background-color: rgb(232, 232, 232);"
" gridline-color: palette(midlight);"
" border: 1px solid palette(mid);"
"}"
"QHeaderView {"
" background-color: palette(button);"
" color: palette(button-text);"
"}"
"QHeaderView::section {"
" background-color: palette(button);"
" color: palette(button-text);"
" border: none;"
" border-right: 1px solid palette(mid);"
" border-bottom: 1px solid palette(mid);"
"}"
"QTableCornerButton::section {"
" background-color: palette(base);"
" border: none;"
" border-right: 1px solid palette(mid);"
" border-bottom: 1px solid palette(mid);"
"}");
m_pFlowHeader = new ZxTableHeaderViewUnit(m_pFlowTable);
m_pFlowTable->setHorizontalHeader(m_pFlowHeader);
m_pFlowTable->horizontalHeader()->setMovable(false);
m_pFlowTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
m_pFlowTable->verticalHeader()->setMovable(false);
m_pFlowTable->verticalHeader()->setDefaultAlignment(Qt::AlignCenter);
m_pFlowTable->verticalHeader()->setMinimumWidth(18);
m_pFlowTableStack->addWidget(m_pFlowTable);
m_pFlowTableStack->addWidget(createEmptyLabel(
tr("No flow data"), m_pFlowTableStack));
m_pHorizontalSplitter->addWidget(m_pChartSplitter);
m_pHorizontalSplitter->addWidget(m_pFlowTableStack);
m_pHorizontalSplitter->setStretchFactor(0, 7);
m_pHorizontalSplitter->setStretchFactor(1, 3);
QList<int> listHorizontalSizes;
listHorizontalSizes << 700 << 300;
m_pHorizontalSplitter->setSizes(listHorizontalSizes);
m_pContentStack->addWidget(m_pHorizontalSplitter);
m_pContentStack->addWidget(createEmptyLabel(
tr("No pressure or flow data"), m_pContentStack));
// 顶部插入压力/流量记录与显示相三个下拉框所在行,不改变下方图表/表格结构。
createRecordSelectionBar();
pMainLayout->addWidget(m_pRecordSelectionBar);
pMainLayout->addWidget(m_pContentStack, 1);
connect(m_pFlowHeader,
SIGNAL(sigCbxValueChanged(int,const iUnitItem*,const iUnitItem*)),
this,
SLOT(slotUnitChanged(int,const iUnitItem*,const iUnitItem*)));
connect(m_pFlowTable,
SIGNAL(currentCellChanged(int,int,int,int)),
this,
SLOT(slotFlowRowChanged(int,int,int,int)));
connect(m_pFlowChart,
SIGNAL(chartPointClicked(double)),
this,
SLOT(slotFlowChartPointClicked(double)));
}
void nmWellPressureFlowPage::createRecordSelectionBar()
{
m_pRecordSelectionBar = new QWidget(this);
QHBoxLayout* pBarLayout = new QHBoxLayout(m_pRecordSelectionBar);
pBarLayout->setContentsMargins(0, 0, 0, 6);
pBarLayout->setSpacing(6);
QLabel* pPressureLabel = new QLabel(
tr("Pressure Record"), m_pRecordSelectionBar);
m_pPressureGaugeCombo = new QComboBox(m_pRecordSelectionBar);
m_pPressureGaugeCombo->setEnabled(false);
m_pPressureGaugeCombo->setMinimumWidth(160);
m_pPressureGaugeCombo->setMaxVisibleItems(16);
QLabel* pFlowLabel = new QLabel(
tr("Flow Record"), m_pRecordSelectionBar);
m_pFlowGaugeCombo = new QComboBox(m_pRecordSelectionBar);
m_pFlowGaugeCombo->setEnabled(false);
m_pFlowGaugeCombo->setMinimumWidth(160);
m_pFlowGaugeCombo->setMaxVisibleItems(16);
// 显示相是纯展示状态,整体容器随记录是否为多相含数据而隐藏/显示。
m_pDisplayPhaseContainer = new QWidget(m_pRecordSelectionBar);
QHBoxLayout* pPhaseLayout = new QHBoxLayout(m_pDisplayPhaseContainer);
pPhaseLayout->setContentsMargins(0, 0, 0, 0);
pPhaseLayout->setSpacing(6);
QLabel* pPhaseLabel = new QLabel(
tr("Display Phase"), m_pDisplayPhaseContainer);
m_pDisplayPhaseCombo = new QComboBox(m_pDisplayPhaseContainer);
m_pDisplayPhaseCombo->setMinimumWidth(80);
pPhaseLayout->addWidget(pPhaseLabel);
pPhaseLayout->addWidget(m_pDisplayPhaseCombo);
m_pDisplayPhaseContainer->setVisible(false);
pBarLayout->addWidget(pPressureLabel);
pBarLayout->addWidget(m_pPressureGaugeCombo);
pBarLayout->addSpacing(12);
pBarLayout->addWidget(pFlowLabel);
pBarLayout->addWidget(m_pFlowGaugeCombo);
pBarLayout->addSpacing(12);
pBarLayout->addWidget(m_pDisplayPhaseContainer);
pBarLayout->addStretch(1);
connect(m_pPressureGaugeCombo,
SIGNAL(currentIndexChanged(int)),
this,
SLOT(slotPressureGaugePreviewChanged(int)));
connect(m_pFlowGaugeCombo,
SIGNAL(currentIndexChanged(int)),
this,
SLOT(slotFlowGaugePreviewChanged(int)));
connect(m_pDisplayPhaseCombo,
SIGNAL(currentIndexChanged(int)),
this,
SLOT(slotDisplayPhaseChanged(int)));
}
void nmWellPressureFlowPage::populatePressureGaugeCombo()
{
if (m_pPressureGaugeCombo == nullptr)
{
return;
}
bool bSignalsWereBlocked = m_pPressureGaugeCombo->blockSignals(true);
m_pPressureGaugeCombo->clear();
if (m_pWell == nullptr)
{
m_pPressureGaugeCombo->setEnabled(false);
m_pPressureGaugeCombo->blockSignals(bSignalsWereBlocked);
return;
}
m_vecPressureGaugeRecords = m_pWell->getPressureRecords();
int nPreviewIndex = -1;
int nCalculationIndex = -1;
int nFirstUsableIndex = -1;
const QString sCalculationCode =
m_pWell->getSelectedPressureGaugeCode();
for (int nIndex = 0; nIndex < m_vecPressureGaugeRecords.size(); ++nIndex)
{
const nmPressureGaugeRecord& oRecord =
m_vecPressureGaugeRecords[nIndex];
appendGaugeComboItem(m_pPressureGaugeCombo,
oRecord.sGaugeName, oRecord.sGaugeCode,
oRecord.sGaugeTime, oRecord.eStatus);
const int nItemIndex = m_pPressureGaugeCombo->count() - 1;
if (oRecord.eStatus != NM_GaugeRecord_Usable)
{
continue;
}
if (nFirstUsableIndex < 0)
{
nFirstUsableIndex = nItemIndex;
}
if (oRecord.sGaugeCode == m_sPreviewPressureGaugeCode)
{
nPreviewIndex = nItemIndex;
}
if (oRecord.sGaugeCode == sCalculationCode)
{
nCalculationIndex = nItemIndex;
}
}
const int nCurrentIndex = nPreviewIndex >= 0
? nPreviewIndex : (nCalculationIndex >= 0
? nCalculationIndex : (nFirstUsableIndex >= 0
? nFirstUsableIndex : -1));
m_pPressureGaugeCombo->setCurrentIndex(nCurrentIndex);
m_pPressureGaugeCombo->setEnabled(nFirstUsableIndex >= 0);
m_sPreviewPressureGaugeCode = nCurrentIndex >= 0
? m_pPressureGaugeCombo->itemData(nCurrentIndex).toString()
: QString();
updatePreviewComboToolTip(m_pPressureGaugeCombo);
m_pPressureGaugeCombo->blockSignals(bSignalsWereBlocked);
}
void nmWellPressureFlowPage::populateFlowGaugeCombo()
{
if (m_pFlowGaugeCombo == nullptr)
{
return;
}
bool bSignalsWereBlocked = m_pFlowGaugeCombo->blockSignals(true);
m_pFlowGaugeCombo->clear();
if (m_pWell == nullptr)
{
m_vecFlowGaugeRecords.clear();
m_pFlowGaugeCombo->setEnabled(false);
m_pFlowGaugeCombo->blockSignals(bSignalsWereBlocked);
return;
}
// 缓存整份记录列表供 currentFlowRecord() 安全返回内部指针,记录内容切换记录时不变。
m_vecFlowGaugeRecords = m_pWell->getFlowRecords();
int nPreviewIndex = -1;
int nCalculationIndex = -1;
int nFirstUsableIndex = -1;
const QString sCalculationCode = m_pWell->getSelectedFlowGaugeCode();
for (int nIndex = 0; nIndex < m_vecFlowGaugeRecords.size(); ++nIndex)
{
const nmFlowGaugeRecord& oRecord = m_vecFlowGaugeRecords[nIndex];
appendGaugeComboItem(m_pFlowGaugeCombo,
oRecord.sGaugeName, oRecord.sGaugeCode,
oRecord.sGaugeTime, oRecord.eStatus);
const int nItemIndex = m_pFlowGaugeCombo->count() - 1;
if (oRecord.eStatus != NM_GaugeRecord_Usable)
{
continue;
}
if (nFirstUsableIndex < 0)
{
nFirstUsableIndex = nItemIndex;
}
if (oRecord.sGaugeCode == m_sPreviewFlowGaugeCode)
{
nPreviewIndex = nItemIndex;
}
if (oRecord.sGaugeCode == sCalculationCode)
{
nCalculationIndex = nItemIndex;
}
}
const int nCurrentIndex = nPreviewIndex >= 0
? nPreviewIndex : (nCalculationIndex >= 0
? nCalculationIndex : (nFirstUsableIndex >= 0
? nFirstUsableIndex : -1));
m_pFlowGaugeCombo->setCurrentIndex(nCurrentIndex);
m_pFlowGaugeCombo->setEnabled(nFirstUsableIndex >= 0);
m_sPreviewFlowGaugeCode = nCurrentIndex >= 0
? m_pFlowGaugeCombo->itemData(nCurrentIndex).toString()
: QString();
updatePreviewComboToolTip(m_pFlowGaugeCombo);
m_pFlowGaugeCombo->blockSignals(bSignalsWereBlocked);
}
void nmWellPressureFlowPage::populateDisplayPhaseCombo(
const nmFlowGaugeRecord* pRecord)
{
if (m_pDisplayPhaseCombo == nullptr ||
m_pDisplayPhaseContainer == nullptr)
{
return;
}
bool bSignalsWereBlocked = m_pDisplayPhaseCombo->blockSignals(true);
m_pDisplayPhaseCombo->clear();
if (pRecord == nullptr)
{
m_pDisplayPhaseContainer->setVisible(false);
m_eDisplayPhase = PHASE_UNKNOWN;
m_pDisplayPhaseCombo->blockSignals(bSignalsWereBlocked);
return;
}
// 仅列出记录中真实含数据的相;若记录只有 1 个相有数据,整行隐藏但仍取该相的点。
QVector<NM_PHASE_TYPE> vecAvailablePhases;
QStringList listAvailableLabels;
if (hasRealPhaseData(pRecord->vecOilPoints))
{
vecAvailablePhases.append(PHASE_Oil);
listAvailableLabels.append(tr("Oil"));
}
if (hasRealPhaseData(pRecord->vecGasPoints))
{
vecAvailablePhases.append(PHASE_Gas);
listAvailableLabels.append(tr("Gas"));
}
if (hasRealPhaseData(pRecord->vecWaterPoints))
{
vecAvailablePhases.append(PHASE_Water);
listAvailableLabels.append(tr("Water"));
}
if (vecAvailablePhases.size() <= 1)
{
m_pDisplayPhaseContainer->setVisible(false);
m_eDisplayPhase = vecAvailablePhases.isEmpty()
? PHASE_UNKNOWN : vecAvailablePhases.first();
m_pDisplayPhaseCombo->blockSignals(bSignalsWereBlocked);
return;
}
m_pDisplayPhaseContainer->setVisible(true);
int nCurrentIndex = -1;
for (int nIndex = 0; nIndex < vecAvailablePhases.size(); ++nIndex)
{
m_pDisplayPhaseCombo->addItem(listAvailableLabels[nIndex],
static_cast<int>(vecAvailablePhases[nIndex]));
if (vecAvailablePhases[nIndex] == m_eDisplayPhase)
{
nCurrentIndex = nIndex;
}
}
if (nCurrentIndex < 0)
{
NM_PHASE_TYPE eDefaultPhase = defaultDisplayPhase(pRecord);
for (int nIndex = 0; nIndex < vecAvailablePhases.size(); ++nIndex)
{
if (vecAvailablePhases[nIndex] == eDefaultPhase)
{
nCurrentIndex = nIndex;
break;
}
}
}
if (nCurrentIndex < 0)
{
nCurrentIndex = 0;
}
m_pDisplayPhaseCombo->setCurrentIndex(nCurrentIndex);
m_eDisplayPhase = static_cast<NM_PHASE_TYPE>(
m_pDisplayPhaseCombo->itemData(nCurrentIndex).toInt());
m_pDisplayPhaseCombo->blockSignals(bSignalsWereBlocked);
}
NM_PHASE_TYPE nmWellPressureFlowPage::defaultDisplayPhase(
const nmFlowGaugeRecord* pRecord) const
{
if (pRecord == nullptr)
{
return PHASE_UNKNOWN;
}
// 优先使用井别对应的参考相,前提是该相在本条记录中确有真实数据。
NM_PHASE_TYPE eReferencePhase = (m_pWell != nullptr)
? m_pWell->getReferenceFlowPhase() : PHASE_UNKNOWN;
if (eReferencePhase == PHASE_Oil && hasRealPhaseData(pRecord->vecOilPoints))
{
return PHASE_Oil;
}
if (eReferencePhase == PHASE_Gas && hasRealPhaseData(pRecord->vecGasPoints))
{
return PHASE_Gas;
}
if (eReferencePhase == PHASE_Water && hasRealPhaseData(pRecord->vecWaterPoints))
{
return PHASE_Water;
}
// 参考相无数据时,退回记录中第一个有真实数据的相,固定按油、气、水顺序。
if (hasRealPhaseData(pRecord->vecOilPoints))
{
return PHASE_Oil;
}
if (hasRealPhaseData(pRecord->vecGasPoints))
{
return PHASE_Gas;
}
if (hasRealPhaseData(pRecord->vecWaterPoints))
{
return PHASE_Water;
}
return PHASE_UNKNOWN;
}
const nmPressureGaugeRecord* nmWellPressureFlowPage::currentPressureRecord() const
{
for (int nIndex = 0; nIndex < m_vecPressureGaugeRecords.size(); ++nIndex)
{
if (m_vecPressureGaugeRecords[nIndex].sGaugeCode ==
m_sPreviewPressureGaugeCode)
{
return &m_vecPressureGaugeRecords[nIndex];
}
}
return nullptr;
}
const nmFlowGaugeRecord* nmWellPressureFlowPage::currentFlowRecord() const
{
for (int nIndex = 0; nIndex < m_vecFlowGaugeRecords.size(); ++nIndex)
{
if (m_vecFlowGaugeRecords[nIndex].sGaugeCode ==
m_sPreviewFlowGaugeCode)
{
return &m_vecFlowGaugeRecords[nIndex];
}
}
return nullptr;
}
void nmWellPressureFlowPage::initializeUnitSupport()
{
QStringList listTimeCandidates;
listTimeCandidates << preferredTimeUnit() << QLatin1String("hr");
QStringList listFlowCandidates;
listFlowCandidates << preferredFlowUnit()
<< QLatin1String("m^3/d")
<< QLatin1String("m^3/D");
// 优先使用当前单位包的h、m³/d旧英文单位包自动兼容hr、m^3/d。
m_pTimeUnitGroup = resolveUnitGroup(
listTimeCandidates, m_sTimeBaseUnit);
m_pFlowUnitGroup = resolveUnitGroup(
listFlowCandidates, m_sFlowBaseUnit);
// 单位组缺失或当前单位不再存在时回退基准单位,入口由框架表头禁用。
if (m_pTimeUnitGroup == nullptr ||
m_pTimeUnitGroup->indexOf(m_sTimeUnit) < 0)
{
m_sTimeUnit = m_sTimeBaseUnit;
}
if (m_pFlowUnitGroup == nullptr ||
m_pFlowUnitGroup->indexOf(m_sFlowUnit) < 0)
{
m_sFlowUnit = m_sFlowBaseUnit;
}
updateUnitHeader();
}
void nmWellPressureFlowPage::updateUnitHeader()
{
if (m_pFlowHeader == nullptr)
{
return;
}
QString sFlowTitle = tr("Flow");
switch (m_eDisplayPhase)
{
case PHASE_Oil:
sFlowTitle = tr("Oil phase flow");
break;
case PHASE_Gas:
sFlowTitle = tr("Gas phase flow");
break;
case PHASE_Water:
sFlowTitle = tr("Water phase flow");
break;
default:
break;
}
QStringList listTitles;
listTitles << tr("Time") << sFlowTitle;
QStringList listUnits;
listUnits << m_sTimeUnit << m_sFlowUnit;
m_pFlowHeader->setHeaderData(listTitles, listUnits);
}
bool nmWellPressureFlowPage::buildFlowTableData(
const QString& sTimeUnit,
const QString& sFlowUnit,
QVector<QPointF>& vecFlowPoints,
int& nTimeDigit,
int& nFlowDigit) const
{
vecFlowPoints = m_vecRawFlowPoints;
// 空数据也必须验证目标单位,避免表头切换后留下不可用单位状态。
double dConvertedZero = 0.0;
if (!convertValue(m_pTimeUnitGroup,
m_sTimeBaseUnit,
sTimeUnit,
0.0,
dConvertedZero,
&nTimeDigit) ||
!convertValue(m_pFlowUnitGroup,
m_sFlowBaseUnit,
sFlowUnit,
0.0,
dConvertedZero,
&nFlowDigit))
{
return false;
}
// 表格每次从原始副本换算,禁止使用当前显示值继续换算。
for (int nIndex = 0; nIndex < vecFlowPoints.size(); ++nIndex)
{
double dDuration = 0.0;
double dFlow = 0.0;
if (!convertValue(m_pTimeUnitGroup,
m_sTimeBaseUnit,
sTimeUnit,
m_vecRawFlowPoints[nIndex].x(),
dDuration) ||
!convertValue(m_pFlowUnitGroup,
m_sFlowBaseUnit,
sFlowUnit,
m_vecRawFlowPoints[nIndex].y(),
dFlow))
{
return false;
}
vecFlowPoints[nIndex] = QPointF(dDuration, dFlow);
}
return true;
}
bool nmWellPressureFlowPage::convertValue(
iUnitGroup* pUnitGroup,
const QString& sBaseUnit,
const QString& sDestinationUnit,
double dSourceValue,
double& dDestinationValue,
int* pDestinationDigit) const
{
int nDigit = 6;
if (pUnitGroup != nullptr)
{
iUnitItem* pUnitItem =
pUnitGroup->getUnitItem(sDestinationUnit);
if (pUnitItem != nullptr && pUnitItem->m_nDigit >= 0)
{
nDigit = pUnitItem->m_nDigit;
}
}
if (sDestinationUnit == sBaseUnit)
{
dDestinationValue = dSourceValue;
if (pDestinationDigit != nullptr)
{
*pDestinationDigit = nDigit;
}
return true;
}
if (pUnitGroup == nullptr ||
pUnitGroup->indexOf(sBaseUnit) < 0 ||
pUnitGroup->indexOf(sDestinationUnit) < 0)
{
return false;
}
int nFallbackDigit = nDigit;
bool bConverted = pUnitGroup->convert(sBaseUnit,
dSourceValue,
sDestinationUnit,
dDestinationValue,
nDigit);
if (bConverted && pDestinationDigit != nullptr)
{
*pDestinationDigit = nDigit >= 0
? nDigit : nFallbackDigit;
}
return bConverted;
}
void nmWellPressureFlowPage::updateViews()
{
QVector<QPointF> vecFlowChartPoints = createFlowChartPoints();
m_pPressureChart->setAxisUnits(
m_sTimeBaseUnit, tr("MPa"));
m_pFlowChart->setAxisUnits(m_sTimeBaseUnit, m_sFlowBaseUnit);
m_pPressureChart->setData(m_vecRawPressurePoints);
m_pFlowChart->setData(vecFlowChartPoints);
updateXAxisRange(vecFlowChartPoints);
updateFlowTable();
bool bPressureEmpty = m_vecRawPressurePoints.isEmpty();
bool bFlowEmpty = m_vecRawFlowPoints.isEmpty();
m_pPressureChartStack->setCurrentIndex(bPressureEmpty ? 1 : 0);
m_pFlowChartStack->setCurrentIndex(bFlowEmpty ? 1 : 0);
m_pFlowTableStack->setCurrentIndex(bFlowEmpty ? 1 : 0);
m_pContentStack->setCurrentIndex(
bPressureEmpty && bFlowEmpty ? 1 : 0);
updateSelectedFlowSegment();
}
QVector<QPointF> nmWellPressureFlowPage::createFlowChartPoints() const
{
QVector<QPointF> vecChartPoints;
double dTotalTime = 0.0;
for (int nIndex = 0;
nIndex < m_vecRawFlowPoints.size();
++nIndex)
{
const QPointF& oSegment = m_vecRawFlowPoints[nIndex];
if (nIndex == 0)
{
vecChartPoints.append(QPointF(0.0, oSegment.y()));
}
dTotalTime += oSegment.x();
vecChartPoints.append(QPointF(dTotalTime, oSegment.y()));
if (nIndex < m_vecRawFlowPoints.size() - 1 &&
qAbs(m_vecRawFlowPoints[nIndex + 1].y() -
oSegment.y()) > 1e-6)
{
vecChartPoints.append(QPointF(
dTotalTime,
m_vecRawFlowPoints[nIndex + 1].y()));
}
}
return vecChartPoints;
}
void nmWellPressureFlowPage::updateFlowTable()
{
bool bSignalsWereBlocked = m_pFlowTable->blockSignals(true);
m_pFlowTable->clearContents();
m_pFlowTable->setRowCount(m_vecTableFlowPoints.size());
// 纵向表头显示Qt默认的1N序号内部仍使用零基流量点索引。
for (int nIndex = 0;
nIndex < m_vecTableFlowPoints.size();
++nIndex)
{
QTableWidgetItem* pTimeItem = new QTableWidgetItem(
displayValueText(m_vecTableFlowPoints[nIndex].x(),
m_nTimeDigit));
pTimeItem->setTextAlignment(Qt::AlignCenter);
m_pFlowTable->setItem(nIndex, 0, pTimeItem);
QTableWidgetItem* pFlowItem = new QTableWidgetItem(
displayValueText(m_vecTableFlowPoints[nIndex].y(),
m_nFlowDigit));
pFlowItem->setTextAlignment(Qt::AlignCenter);
m_pFlowTable->setItem(nIndex, 1, pFlowItem);
}
if (m_nSelectedFlowSegment >= 0 &&
m_nSelectedFlowSegment < m_pFlowTable->rowCount())
{
m_pFlowTable->setCurrentCell(m_nSelectedFlowSegment, 0);
m_pFlowTable->selectRow(m_nSelectedFlowSegment);
}
else
{
m_nSelectedFlowSegment = -1;
m_pFlowTable->setCurrentCell(-1, -1);
m_pFlowTable->clearSelection();
}
m_pFlowTable->blockSignals(bSignalsWereBlocked);
}
void nmWellPressureFlowPage::updateXAxisRange(
const QVector<QPointF>& vecFlowChartPoints)
{
bool bHasRange = false;
double dMinimum = 0.0;
double dMaximum = 0.0;
for (int nIndex = 0;
nIndex < m_vecRawPressurePoints.size();
++nIndex)
{
double dTime = m_vecRawPressurePoints[nIndex].x();
dMinimum = bHasRange ? qMin(dMinimum, dTime) : dTime;
dMaximum = bHasRange ? qMax(dMaximum, dTime) : dTime;
bHasRange = true;
}
for (int nIndex = 0; nIndex < vecFlowChartPoints.size(); ++nIndex)
{
double dTime = vecFlowChartPoints[nIndex].x();
dMinimum = bHasRange ? qMin(dMinimum, dTime) : dTime;
dMaximum = bHasRange ? qMax(dMaximum, dTime) : dTime;
bHasRange = true;
}
if (bHasRange)
{
m_pPressureChart->setXAxisRange(dMinimum, dMaximum);
m_pFlowChart->setXAxisRange(dMinimum, dMaximum);
}
else
{
m_pPressureChart->clearXAxisRange();
m_pFlowChart->clearXAxisRange();
}
}
void nmWellPressureFlowPage::updateSelectedFlowSegment()
{
if (m_nSelectedFlowSegment < 0 ||
m_nSelectedFlowSegment >= m_vecRawFlowPoints.size())
{
m_pPressureChart->clearHighlightedTimeRange();
m_pFlowChart->clearHighlightedTimeRange();
return;
}
// 流量点X值是持续时间段区间必须从第0段开始逐段累加。
double dStartTime = 0.0;
for (int nIndex = 0;
nIndex < m_nSelectedFlowSegment;
++nIndex)
{
dStartTime += m_vecRawFlowPoints[nIndex].x();
}
double dEndTime = dStartTime +
m_vecRawFlowPoints[m_nSelectedFlowSegment].x();
m_pPressureChart->setHighlightedTimeRange(
dStartTime, dEndTime);
m_pFlowChart->setHighlightedTimeRange(dStartTime, dEndTime);
}
void nmWellPressureFlowPage::applyFlowSegmentSelection(int nSegmentIndex)
{
if (nSegmentIndex < 0 ||
nSegmentIndex >= m_vecRawFlowPoints.size() ||
nSegmentIndex >= m_pFlowTable->rowCount())
{
nSegmentIndex = -1;
}
m_nSelectedFlowSegment = nSegmentIndex;
bool bSignalsWereBlocked = m_pFlowTable->blockSignals(true);
if (m_nSelectedFlowSegment >= 0)
{
m_pFlowTable->setCurrentCell(m_nSelectedFlowSegment, 0);
m_pFlowTable->selectRow(m_nSelectedFlowSegment);
QTableWidgetItem* pItem = m_pFlowTable->item(
m_nSelectedFlowSegment, 0);
if (pItem != nullptr)
{
m_pFlowTable->scrollToItem(
pItem, QAbstractItemView::EnsureVisible);
}
}
else
{
m_pFlowTable->setCurrentCell(-1, -1);
m_pFlowTable->clearSelection();
}
m_pFlowTable->blockSignals(bSignalsWereBlocked);
updateSelectedFlowSegment();
}
int nmWellPressureFlowPage::findFlowSegmentByTableTime(
double dTimePoint) const
{
double dTotalTime = 0.0;
for (int nIndex = 0;
nIndex < m_vecTableFlowPoints.size();
++nIndex)
{
dTotalTime += qMax(0.0, m_vecTableFlowPoints[nIndex].x());
}
const double dTolerance = qMax(1.0, qAbs(dTotalTime)) * 1e-9;
if (dTimePoint < 0.0 ||
dTimePoint > dTotalTime + dTolerance)
{
return -1;
}
// 零时长点不占区间;分界点继续查找后一有效段。
double dStartTime = 0.0;
int nLastValidSegment = -1;
for (int nIndex = 0;
nIndex < m_vecTableFlowPoints.size();
++nIndex)
{
double dDuration = m_vecTableFlowPoints[nIndex].x();
if (dDuration <= dTolerance)
{
continue;
}
nLastValidSegment = nIndex;
double dEndTime = dStartTime + dDuration;
if (dTimePoint < dEndTime - dTolerance)
{
return nIndex;
}
dStartTime = dEndTime;
}
// 最终结束点没有后一段,归入最后一个有效流量段。
if (nLastValidSegment >= 0 &&
qAbs(dTimePoint - dTotalTime) <= dTolerance)
{
return nLastValidSegment;
}
return -1;
}
void nmWellPressureFlowPage::resetDataViews()
{
m_vecRawPressurePoints.clear();
m_vecRawFlowPoints.clear();
m_vecTableFlowPoints.clear();
m_nSelectedFlowSegment = -1;
if (m_pWell == nullptr)
{
m_sPreviewPressureGaugeCode.clear();
m_sPreviewFlowGaugeCode.clear();
}
// 井绑定切换或清空时先清空并禁用三个记录/相下拉框,等待 refreshData() 重新填充。
if (m_pPressureGaugeCombo != nullptr)
{
bool bSignalsWereBlocked = m_pPressureGaugeCombo->blockSignals(true);
m_pPressureGaugeCombo->clear();
m_pPressureGaugeCombo->setEnabled(false);
m_pPressureGaugeCombo->setToolTip(QString());
m_pPressureGaugeCombo->blockSignals(bSignalsWereBlocked);
}
if (m_pFlowGaugeCombo != nullptr)
{
bool bSignalsWereBlocked = m_pFlowGaugeCombo->blockSignals(true);
m_pFlowGaugeCombo->clear();
m_pFlowGaugeCombo->setEnabled(false);
m_pFlowGaugeCombo->setToolTip(QString());
m_pFlowGaugeCombo->blockSignals(bSignalsWereBlocked);
}
m_vecPressureGaugeRecords.clear();
m_vecFlowGaugeRecords.clear();
if (m_pDisplayPhaseCombo != nullptr)
{
bool bSignalsWereBlocked = m_pDisplayPhaseCombo->blockSignals(true);
m_pDisplayPhaseCombo->clear();
m_pDisplayPhaseCombo->blockSignals(bSignalsWereBlocked);
}
if (m_pDisplayPhaseContainer != nullptr)
{
m_pDisplayPhaseContainer->setVisible(false);
}
m_eDisplayPhase = PHASE_UNKNOWN;
updateViews();
}