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/nmWellParameterPanel.cpp

825 lines
27 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 "nmWellParameterPanel.h"
#include "nmDataAttribute.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmDataPerforation.h"
#include "nmDataWellBase.h"
#include "nmParameterField.h"
#include "nmWellParameterCatalog.h"
#include <QComboBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLayoutItem>
#include <QLineEdit>
#include <QPalette>
#include <QtGlobal>
#include <QVBoxLayout>
#include <QVector>
namespace
{
struct PerforationCandidate
{
QString m_sName;
double m_dStart;
double m_dEnd;
double m_dSkin;
};
}
nmWellParameterPanel::nmWellParameterPanel(
nmIParameterUnitService* pUnitService,
QWidget* pParent)
: QWidget(pParent),
m_pUnitService(pUnitService),
m_pWell(nullptr),
m_pMainLayout(new QVBoxLayout(this)),
m_pPerforationComboBox(nullptr),
m_listFields(),
m_mapFields(),
m_nCurrentPerforationIndex(0),
m_bReloading(false),
m_bUpdatingModel(false)
{
// 参数分组使用透明背景,由面板统一填充主题基础色。
setAutoFillBackground(true);
setBackgroundRole(QPalette::Base);
m_pMainLayout->setContentsMargins(8, 8, 8, 8);
m_pMainLayout->setSpacing(10);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
}
nmWellParameterPanel::~nmWellParameterPanel()
{
// 井对象由会话拥有,析构时只解除连接,不在面板中释放。
if (m_pWell != nullptr)
{
disconnect(m_pWell, nullptr, this, nullptr);
}
m_pUnitService = nullptr;
m_pWell = nullptr;
}
bool nmWellParameterPanel::setWellData(nmDataWellBase* pWell,
QString& sError)
{
if (pWell == nullptr)
{
sError = tr("The well working copy is unavailable.");
return false;
}
if (m_pWell != nullptr)
{
disconnect(m_pWell, nullptr, this, nullptr);
}
// 第一步:井型切换时只重建一次目录分组。
m_pWell = pWell;
m_nCurrentPerforationIndex = 0;
clearGroups();
QList<nmWellParameterGroupInfo> listGroups =
nmWellParameterCatalog::createGroups(m_pWell->getWellType());
for (int nIndex = 0; nIndex < listGroups.size(); ++nIndex)
{
if (!buildGroup(listGroups[nIndex], sError))
{
return false;
}
}
m_pMainLayout->addStretch(1);
// 第二步:数据交互和字段刷新复用现有控件,不重复创建窗口对象。
connect(m_pWell, SIGNAL(sigWellDataChanged()),
this, SLOT(slotWellDataChanged()));
connect(m_pWell, SIGNAL(sigParameterChanged()),
this, SLOT(slotWellDataChanged()));
reloadPerforationSelector();
reloadFromWell();
sError.clear();
return true;
}
bool nmWellParameterPanel::commitPendingEdits(QString& sError)
{
// 第一步:先收集并校验全部字段,任何错误都不能产生部分写入。
QMap<QString, QVariant> mapValues;
QVector<nmDataPerforation> vecPerforations;
if (!collectValues(mapValues, sError, &vecPerforations))
{
emit sigValidityChanged(false);
emit sigValidationMessageChanged(sError);
return false;
}
// 第二步:普通属性直接更新;井筒范围和射孔留给同一次批量提交。
QString sBottomholeMdId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_BOTTOMHOLE_MD);
QString sWellLengthId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_LENGTH);
bool bWellboreSnapshotChanged = false;
m_bUpdatingModel = true;
for (int nIndex = 0; nIndex < m_listFields.size(); ++nIndex)
{
nmParameterField* pField = m_listFields[nIndex];
if (pField == nullptr || pField->isEnum())
{
continue;
}
nmDataAttribute* pAttribute = nmWellParameterCatalog::attribute(
m_pWell,
pField->parameterId(),
m_nCurrentPerforationIndex);
if (pAttribute == nullptr)
{
continue;
}
QVariant oValue = mapValues.value(pField->parameterId());
if (pField->isText())
{
oValue = oValue.toString().trimmed();
}
bool bChanged = pField->isText()
? pAttribute->getValue().toString() != oValue.toString()
: qAbs(pAttribute->getValue().toDouble() - oValue.toDouble()) >
qMax(1.0, qAbs(oValue.toDouble())) * 1.0e-12;
if (bChanged)
{
QString sParameterId = pField->parameterId();
if (sParameterId == sBottomholeMdId ||
sParameterId == sWellLengthId ||
nmWellParameterCatalog::isPerforationParameter(
sParameterId))
{
bWellboreSnapshotChanged = true;
}
else
{
pAttribute->setValue(oValue);
}
}
}
// 第三步:模型跳过井长隐式缩放,原子写入刚刚校验通过的完整快照。
if (bWellboreSnapshotChanged)
{
double dBottomholeMd = mapValues.value(
sBottomholeMdId,
m_pWell->getBottomholeMD().getValue()).toDouble();
double dWellLength = mapValues.value(
sWellLengthId,
m_pWell->getWellLength().getValue()).toDouble();
if (!m_pWell->applyWellboreEdit(
dBottomholeMd, dWellLength, vecPerforations))
{
m_bUpdatingModel = false;
sError = tr("Failed to apply the validated wellbore parameters.");
emit sigValidityChanged(false);
emit sigValidationMessageChanged(sError);
return false;
}
}
m_bUpdatingModel = false;
// 第四步:只更新当前射孔名称,避免提交时重建选择器吞掉切换操作。
updateCurrentPerforationItem();
sError.clear();
emit sigValidityChanged(true);
emit sigValidationMessageChanged(QString());
return true;
}
bool nmWellParameterPanel::isInputValid()
{
QMap<QString, QVariant> mapValues;
QString sError;
return collectValues(mapValues, sError);
}
void nmWellParameterPanel::focusFirstInvalid()
{
// 按界面显示顺序定位错误,使用户无需自行查找红色字段。
for (int nIndex = 0; nIndex < m_listFields.size(); ++nIndex)
{
nmParameterField* pField = m_listFields[nIndex];
if (pField != nullptr && !pField->validate() &&
pField->valueEditor() != nullptr)
{
pField->valueEditor()->setFocus();
pField->valueEditor()->selectAll();
return;
}
}
// 非当前射孔出错时保留未提交输入,仅将焦点移到射孔选择器供用户定位。
if (m_pPerforationComboBox != nullptr &&
!m_pPerforationComboBox->toolTip().isEmpty())
{
m_pPerforationComboBox->setFocus();
}
}
void nmWellParameterPanel::reloadFromWell()
{
if (m_pWell == nullptr || m_bUpdatingModel)
{
return;
}
// 程序回填期间屏蔽编辑槽,避免刷新再次触发模型提交。
m_bReloading = true;
for (int nIndex = 0; nIndex < m_listFields.size(); ++nIndex)
{
nmParameterField* pField = m_listFields[nIndex];
nmDataAttribute* pAttribute = pField != nullptr
? nmWellParameterCatalog::attribute(
m_pWell,
pField->parameterId(),
m_nCurrentPerforationIndex)
: nullptr;
// 目录可复用于多个派生井型,不存在的数据属性直接隐藏。
if (pField != nullptr)
{
pField->setFieldVisible(pAttribute != nullptr);
}
if (pField != nullptr && pAttribute != nullptr)
{
pField->setExternalValidationError(QString());
pField->setBaseValue(pAttribute->getValue());
}
}
m_bReloading = false;
emit sigValidityChanged(isInputValid());
}
void nmWellParameterPanel::slotFieldValueEdited()
{
if (m_bReloading)
{
return;
}
// 输入过程中只更新状态,避免每个按键都重建裂缝或刷新轨迹。
bool bValid = isInputValid();
emit sigValidityChanged(bValid);
if (bValid)
{
emit sigValidationMessageChanged(QString());
}
}
void nmWellParameterPanel::slotFieldEditingFinished()
{
if (m_bReloading)
{
return;
}
// 失去焦点时一次性写工作副本,轨迹只响应最终有效值。
QString sError;
if (!commitPendingEdits(sError))
{
focusFirstInvalid();
}
}
void nmWellParameterPanel::slotPerforationChanged(int nIndex)
{
if (m_bReloading || m_pPerforationComboBox == nullptr || nIndex < 0)
{
return;
}
QVariant oPerforationIndex =
m_pPerforationComboBox->itemData(nIndex);
if (!oPerforationIndex.isValid())
{
return;
}
int nTargetPerforationIndex = oPerforationIndex.toInt();
if (nTargetPerforationIndex == m_nCurrentPerforationIndex)
{
return;
}
// 第一步:离开当前射孔前提交输入,失败则恢复原选择。
int nPreviousPerforationIndex = m_nCurrentPerforationIndex;
QString sError;
if (!commitPendingEdits(sError))
{
int nPreviousComboIndex =
m_pPerforationComboBox->findData(
nPreviousPerforationIndex);
m_bReloading = true;
if (nPreviousComboIndex >= 0)
{
m_pPerforationComboBox->setCurrentIndex(
nPreviousComboIndex);
}
m_bReloading = false;
focusFirstInvalid();
return;
}
// 第二步:切换成功后复用现有字段显示目标射孔数据。
m_nCurrentPerforationIndex = nTargetPerforationIndex;
reloadFromWell();
}
void nmWellParameterPanel::slotWellDataChanged()
{
if (m_bUpdatingModel)
{
return;
}
// 轨迹控制器可能修改射孔集合,选择器和字段必须一起同步。
reloadPerforationSelector();
reloadFromWell();
}
void nmWellParameterPanel::clearGroups()
{
m_listFields.clear();
m_mapFields.clear();
m_pPerforationComboBox = nullptr;
// 字段对象以分组为QObject父对象删除分组即可统一释放字段和控件。
while (m_pMainLayout->count() > 0)
{
QLayoutItem* pItem = m_pMainLayout->takeAt(0);
if (pItem->widget() != nullptr)
{
delete pItem->widget();
}
delete pItem;
}
}
bool nmWellParameterPanel::buildGroup(
const nmWellParameterGroupInfo& oGroupInfo,
QString& sError)
{
// 第一步:建立与通用参数字段一致的标签、值和单位三列布局。
QGroupBox* pGroupBox = new QGroupBox(oGroupInfo.m_sTitle, this);
pGroupBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QGridLayout* pFieldLayout = new QGridLayout(pGroupBox);
pFieldLayout->setContentsMargins(8, 15, 8, 10);
pFieldLayout->setHorizontalSpacing(6);
pFieldLayout->setVerticalSpacing(6);
pFieldLayout->setColumnStretch(0, 0);
pFieldLayout->setColumnStretch(1, 8);
pFieldLayout->setColumnStretch(2, 5);
pFieldLayout->setColumnMinimumWidth(
1, nmParameterField::editorMinimumWidth());
pFieldLayout->setColumnMinimumWidth(
2, nmParameterField::unitMinimumWidth());
// 第二步:射孔选择器只占第二列,不侵入单位列。
int nRowOffset = 0;
if (oGroupInfo.m_bPerforationGroup)
{
QLabel* pSelectorLabel = new QLabel(tr("Perforation"), pGroupBox);
pSelectorLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_pPerforationComboBox = new QComboBox(pGroupBox);
m_pPerforationComboBox->setFixedHeight(
nmParameterField::editorHeight());
pFieldLayout->addWidget(pSelectorLabel, 0, 0);
pFieldLayout->addWidget(m_pPerforationComboBox, 0, 1);
connect(m_pPerforationComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotPerforationChanged(int)));
nRowOffset = 1;
}
// 第三步按目录顺序创建字段并建立稳定ID到字段对象的索引。
for (int nIndex = 0; nIndex < oGroupInfo.m_listFields.size(); ++nIndex)
{
nmParameterField* pField = new nmParameterField(
oGroupInfo.m_listFields[nIndex],
m_pUnitService,
pGroupBox);
if (!pField->initialize(pFieldLayout,
nIndex + nRowOffset,
0,
sError))
{
delete pGroupBox;
return false;
}
pField->setValueRightAligned(!pField->isText());
if (pField->valueEditor() != nullptr)
{
connect(pField->valueEditor(), SIGNAL(textChanged(QString)),
this, SLOT(slotFieldValueEdited()));
connect(pField->valueEditor(), SIGNAL(editingFinished()),
this, SLOT(slotFieldEditingFinished()));
}
m_listFields.append(pField);
m_mapFields.insert(pField->parameterId(), pField);
}
m_pMainLayout->addWidget(pGroupBox);
return true;
}
void nmWellParameterPanel::reloadPerforationSelector()
{
if (m_pPerforationComboBox == nullptr || m_pWell == nullptr)
{
return;
}
// 第一步:增量同步列表,避免清空控件导致焦点切换和点击事件失效。
bool bWasReloading = m_bReloading;
m_bReloading = true;
int nPerforationCount = m_pWell->getPerforationCount();
if (nPerforationCount <= 0)
{
while (m_pPerforationComboBox->count() > 1)
{
m_pPerforationComboBox->removeItem(
m_pPerforationComboBox->count() - 1);
}
if (m_pPerforationComboBox->count() <= 0)
{
m_pPerforationComboBox->addItem(tr("No perforations"));
}
else
{
m_pPerforationComboBox->setItemText(
0, tr("No perforations"));
m_pPerforationComboBox->setItemData(0, QVariant());
}
m_pPerforationComboBox->setEnabled(false);
m_nCurrentPerforationIndex = -1;
m_pPerforationComboBox->setCurrentIndex(0);
}
else
{
while (m_pPerforationComboBox->count() > nPerforationCount)
{
m_pPerforationComboBox->removeItem(
m_pPerforationComboBox->count() - 1);
}
for (int nIndex = 0; nIndex < nPerforationCount; ++nIndex)
{
QString sName = perforationDisplayName(nIndex);
if (nIndex >= m_pPerforationComboBox->count())
{
m_pPerforationComboBox->addItem(sName, nIndex);
}
else
{
if (m_pPerforationComboBox->itemText(nIndex) != sName)
{
m_pPerforationComboBox->setItemText(nIndex, sName);
}
QVariant oItemData =
m_pPerforationComboBox->itemData(nIndex);
if (!oItemData.isValid() || oItemData.toInt() != nIndex)
{
m_pPerforationComboBox->setItemData(nIndex, nIndex);
}
}
}
// 第二步集合变化后约束业务索引再通过itemData定位界面项。
m_pPerforationComboBox->setEnabled(true);
m_nCurrentPerforationIndex = qBound(
0, m_nCurrentPerforationIndex, nPerforationCount - 1);
int nCurrentComboIndex = m_pPerforationComboBox->findData(
m_nCurrentPerforationIndex);
if (nCurrentComboIndex >= 0 &&
m_pPerforationComboBox->currentIndex() !=
nCurrentComboIndex)
{
m_pPerforationComboBox->setCurrentIndex(nCurrentComboIndex);
}
}
m_bReloading = bWasReloading;
}
QString nmWellParameterPanel::perforationDisplayName(
int nPerforationIndex) const
{
nmDataPerforation* pPerforation = m_pWell != nullptr
? m_pWell->getPerforation(nPerforationIndex)
: nullptr;
QString sName = pPerforation != nullptr
? pPerforation->getName().getValue().toString().trimmed()
: QString();
// 旧工程可能没有射孔名称,界面提供稳定的序号回退文本。
return sName.isEmpty()
? tr("Perforation %1").arg(nPerforationIndex + 1)
: sName;
}
void nmWellParameterPanel::updateCurrentPerforationItem()
{
if (m_pPerforationComboBox == nullptr || m_pWell == nullptr ||
m_nCurrentPerforationIndex < 0)
{
return;
}
int nComboIndex = m_pPerforationComboBox->findData(
m_nCurrentPerforationIndex);
if (nComboIndex < 0)
{
return;
}
QString sName = perforationDisplayName(
m_nCurrentPerforationIndex);
if (m_pPerforationComboBox->itemText(nComboIndex) == sName)
{
return;
}
// 只修改文本并屏蔽选择信号,不改变当前索引和弹出列表状态。
bool bWasReloading = m_bReloading;
m_bReloading = true;
m_pPerforationComboBox->setItemText(nComboIndex, sName);
m_bReloading = bWasReloading;
}
bool nmWellParameterPanel::collectValues(
QMap<QString, QVariant>& mapValues,
QString& sError,
QVector<nmDataPerforation>* pVecPerforations)
{
// 第一步:清除上一次跨字段校验留下的错误,重新计算完整状态。
mapValues.clear();
if (m_pPerforationComboBox != nullptr)
{
m_pPerforationComboBox->setToolTip(QString());
}
for (int nIndex = 0; nIndex < m_listFields.size(); ++nIndex)
{
if (m_listFields[nIndex] != nullptr)
{
m_listFields[nIndex]->setExternalValidationError(QString());
}
}
// 第二步:字段统一转换为业务基准单位后写入临时映射。
for (int nIndex = 0; nIndex < m_listFields.size(); ++nIndex)
{
nmParameterField* pField = m_listFields[nIndex];
if (pField == nullptr || !pField->isFieldVisible() ||
pField->isEnum())
{
continue;
}
bool bValid = false;
QVariant oValue = pField->baseValue(bValid);
if (!bValid || !oValue.isValid())
{
sError = tr("Please correct the invalid parameter values.");
return false;
}
mapValues.insert(pField->parameterId(), oValue);
}
// 第三步:单字段合法后再执行依赖多个参数的射孔规则。
if (!validatePerforationValues(
mapValues, sError, pVecPerforations))
{
return false;
}
sError.clear();
return true;
}
bool nmWellParameterPanel::validatePerforationValues(
const QMap<QString, QVariant>& mapValues,
QString& sError,
QVector<nmDataPerforation>* pVecPerforations)
{
if (pVecPerforations != nullptr)
{
pVecPerforations->clear();
}
if (m_pWell == nullptr)
{
return true;
}
// 使用稳定ID读取临时值校验逻辑不依赖界面语言和XML显示名称。
QString sNameId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_PERFORATION_NAME);
QString sStartId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_PERFORATION_MD_START);
QString sEndId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_PERFORATION_MD_END);
QString sSkinId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_PERFORATION_SKIN);
QString sBottomholeMdId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_BOTTOMHOLE_MD);
QString sWellLengthId = nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_LENGTH);
int nPerforationCount = m_pWell->getPerforationCount();
if (nPerforationCount <= 0)
{
if (dynamic_cast<nmDataHorizontalFracturedWell*>(m_pWell) != nullptr)
{
sError = tr(
"A multistage fractured horizontal well requires at least one perforation.");
if (m_pPerforationComboBox != nullptr)
{
m_pPerforationComboBox->setToolTip(sError);
}
return false;
}
return true;
}
// 第一步:输入过程只构造轻量候选值,正式提交时才按需深拷贝完整射孔。
QVector<PerforationCandidate> vecCandidates;
vecCandidates.reserve(nPerforationCount);
if (pVecPerforations != nullptr)
{
pVecPerforations->reserve(nPerforationCount);
}
for (int nIndex = 0; nIndex < nPerforationCount; ++nIndex)
{
nmDataPerforation* pPerforation = m_pWell->getPerforation(nIndex);
if (pPerforation == nullptr)
{
sError = tr("Please correct the invalid parameter values.");
return false;
}
PerforationCandidate oCandidate;
oCandidate.m_sName =
pPerforation->getName().getValue().toString().trimmed();
oCandidate.m_dStart =
pPerforation->getMdStart().getValue().toDouble();
oCandidate.m_dEnd =
pPerforation->getMdEnd().getValue().toDouble();
oCandidate.m_dSkin =
pPerforation->getSkin().getValue().toDouble();
if (nIndex == m_nCurrentPerforationIndex)
{
oCandidate.m_sName = mapValues.value(
sNameId, oCandidate.m_sName).toString().trimmed();
oCandidate.m_dStart = mapValues.value(
sStartId, oCandidate.m_dStart).toDouble();
oCandidate.m_dEnd = mapValues.value(
sEndId, oCandidate.m_dEnd).toDouble();
oCandidate.m_dSkin = mapValues.value(
sSkinId, oCandidate.m_dSkin).toDouble();
}
vecCandidates.append(oCandidate);
if (pVecPerforations != nullptr)
{
nmDataPerforation oPerforationSnapshot(*pPerforation);
oPerforationSnapshot.getName().setValue(oCandidate.m_sName);
oPerforationSnapshot.getMdStart().setValue(oCandidate.m_dStart);
oPerforationSnapshot.getMdEnd().setValue(oCandidate.m_dEnd);
oPerforationSnapshot.getSkin().setValue(oCandidate.m_dSkin);
pVecPerforations->append(oPerforationSnapshot);
}
}
// 第二步逐项检查名称和MD顺序错误文字带序号便于定位非当前射孔。
for (int nIndex = 0; nIndex < vecCandidates.size(); ++nIndex)
{
const PerforationCandidate& oCandidate = vecCandidates[nIndex];
if (oCandidate.m_sName.isEmpty())
{
QString sRuleError = tr("Perforation name cannot be empty.");
sError = perforationErrorText(nIndex, sRuleError);
if (nIndex == m_nCurrentPerforationIndex)
{
nmParameterField* pNameField = field(sNameId);
if (pNameField != nullptr)
{
pNameField->setExternalValidationError(sError);
}
}
else if (m_pPerforationComboBox != nullptr)
{
m_pPerforationComboBox->setToolTip(sError);
}
return false;
}
if (oCandidate.m_dStart > oCandidate.m_dEnd)
{
QString sRuleError = tr(
"Perforation start MD cannot exceed end MD.");
sError = perforationErrorText(nIndex, sRuleError);
setPerforationError(nIndex, sError);
return false;
}
}
// 第三步:以候选井身参数检查全部射孔;水平井从第一段射孔起点延伸。
double dWellTop = mapValues.value(
sBottomholeMdId,
m_pWell->getBottomholeMD().getValue()).toDouble();
double dWellLength = mapValues.value(
sWellLengthId,
m_pWell->getWellLength().getValue()).toDouble();
nmDataHorizontalFracturedWell* pHorizontal =
dynamic_cast<nmDataHorizontalFracturedWell*>(m_pWell);
if (pHorizontal != nullptr && !vecCandidates.isEmpty())
{
dWellTop = vecCandidates[0].m_dStart;
}
double dWellBottom = dWellTop + dWellLength;
double dBoundaryTolerance = qMax(
1.0, qMax(qAbs(dWellTop), qAbs(dWellBottom))) * 1.0e-10;
for (int nIndex = 0; nIndex < vecCandidates.size(); ++nIndex)
{
const PerforationCandidate& oCandidate = vecCandidates[nIndex];
if (oCandidate.m_dStart < dWellTop - dBoundaryTolerance ||
oCandidate.m_dEnd > dWellBottom + dBoundaryTolerance)
{
QString sRuleError = tr(
"The perforation interval is outside the wellbore.");
sError = perforationErrorText(nIndex, sRuleError);
setPerforationError(nIndex, sError);
return false;
}
}
// 第四步:两两检查全部候选区间,端点相接允许但区间不能相交。
for (int nFirstIndex = 0;
nFirstIndex < vecCandidates.size();
++nFirstIndex)
{
for (int nSecondIndex = nFirstIndex + 1;
nSecondIndex < vecCandidates.size();
++nSecondIndex)
{
const PerforationCandidate& oFirst =
vecCandidates[nFirstIndex];
const PerforationCandidate& oSecond =
vecCandidates[nSecondIndex];
if (oFirst.m_dStart < oSecond.m_dEnd - dBoundaryTolerance &&
oFirst.m_dEnd > oSecond.m_dStart + dBoundaryTolerance)
{
int nErrorIndex =
nFirstIndex == m_nCurrentPerforationIndex
? nFirstIndex : nSecondIndex;
QString sRuleError = tr(
"Perforation intervals cannot overlap.");
sError = perforationErrorText(nErrorIndex, sRuleError);
setPerforationError(nErrorIndex, sError);
return false;
}
}
}
return true;
}
nmParameterField* nmWellParameterPanel::field(
const QString& sParameterId) const
{
return m_mapFields.value(sParameterId, nullptr);
}
QString nmWellParameterPanel::perforationErrorText(
int nPerforationIndex,
const QString& sRuleError) const
{
return QString("%1 %2: %3")
.arg(tr("Perforation"))
.arg(nPerforationIndex + 1)
.arg(sRuleError);
}
void nmWellParameterPanel::setPerforationError(
int nPerforationIndex,
const QString& sError)
{
if (m_pPerforationComboBox != nullptr)
{
m_pPerforationComboBox->setToolTip(sError);
}
if (nPerforationIndex != m_nCurrentPerforationIndex)
{
return;
}
nmParameterField* pStartField = field(
nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_PERFORATION_MD_START));
nmParameterField* pEndField = field(
nmWellParameterCatalog::parameterId(
NM_WELL_PARAMETER_PERFORATION_MD_END));
if (pStartField != nullptr)
{
pStartField->setExternalValidationError(sError);
}
if (pEndField != nullptr)
{
pEndField->setExternalValidationError(sError);
}
}