feat(nmNum): 数值成果保存完整压力流量副本

- 按井 UUID 保存全部压力和流量 Gauge 记录及记录级流动段索引
- 成果加载只恢复保存副本,不再访问框架或自动回退 Gauge Code
- 严格恢复选中记录、分相开关和井级流动段索引
- 为规范化 Gauge 输入生成固定格式 SHA-1 签名
- 将历史曲线与 Gauge 输入签名原子更新并校验一致性
- 求解快照区分当前、陈旧和未来版本,陈旧快照保持独立可查看
- 增加文件哈希、UUID、路径、枚举、数值和二进制边界校验
- 使用候选数据管理器加载成果,失败时保持当前窗口状态不变
- 数值项目格式升级至 v5,快照格式升级至 v3,不兼容旧成果
feature/nmNum-multi-gauge-record
lh 3 days ago
parent b2a0e6cbaa
commit 29b1840fde

@ -45,6 +45,7 @@ struct nmPebiWellInputSnapshot
QString m_sWellCode; ///< 项目内稳定井编码。
QString m_sWellName; ///< 捕获时井名称。
QString m_sWellInstanceId; ///< 完整场结果使用的稳定井 UUID。
QString m_sGaugeInputSha1; ///< 与井角色无关的规范化 Gauge 输入签名。
QVector<double> m_vecFlowDurations; ///< 去除框架占位行后的公共流动段时长。
QVector<double> m_vecOilRates; ///< 与公共流动段逐项对应的油相流量。
QVector<double> m_vecGasRates; ///< 与公共流动段逐项对应的气相流量。

@ -744,7 +744,7 @@ public:
const nmNumericalWindowPayloadReferences* pPayloadReferences = NULL);
/**
* @brief v3
* @brief v5
* @note JSON
*/
bool saveNmResultV3(
@ -753,7 +753,7 @@ public:
QString* pError = NULL);
/**
* @brief v3 JSON
* @brief v5 JSON
* @note
*/
static bool validateNmResultV3Window(
@ -781,8 +781,7 @@ public:
return bExists;
}
// 加载井数组里对应的压力、流量数据,这个是在井里没有这些数据的情况下调用
/** @brief 按保存井别重新读取工程井压力和流量,任一井映射失败时返回 false。 */
/** @brief 显式刷新框架井压力和流量;成果加载流程不得调用。 */
bool loadWellPreAndFlow();
// 设置当前分析下的VTK网格对象
@ -802,6 +801,8 @@ public:
QVector<QVector<double>>& vvecHistoryData,
QVector<QVector<double>>& vvecLogPreData,
QVector<QVector<double>>& vvecSemiLogPreData);
/** @brief 使用同一份 Gauge 输入原子更新井的三组历史曲线及签名。 */
bool updateWellHistoryData(nmDataWellBase* pWellData);
// 获取许可证路径
void setLicensePath(const QString& licensePath);
@ -834,7 +835,7 @@ public:
private:
/** @brief 按 UUID 给当前实时井统一绑定或解除最后结果快照弱引用。 */
void rebindPebiResultSnapshotToWells();
/** @brief 在候选 Manager 内完整读取并校验一套 v3 成果。 */
/** @brief 在候选 Manager 内完整读取并校验一套 v5 成果。 */
bool loadNmResultV3InPlace(const QString& sLoadAnalDir);
/** @brief 通过交换所有权一次发布已完整校验的候选项目状态。 */
void swapLoadedProjectState(nmDataAnalyzeManager& oLoadedManager);

@ -8,7 +8,7 @@
/**
* @brief
* @note
* @note
*
*/
struct nmPressureGaugeRecord {
@ -27,7 +27,8 @@ struct nmPressureGaugeRecord {
/**
* @brief
* @note GaugeDataEx2 N []
* GaugeData
* GaugeData
*
*/
struct nmFlowGaugeRecord {
QString sGaugeCode; // 对应 ZxDataGaugeF::getCode(),选择持久化键
@ -47,3 +48,41 @@ struct nmFlowGaugeRecord {
{
}
};
/** @brief 与井角色无关、供历史曲线和求解捕获共同使用的规范化 Gauge 输入。 */
struct nmWellGaugeInputData {
QString sWellInstanceId;
QString sWellCode;
NM_WELL_CATEGORY eWellCategory;
QString sPressureGaugeCode;
QVector<QPointF> vecPressurePoints;
QString sFlowGaugeCode;
bool bUseOilRate;
bool bUseGasRate;
bool bUseWaterRate;
NM_PHASE_TYPE eFlowSchedulePhase;
QVector<double> vecFlowDurations;
QVector<double> vecOilRates;
QVector<double> vecGasRates;
QVector<double> vecWaterRates;
int nFlowSectionIndex;
nmWellGaugeInputData()
: eWellCategory(NM_WellCategory_Unknown)
, bUseOilRate(false)
, bUseGasRate(false)
, bUseWaterRate(false)
, eFlowSchedulePhase(PHASE_UNKNOWN)
, nFlowSectionIndex(0)
{
}
bool hasAlignedFlowSchedule() const
{
return eFlowSchedulePhase != PHASE_UNKNOWN &&
!vecFlowDurations.isEmpty() &&
vecOilRates.size() == vecFlowDurations.size() &&
vecGasRates.size() == vecFlowDurations.size() &&
vecWaterRates.size() == vecFlowDurations.size();
}
};

@ -199,6 +199,16 @@ public:
void setUseOilRate(bool bUsed);
void setUseGasRate(bool bUsed);
void setUseWaterRate(bool bUsed);
/** @brief 构造与井角色无关的规范化 Gauge 输入。 */
void buildGaugeInputData(nmWellGaugeInputData& oInput) const;
/** @brief 按固定二进制格式计算指定规范化 Gauge 输入的 SHA-1。 */
static QString calculateGaugeInputSha1(
const nmWellGaugeInputData& oInput);
/** @brief 返回当前井规范化 Gauge 输入的 SHA-1。 */
QString calculateGaugeInputSha1() const;
/** @brief 返回记录自身、不受分相开关影响的真实段数。 */
static int getRawFlowRecordSegmentCount(
const nmFlowGaugeRecord& oRecord);
int getIndexF() const;
void setIndexF(const int newIndex);
@ -229,6 +239,14 @@ public:
QVector<QVector<double>> getHistorySemiLog();
void setHistorySemiLog(QVector<QVector<double>> semiLogData);
/** @brief 原子写入三组历史曲线及其对应的 Gauge 输入签名。 */
void setHistoryData(
const QVector<QVector<double> >& vecPressure,
const QVector<QVector<double> >& vecLogLog,
const QVector<QVector<double> >& vecSemiLog,
const QString& sGaugeInputSha1);
/** @brief 返回生成当前三组历史曲线时冻结的 Gauge 输入签名。 */
QString getHistoryGaugeInputSha1() const;
void setWellType(NM_WELL_MODEL newWellType);
NM_WELL_MODEL getWellType() const;
@ -362,6 +380,7 @@ protected:
QVector<QVector<double>> m_vvecHsyPressure; // 历史压力数据
QVector<QVector<double>> m_vvecHsyLogLog; // 历史双对数数据
QVector<QVector<double>> m_vvecHsySemiLog; // 历史半对数数据
QString m_sHistoryGaugeInputSha1; // 三组历史曲线对应的规范化 Gauge 输入签名
/** @brief 最后结果快照的弱引用,不延长历史结果生命周期。 */
QWeakPointer<const nmPebiResultSnapshot> m_pPebiResultSnapshot;

@ -2,6 +2,7 @@
#define NMNUMERICALRESULTPERSISTENCE_H
#include "nmData_global.h"
#include "nmDataGaugeRecord.h"
#include <QMap>
#include <QString>
@ -12,7 +13,7 @@
class QFile;
class vtkUnstructuredGrid;
/** @brief v3 清单中对一个不可变文件的长度和 SHA-1 引用。 */
/** @brief 数值成果清单中对一个不可变文件的长度和 SHA-1 引用。 */
struct NM_DATA_EXPORT nmNumericalFileReference
{
/** @brief 初始化空文件引用。 */
@ -29,9 +30,11 @@ struct NM_DATA_EXPORT nmNumericalFileReference
bool isValid() const;
};
/** @brief 一口实时井在 v3 成果中的冻结历史曲线。 */
/** @brief 一口实时井在数值成果中的冻结历史曲线。 */
struct NM_DATA_EXPORT nmNumericalWellHistoryData
{
/** @brief 生成三组曲线时使用的 Gauge 输入签名。 */
QString m_sGaugeInputSha1;
/** @brief 压力历史曲线。 */
QVector<QVector<double> > m_vecPressure;
/** @brief 双对数历史曲线。 */
@ -40,6 +43,17 @@ struct NM_DATA_EXPORT nmNumericalWellHistoryData
QVector<QVector<double> > m_vecSemiLog;
};
/** @brief 一口实时井在数值成果中的完整 Gauge 候选记录副本。 */
struct NM_DATA_EXPORT nmNumericalWellGaugeData
{
/** @brief 记录所属实时井的稳定 UUID。 */
QString m_sWellInstanceId;
/** @brief 全部压力候选记录。 */
QVector<nmPressureGaugeRecord> m_vecPressureRecords;
/** @brief 全部流量候选记录。 */
QVector<nmFlowGaugeRecord> m_vecFlowRecords;
};
/** @brief 一个窗口主 JSON 引用的全部外部载荷。 */
struct NM_DATA_EXPORT nmNumericalWindowPayloadReferences
{
@ -49,6 +63,8 @@ struct NM_DATA_EXPORT nmNumericalWindowPayloadReferences
nmNumericalFileReference m_oCurrentGrid;
/** @brief 按实时井 UUID 保存的历史曲线文件引用。 */
QMap<QString, nmNumericalFileReference> m_mapWellHistories;
/** @brief 按实时井 UUID 保存的完整 Gauge 记录文件引用。 */
QMap<QString, nmNumericalFileReference> m_mapWellGaugeRecords;
/** @brief 最后成功结果快照是否存在。 */
bool m_bHasSnapshot;
/** @brief 快照元数据 JSON 文件引用。 */
@ -59,7 +75,7 @@ struct NM_DATA_EXPORT nmNumericalWindowPayloadReferences
};
/**
* @brief v3
* @brief
*
* JSON
*/
@ -99,6 +115,17 @@ public:
nmNumericalWellHistoryData& oHistory,
QString* pError = NULL);
/** @brief 写入一口井的全部压力和流量候选记录。 */
static bool writeWellGaugeData(
const QString& sFilePath,
const nmNumericalWellGaugeData& oGaugeData,
QString* pError = NULL);
/** @brief 严格读取一口井的全部压力和流量候选记录。 */
static bool readWellGaugeData(
const QString& sFilePath,
nmNumericalWellGaugeData& oGaugeData,
QString* pError = NULL);
/** @brief 以二进制 VTU 写出网格并检查 VTK 返回值和文件存在性。 */
static bool writeGrid(vtkUnstructuredGrid* pGrid,
const QString& sFilePath,

@ -76,6 +76,8 @@ struct NM_DATA_EXPORT nmPebiResultWellSnapshot
NM_WELL_CATEGORY m_eWellCategory;
/** @brief 求解时冻结的井角色。 */
NM_CASE_WELL_MODE m_eWellMode;
/** @brief 求解时冻结的规范化 Gauge 输入签名。 */
QString m_sGaugeInputSha1;
/** @brief 求解时冻结的井平面位置。 */
QPointF m_oLocation;
/** @brief 求解时是否存在射孔数据。 */
@ -343,7 +345,7 @@ private:
/** @brief 求解时冻结的求解设置。 */
nmPebiResultSolverSettings m_oSolverSettings;
// 只有 Builder 和后续 v3 序列化器可以在发布前填充私有数据。
// 只有 Builder 和快照序列化器可以在发布前填充私有数据。
friend class nmPebiResultSnapshotBuilder;
friend class nmPebiResultSnapshotRenderAdapter;
friend class nmPebiResultSnapshotSerializer;

@ -21,7 +21,7 @@ public:
/** @brief 销毁尚未被 takeCandidate() 转交的候选。 */
~nmPebiResultSnapshotBuilder();
/** @brief v4 加载时恢复已保存的快照 UUID求解候选可使用自动生成值。 */
/** @brief 加载时恢复已保存的快照 UUID求解候选可使用自动生成值。 */
bool setSnapshotId(const QString& sSnapshotId);
/** @brief 设置候选创建时对应的网格输入版本和求解输入版本。 */
bool setInputRevisions(quint64 nGridInputRevision,

@ -6,11 +6,12 @@
#include <QSharedPointer>
#include <QString>
#include <QStringList>
class nmPebiResultSnapshot;
/**
* @brief v4 Snapshot.json
* @brief Snapshot.json
*
* VTK
* nmPebiResultSnapshotBuilder
@ -31,6 +32,13 @@ public:
const nmNumericalFileReference& oSnapshotJsonReference,
QString* pError = NULL);
/** @brief 返回 Snapshot.json 中三个规范化载荷路径,供窗口级全局查重。 */
static bool getPayloadRelativePaths(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
QStringList& listRelativePaths,
QString* pError = NULL);
/** @brief 完整读取并通过 Builder 返回尚未发布的可写候选。 */
static bool load(
const QString& sWindowDirectory,

@ -190,10 +190,6 @@ public:
void updateWellDataMapping(nmObjPointWell* pGraphic, nmDataWellBase* pNewData) { m_mapWellData[pGraphic] = pNewData; }
// 井相关数据中心委托方法
void calculateWellLogData(nmDataWellBase* pWellData,
QVector<QVector<double>>& pressure,
QVector<QVector<double>>& logLog,
QVector<QVector<double>>& semiLog);
bool hasLayers() const;
nmDataWellBase* getCurrentWellData() const;
void setCurrentWellData(nmDataWellBase* pWellData);

@ -219,6 +219,14 @@ bool captureResultWellMetadata(
return false;
}
const QString sCurrentGaugeInputSha1 =
pWellData->calculateGaugeInputSha1();
if(sCurrentGaugeInputSha1.isEmpty() ||
sCurrentGaugeInputSha1 != oWellInput.m_sGaugeInputSha1 ||
sCurrentGaugeInputSha1 != pWellData->getHistoryGaugeInputSha1()) {
return false;
}
// 正式结果所需井值在主线程一次冻结;后台只读取本结构。
oWellInput.m_sWellInstanceId = pWellData->getWellInstanceId();
oWellInput.m_sWellName = pWellData->getWellName();
@ -1972,6 +1980,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
oWell.m_eWellMode = oWellInput.m_bRateControlled
? NM_CaseWell_RateControlled
: NM_CaseWell_Observation;
oWell.m_sGaugeInputSha1 = oWellInput.m_sGaugeInputSha1;
oWell.m_oLocation = oWellInput.m_oLocation;
oWell.m_bHasPerforation = oWellInput.m_bHasPerforation;
oWell.m_bHasSkin = oWellInput.m_bHasPerforation;

@ -209,64 +209,21 @@ void fillScenePseudoPressureTable(nmDataBinaryTools::NM_PEBI_SCENE& scene,
// 把当前所选流量记录整理成求解器的公共段时长和三相数组。
// 单相记录缺少的相只补同轴零值;多相记录的三相数值不做业务修正。
bool captureWellFlowSchedule(
nmDataWellBase* pWellData,
const nmWellGaugeInputData& oGaugeInput,
nmPebiWellInputSnapshot& oWellInput)
{
oWellInput.m_vecFlowDurations.clear();
oWellInput.m_vecOilRates.clear();
oWellInput.m_vecGasRates.clear();
oWellInput.m_vecWaterRates.clear();
if(pWellData == nullptr) {
if(!oGaugeInput.hasAlignedFlowSchedule()) {
return false;
}
const QVector<QPointF> vecOilPoints =
pWellData->getFlowSegmentPoints(PHASE_Oil);
const QVector<QPointF> vecGasPoints =
pWellData->getFlowSegmentPoints(PHASE_Gas);
const QVector<QPointF> vecWaterPoints =
pWellData->getFlowSegmentPoints(PHASE_Water);
// 公共段时长取自当前至少一个启用相;井别只影响首选顺序,不裁剪相数据。
oWellInput.m_eFlowSchedulePhase =
pWellData->getFlowSchedulePhase();
const QVector<QPointF> vecTimePoints =
pWellData->getFlowSegmentPoints();
if(oWellInput.m_eFlowSchedulePhase == PHASE_UNKNOWN ||
vecTimePoints.isEmpty()) {
return false;
}
const int nSegmentCount = vecTimePoints.size();
// GaugeDataEx2 本身只有一列公共段时长。这里仅防御启用相数组长度异常,
// 未启用相即使保留原始数据,也不会参与本次快照组装。
if((pWellData->getUseOilRate() && !vecOilPoints.isEmpty() &&
vecOilPoints.size() != nSegmentCount) ||
(pWellData->getUseGasRate() && !vecGasPoints.isEmpty() &&
vecGasPoints.size() != nSegmentCount) ||
(pWellData->getUseWaterRate() && !vecWaterPoints.isEmpty() &&
vecWaterPoints.size() != nSegmentCount)) {
return false;
}
oWellInput.m_vecFlowDurations.reserve(nSegmentCount);
oWellInput.m_vecOilRates.reserve(nSegmentCount);
oWellInput.m_vecGasRates.reserve(nSegmentCount);
oWellInput.m_vecWaterRates.reserve(nSegmentCount);
// 求解器要求 qo/qg/qw 与 Rate.t 等长。用户设为“无”的相用同轴零值占位,
// 只关闭该相源汇项,不改变其他相的段数和时间位置。
for(int nIndex = 0; nIndex < nSegmentCount; ++nIndex) {
oWellInput.m_vecFlowDurations.append(vecTimePoints[nIndex].x());
oWellInput.m_vecOilRates.append(
!pWellData->getUseOilRate() || vecOilPoints.isEmpty()
? 0.0 : vecOilPoints[nIndex].y());
oWellInput.m_vecGasRates.append(
!pWellData->getUseGasRate() || vecGasPoints.isEmpty()
? 0.0 : vecGasPoints[nIndex].y());
oWellInput.m_vecWaterRates.append(
!pWellData->getUseWaterRate() || vecWaterPoints.isEmpty()
? 0.0 : vecWaterPoints[nIndex].y());
}
oWellInput.m_eFlowSchedulePhase = oGaugeInput.eFlowSchedulePhase;
oWellInput.m_vecFlowDurations = oGaugeInput.vecFlowDurations;
oWellInput.m_vecOilRates = oGaugeInput.vecOilRates;
oWellInput.m_vecGasRates = oGaugeInput.vecGasRates;
oWellInput.m_vecWaterRates = oGaugeInput.vecWaterRates;
return true;
}
@ -1170,7 +1127,11 @@ bool nmCalculationPebiGrid::appendManualWellInputSnapshot(
oWellInput.m_sWellCode = pWellData->getWellCode();
oWellInput.m_sWellName = pWellData->getWellName();
oWellInput.m_eWellCategory = pWellData->getWellCategory();
oWellInput.m_nFlowSectionIndex = pWellData->getIndexF();
nmWellGaugeInputData oGaugeInput;
pWellData->buildGaugeInputData(oGaugeInput);
oWellInput.m_sGaugeInputSha1 =
nmDataWellBase::calculateGaugeInputSha1(oGaugeInput);
oWellInput.m_nFlowSectionIndex = oGaugeInput.nFlowSectionIndex;
oWellInput.m_oLocation = QPointF(
pWellData->getX().getValue().toDouble(),
pWellData->getY().getValue().toDouble());
@ -1181,9 +1142,10 @@ bool nmCalculationPebiGrid::appendManualWellInputSnapshot(
: 0.0;
oWellInput.m_bRateControlled =
nWellMode == static_cast<int>(NM_CaseWell_RateControlled);
if(!nmIsValidWellCategory(oWellInput.m_eWellCategory) ||
if(oWellInput.m_sGaugeInputSha1.isEmpty() ||
!nmIsValidWellCategory(oWellInput.m_eWellCategory) ||
(oWellInput.m_bRateControlled &&
(!captureWellFlowSchedule(pWellData, oWellInput) ||
(!captureWellFlowSchedule(oGaugeInput, oWellInput) ||
oWellInput.m_nFlowSectionIndex < 1 ||
oWellInput.m_nFlowSectionIndex >
oWellInput.m_vecFlowDurations.size()))) {
@ -1460,7 +1422,11 @@ bool nmCalculationPebiGrid::captureInputSnapshot(
oWellInput.m_bRealWell = true;
oWellInput.m_sWellName = pWellData->getWellName();
oWellInput.m_eWellCategory = pWellData->getWellCategory();
oWellInput.m_nFlowSectionIndex = pWellData->getIndexF();
nmWellGaugeInputData oGaugeInput;
pWellData->buildGaugeInputData(oGaugeInput);
oWellInput.m_sGaugeInputSha1 =
nmDataWellBase::calculateGaugeInputSha1(oGaugeInput);
oWellInput.m_nFlowSectionIndex = oGaugeInput.nFlowSectionIndex;
oWellInput.m_oLocation = QPointF(
pWellData->getX().getValue().toDouble(),
pWellData->getY().getValue().toDouble());
@ -1473,15 +1439,16 @@ bool nmCalculationPebiGrid::captureInputSnapshot(
oWellInput.m_bRateControlled =
mapWellModes.value(oWellRef.m_sWellCode) ==
static_cast<int>(NM_CaseWell_RateControlled);
if(oSnapshot.m_bRequireSolverInput &&
!nmIsValidWellCategory(oWellInput.m_eWellCategory)) {
if(oWellInput.m_sGaugeInputSha1.isEmpty() ||
(oSnapshot.m_bRequireSolverInput &&
!nmIsValidWellCategory(oWellInput.m_eWellCategory))) {
qWarning() << "Solver well has an invalid category:"
<< oWellRef.m_sWellCode;
return false;
}
if(oSnapshot.m_bRequireSolverInput &&
oWellInput.m_bRateControlled &&
(!captureWellFlowSchedule(pWellData, oWellInput) ||
(!captureWellFlowSchedule(oGaugeInput, oWellInput) ||
oWellInput.m_nFlowSectionIndex < 1 ||
oWellInput.m_nFlowSectionIndex >
oWellInput.m_vecFlowDurations.size())) {

File diff suppressed because it is too large Load Diff

@ -9,8 +9,11 @@
#include <QTextStream>
#include <QCoreApplication>
#include <QDateTime>
#include <QCryptographicHash>
#include <QUuid>
#include <string.h>
/**
* @brief Gauge Code Code nullptr
* @note 使 Code
@ -59,6 +62,76 @@ static bool hasNonZeroFlowSegment(const QVector<QPointF>& vecPoints)
return false;
}
static void appendGaugeUInt32(QByteArray& baData, quint32 nValue)
{
for(int nByte = 0; nByte < 4; ++nByte) {
baData.append(static_cast<char>((nValue >> (nByte * 8)) & 0xffu));
}
}
static void appendGaugeUInt64(QByteArray& baData, quint64 nValue)
{
for(int nByte = 0; nByte < 8; ++nByte) {
baData.append(static_cast<char>((nValue >> (nByte * 8)) & 0xffu));
}
}
static bool appendGaugeString(QByteArray& baData, const QString& sValue)
{
const QByteArray baValue = sValue.toUtf8();
if(baValue.size() < 0) {
return false;
}
appendGaugeUInt32(baData, static_cast<quint32>(baValue.size()));
baData.append(baValue);
return true;
}
static void appendGaugeDouble(QByteArray& baData, double dValue)
{
quint64 nBits = 0;
memcpy(&nBits, &dValue, sizeof(double));
appendGaugeUInt64(baData, nBits);
}
static void appendGaugeDoubleVector(
QByteArray& baData,
const QVector<double>& vecValues)
{
appendGaugeUInt32(baData, static_cast<quint32>(vecValues.size()));
for(int nIndex = 0; nIndex < vecValues.size(); ++nIndex) {
appendGaugeDouble(baData, vecValues[nIndex]);
}
}
static void appendGaugePointVector(
QByteArray& baData,
const QVector<QPointF>& vecPoints)
{
appendGaugeUInt32(baData, static_cast<quint32>(vecPoints.size()));
for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) {
appendGaugeDouble(baData, vecPoints[nIndex].x());
appendGaugeDouble(baData, vecPoints[nIndex].y());
}
}
static void buildNormalizedPhaseRates(
const QVector<QPointF>& vecPoints,
bool bUsed,
int nScheduleCount,
QVector<double>& vecRates)
{
vecRates.clear();
if(!bUsed || vecPoints.isEmpty()) {
vecRates.fill(0.0, nScheduleCount);
return;
}
vecRates.reserve(vecPoints.size());
for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) {
vecRates.append(vecPoints[nIndex].y());
}
}
nmDataWellBase::nmDataWellBase()
: m_eWellCategory(NM_WellCategory_Unknown)
, m_bUseOilRate(true)
@ -219,6 +292,7 @@ nmDataWellBase& nmDataWellBase::operator=(const nmDataWellBase& other)
m_vvecHsyPressure = other.m_vvecHsyPressure;
m_vvecHsyLogLog = other.m_vvecHsyLogLog;
m_vvecHsySemiLog = other.m_vvecHsySemiLog;
m_sHistoryGaugeInputSha1 = other.m_sHistoryGaugeInputSha1;
// 默认与当前井身长度一致
m_dLastWellLength = m_wellLength.getValue().toDouble();
@ -248,7 +322,7 @@ rapidjson::Value nmDataWellBase::ToJsonValue(rapidjson::Document::AllocatorType&
// 序列化井类型
wellObject.AddMember("WellType", rapidjson::Value(static_cast<int>(m_eWellType)), allocator);
// 井别决定流量大字段的油、气、水索引v4 项目必须显式保存。
// 井别决定流量大字段的油、气、水索引v5 项目必须显式保存。
wellObject.AddMember("WellCategory",
rapidjson::Value(static_cast<int>(m_eWellCategory)), allocator);
@ -346,7 +420,7 @@ void nmDataWellBase::FromJsonValue(const rapidjson::Value& jsonValue)
m_eWellType = static_cast<NM_WELL_MODEL>(jsonValue["WellType"].GetInt());
}
// v4 不兼容旧项目,缺失或非法井别由 Manager 的严格结构校验直接拒绝。
// v5 不兼容旧项目,缺失或非法井别由 Manager 的严格结构校验直接拒绝。
if(jsonValue.HasMember("WellCategory") &&
jsonValue["WellCategory"].IsInt()) {
m_eWellCategory = static_cast<NM_WELL_CATEGORY>(
@ -465,7 +539,7 @@ void nmDataWellBase::FromJsonValue(const rapidjson::Value& jsonValue)
m_nIndexF = jsonValue["IndexFlow"].GetInt();
}
// 先恢复选择键;记录数据在 Manager 完成 JSON 读取后从框架重新枚举
// 先恢复选择键;记录数据由 Manager 在 JSON 读取后从成果 Gauge 副本恢复
if(jsonValue.HasMember("SelectedPressureGaugeCode") &&
jsonValue["SelectedPressureGaugeCode"].IsString()) {
m_sSelectedPressureGaugeCode = QString::fromUtf8(
@ -1008,6 +1082,104 @@ void nmDataWellBase::setUseWaterRate(bool bUsed)
m_bUseWaterRate = bUsed;
}
void nmDataWellBase::buildGaugeInputData(
nmWellGaugeInputData& oInput) const
{
oInput = nmWellGaugeInputData();
oInput.sWellInstanceId = m_sWellInstanceId;
oInput.sWellCode = m_wellCode;
oInput.eWellCategory = m_eWellCategory;
oInput.sPressureGaugeCode = m_sSelectedPressureGaugeCode;
oInput.vecPressurePoints = getPressurePoints();
oInput.sFlowGaugeCode = m_sSelectedFlowGaugeCode;
oInput.bUseOilRate = m_bUseOilRate;
oInput.bUseGasRate = m_bUseGasRate;
oInput.bUseWaterRate = m_bUseWaterRate;
oInput.eFlowSchedulePhase = getFlowSchedulePhase();
oInput.nFlowSectionIndex = m_nIndexF;
QVector<QPointF> vecSchedulePoints;
if(oInput.eFlowSchedulePhase != PHASE_UNKNOWN) {
vecSchedulePoints = getFlowSegmentPoints(oInput.eFlowSchedulePhase);
}
oInput.vecFlowDurations.reserve(vecSchedulePoints.size());
for(int nIndex = 0; nIndex < vecSchedulePoints.size(); ++nIndex) {
oInput.vecFlowDurations.append(vecSchedulePoints[nIndex].x());
}
buildNormalizedPhaseRates(getFlowSegmentPoints(PHASE_Oil),
m_bUseOilRate, vecSchedulePoints.size(), oInput.vecOilRates);
buildNormalizedPhaseRates(getFlowSegmentPoints(PHASE_Gas),
m_bUseGasRate, vecSchedulePoints.size(), oInput.vecGasRates);
buildNormalizedPhaseRates(getFlowSegmentPoints(PHASE_Water),
m_bUseWaterRate, vecSchedulePoints.size(), oInput.vecWaterRates);
}
QString nmDataWellBase::calculateGaugeInputSha1(
const nmWellGaugeInputData& oInput)
{
QByteArray baData;
appendGaugeUInt32(baData, 1u);
if(!appendGaugeString(baData, oInput.sWellInstanceId) ||
!appendGaugeString(baData, oInput.sWellCode) ||
!appendGaugeString(baData, oInput.sPressureGaugeCode) ||
!appendGaugeString(baData, oInput.sFlowGaugeCode)) {
return QString();
}
appendGaugeUInt32(baData, static_cast<quint32>(oInput.eWellCategory));
appendGaugePointVector(baData, oInput.vecPressurePoints);
baData.append(oInput.bUseOilRate ? '\1' : '\0');
baData.append(oInput.bUseGasRate ? '\1' : '\0');
baData.append(oInput.bUseWaterRate ? '\1' : '\0');
appendGaugeUInt32(baData,
static_cast<quint32>(oInput.eFlowSchedulePhase));
appendGaugeDoubleVector(baData, oInput.vecFlowDurations);
appendGaugeDoubleVector(baData, oInput.vecOilRates);
appendGaugeDoubleVector(baData, oInput.vecGasRates);
appendGaugeDoubleVector(baData, oInput.vecWaterRates);
appendGaugeUInt32(baData,
static_cast<quint32>(oInput.nFlowSectionIndex));
return QString::fromLatin1(QCryptographicHash::hash(
baData, QCryptographicHash::Sha1).toHex());
}
QString nmDataWellBase::calculateGaugeInputSha1() const
{
nmWellGaugeInputData oInput;
buildGaugeInputData(oInput);
return calculateGaugeInputSha1(oInput);
}
int nmDataWellBase::getRawFlowRecordSegmentCount(
const nmFlowGaugeRecord& oRecord)
{
bool bSharedPlaceholder = false;
if(oRecord.bMultiPhase && !oRecord.vecOilPoints.isEmpty() &&
!oRecord.vecGasPoints.isEmpty() &&
!oRecord.vecWaterPoints.isEmpty()) {
bSharedPlaceholder = oRecord.vecOilPoints.first() == QPointF(0.0, 0.0) &&
oRecord.vecGasPoints.first() == QPointF(0.0, 0.0) &&
oRecord.vecWaterPoints.first() == QPointF(0.0, 0.0);
}
const QVector<QPointF> arrPoints[] = {
oRecord.vecOilPoints, oRecord.vecGasPoints, oRecord.vecWaterPoints
};
int nMaximumCount = 0;
for(int nPhase = 0; nPhase < 3; ++nPhase) {
int nCount = arrPoints[nPhase].size();
if(oRecord.bMultiPhase) {
if(bSharedPlaceholder && nCount > 0) {
--nCount;
}
} else if(nCount > 0 &&
arrPoints[nPhase].first() == QPointF(0.0, 0.0)) {
--nCount;
}
nMaximumCount = qMax(nMaximumCount, nCount);
}
return nMaximumCount;
}
QVector<QPointF> nmDataWellBase::getFlowSegmentPoints(
NM_PHASE_TYPE eFlowPhase) const
{
@ -1228,6 +1400,7 @@ QVector<QVector<double>> nmDataWellBase::getHistoryPressure() {
void nmDataWellBase::setHistoryPressure(QVector<QVector<double>> pressureData) {
m_vvecHsyPressure = pressureData;
m_sHistoryGaugeInputSha1.clear();
}
QVector<QVector<double>> nmDataWellBase::getHistoryLogLog() {
@ -1236,6 +1409,7 @@ QVector<QVector<double>> nmDataWellBase::getHistoryLogLog() {
void nmDataWellBase::setHistoryLogLog(QVector<QVector<double>> loglogData) {
m_vvecHsyLogLog = loglogData;
m_sHistoryGaugeInputSha1.clear();
}
QVector<QVector<double>> nmDataWellBase::getHistorySemiLog() {
@ -1244,6 +1418,24 @@ QVector<QVector<double>> nmDataWellBase::getHistorySemiLog() {
void nmDataWellBase::setHistorySemiLog(QVector<QVector<double>> semiLogData) {
m_vvecHsySemiLog = semiLogData;
m_sHistoryGaugeInputSha1.clear();
}
void nmDataWellBase::setHistoryData(
const QVector<QVector<double> >& vecPressure,
const QVector<QVector<double> >& vecLogLog,
const QVector<QVector<double> >& vecSemiLog,
const QString& sGaugeInputSha1)
{
m_vvecHsyPressure = vecPressure;
m_vvecHsyLogLog = vecLogLog;
m_vvecHsySemiLog = vecSemiLog;
m_sHistoryGaugeInputSha1 = sGaugeInputSha1;
}
QString nmDataWellBase::getHistoryGaugeInputSha1() const
{
return m_sHistoryGaugeInputSha1;
}
// 计算结果相关方法

@ -1,14 +1,19 @@
#include "nmNumericalResultPersistence.h"
#include "nmDataWellBase.h"
#include <QByteArray>
#include <QCryptographicHash>
#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QUuid>
#include <QtGlobal>
#include <float.h>
#include <limits.h>
#include <new>
#include <string.h>
#include <vtkNew.h>
#include <vtkUnstructuredGrid.h>
@ -17,12 +22,17 @@
namespace {
const char g_aWellHistoryMagic[8] = { 'N', 'M', 'W', 'H', 'I', 'S', '3', 0 };
const quint32 g_nWellHistoryFormatVersion = 1;
const char g_aWellHistoryMagic[8] = { 'N', 'M', 'W', 'H', 'I', 'S', '5', 0 };
const char g_aWellGaugeMagic[8] = { 'N', 'M', 'W', 'G', 'A', 'U', '5', 0 };
const quint32 g_nWellHistoryFormatVersion = 2;
const quint32 g_nWellGaugeFormatVersion = 1;
const quint32 g_nEndianMarker = 0x01020304u;
const quint32 g_nMaximumCurveSeries = 64u;
const quint64 g_nMaximumCurvePoints = 50000000ull;
const quint32 g_nMaximumGaugeRecordCount = 10000u;
const quint32 g_nMaximumStringBytes = 16u * 1024u * 1024u;
const qint64 g_nStreamChunkBytes = 4 * 1024 * 1024;
const int g_nDoubleChunkCount = 8192;
bool setError(QString* pError, const QString& sError)
{
@ -69,18 +79,192 @@ bool readAll(QFile& oFile, char* pData, quint64 nBytes)
return true;
}
template<typename T>
bool writePod(QFile& oFile, const T& oValue)
bool writeUInt32(QFile& oFile, quint32 nValue)
{
char aBytes[4];
for(int nIndex = 0; nIndex < 4; ++nIndex)
{
aBytes[nIndex] = static_cast<char>((nValue >> (nIndex * 8)) & 0xffu);
}
return writeAll(oFile, aBytes, 4);
}
bool readUInt32(QFile& oFile, quint32& nValue)
{
unsigned char aBytes[4];
if(!readAll(oFile, reinterpret_cast<char*>(aBytes), 4))
{
return false;
}
nValue = 0;
for(int nIndex = 0; nIndex < 4; ++nIndex)
{
nValue |= static_cast<quint32>(aBytes[nIndex]) << (nIndex * 8);
}
return true;
}
bool writeUInt64(QFile& oFile, quint64 nValue)
{
char aBytes[8];
for(int nIndex = 0; nIndex < 8; ++nIndex)
{
aBytes[nIndex] = static_cast<char>((nValue >> (nIndex * 8)) & 0xffu);
}
return writeAll(oFile, aBytes, 8);
}
bool readUInt64(QFile& oFile, quint64& nValue)
{
unsigned char aBytes[8];
if(!readAll(oFile, reinterpret_cast<char*>(aBytes), 8))
{
return false;
}
nValue = 0;
for(int nIndex = 0; nIndex < 8; ++nIndex)
{
nValue |= static_cast<quint64>(aBytes[nIndex]) << (nIndex * 8);
}
return true;
}
bool isFiniteValue(double dValue)
{
#if defined(_MSC_VER)
return _finite(dValue) != 0;
#else
return qIsFinite(dValue);
#endif
}
void appendDouble(QByteArray& baBytes, double dValue)
{
quint64 nBits = 0;
memcpy(&nBits, &dValue, sizeof(nBits));
for(int nIndex = 0; nIndex < 8; ++nIndex)
{
baBytes.append(static_cast<char>((nBits >> (nIndex * 8)) & 0xffu));
}
}
bool readDouble(const char* pBytes, double& dValue)
{
quint64 nBits = 0;
for(int nIndex = 0; nIndex < 8; ++nIndex)
{
nBits |= static_cast<quint64>(
static_cast<unsigned char>(pBytes[nIndex])) << (nIndex * 8);
}
memcpy(&dValue, &nBits, sizeof(dValue));
return isFiniteValue(dValue);
}
bool isSha1(const QString& sSha1)
{
if(sSha1.size() != 40)
{
return false;
}
for(int nIndex = 0; nIndex < sSha1.size(); ++nIndex)
{
const QChar oChar = sSha1[nIndex];
if(!((oChar >= '0' && oChar <= '9') ||
(oChar >= 'a' && oChar <= 'f')))
{
return false;
}
}
return true;
}
bool writeString(QFile& oFile, const QString& sValue)
{
const QByteArray baValue = sValue.toUtf8();
return static_cast<quint64>(baValue.size()) <= g_nMaximumStringBytes &&
writeUInt32(oFile, static_cast<quint32>(baValue.size())) &&
(baValue.isEmpty() || writeAll(oFile, baValue.constData(),
static_cast<quint64>(baValue.size())));
}
bool readString(QFile& oFile, QString& sValue)
{
return writeAll(oFile, reinterpret_cast<const char*>(&oValue),
static_cast<quint64>(sizeof(T)));
quint32 nBytes = 0;
if(!readUInt32(oFile, nBytes) || nBytes > g_nMaximumStringBytes ||
static_cast<quint64>(nBytes) >
static_cast<quint64>(oFile.bytesAvailable()) ||
nBytes > static_cast<quint32>(INT_MAX))
{
return false;
}
QByteArray baValue;
baValue.resize(static_cast<int>(nBytes));
if(nBytes > 0 && !readAll(oFile, baValue.data(), nBytes))
{
return false;
}
sValue = QString::fromUtf8(baValue.constData(), baValue.size());
return sValue.toUtf8() == baValue;
}
template<typename T>
bool readPod(QFile& oFile, T& oValue)
bool writeDoubleVector(QFile& oFile, const QVector<double>& vecValues)
{
return readAll(oFile, reinterpret_cast<char*>(&oValue),
static_cast<quint64>(sizeof(T)));
QByteArray baChunk;
baChunk.reserve(g_nDoubleChunkCount * static_cast<int>(sizeof(double)));
for(int nStart = 0; nStart < vecValues.size();
nStart += g_nDoubleChunkCount)
{
baChunk.clear();
const int nEnd = qMin(nStart + g_nDoubleChunkCount, vecValues.size());
for(int nIndex = nStart; nIndex < nEnd; ++nIndex)
{
if(!isFiniteValue(vecValues[nIndex]))
{
return false;
}
appendDouble(baChunk, vecValues[nIndex]);
}
if(!baChunk.isEmpty() && !writeAll(oFile, baChunk.constData(),
static_cast<quint64>(baChunk.size())))
{
return false;
}
}
return true;
}
bool readDoubleVector(QFile& oFile, QVector<double>& vecValues,
quint64 nPointCount)
{
if(nPointCount > static_cast<quint64>(INT_MAX))
{
return false;
}
vecValues.resize(static_cast<int>(nPointCount));
QByteArray baChunk;
for(quint64 nStart = 0; nStart < nPointCount;
nStart += static_cast<quint64>(g_nDoubleChunkCount))
{
const int nCount = static_cast<int>(qMin(
static_cast<quint64>(g_nDoubleChunkCount),
nPointCount - nStart));
baChunk.resize(nCount * static_cast<int>(sizeof(double)));
if(!readAll(oFile, baChunk.data(),
static_cast<quint64>(baChunk.size())))
{
return false;
}
for(int nOffset = 0; nOffset < nCount; ++nOffset)
{
if(!readDouble(baChunk.constData() +
nOffset * static_cast<int>(sizeof(double)),
vecValues[static_cast<int>(nStart) + nOffset]))
{
return false;
}
}
}
return true;
}
bool safeMultiply(quint64 nLeft, quint64 nRight, quint64& nProduct)
@ -95,8 +279,12 @@ bool safeMultiply(quint64 nLeft, quint64 nRight, quint64& nProduct)
bool writeCurve(QFile& oFile, const QVector<QVector<double> >& vecCurve)
{
if(static_cast<quint32>(vecCurve.size()) > g_nMaximumCurveSeries)
{
return false;
}
const quint32 nSeriesCount = static_cast<quint32>(vecCurve.size());
if(!writePod(oFile, nSeriesCount))
if(!writeUInt32(oFile, nSeriesCount))
{
return false;
}
@ -106,10 +294,9 @@ bool writeCurve(QFile& oFile, const QVector<QVector<double> >& vecCurve)
const quint64 nPointCount = static_cast<quint64>(vecValues.size());
quint64 nBytes = 0;
if(!safeMultiply(nPointCount, sizeof(double), nBytes) ||
!writePod(oFile, nPointCount) ||
(nBytes > 0 && !writeAll(oFile,
reinterpret_cast<const char*>(vecValues.constData()),
nBytes)))
nPointCount > g_nMaximumCurvePoints ||
!writeUInt64(oFile, nPointCount) ||
(nBytes > 0 && !writeDoubleVector(oFile, vecValues)))
{
return false;
}
@ -122,7 +309,8 @@ bool readCurve(QFile& oFile,
quint64& nTotalPointCount)
{
quint32 nSeriesCount = 0;
if(!readPod(oFile, nSeriesCount) || nSeriesCount > g_nMaximumCurveSeries)
if(!readUInt32(oFile, nSeriesCount) ||
nSeriesCount > g_nMaximumCurveSeries)
{
return false;
}
@ -133,7 +321,7 @@ bool readCurve(QFile& oFile,
{
quint64 nPointCount = 0;
quint64 nBytes = 0;
if(!readPod(oFile, nPointCount) ||
if(!readUInt64(oFile, nPointCount) ||
nPointCount > g_nMaximumCurvePoints ||
nTotalPointCount > g_nMaximumCurvePoints - nPointCount ||
!safeMultiply(nPointCount, sizeof(double), nBytes) ||
@ -143,11 +331,8 @@ bool readCurve(QFile& oFile,
return false;
}
nTotalPointCount += nPointCount;
vecLoaded[static_cast<int>(nSeries)].resize(
static_cast<int>(nPointCount));
if(nBytes > 0 && !readAll(oFile,
reinterpret_cast<char*>(
vecLoaded[static_cast<int>(nSeries)].data()), nBytes))
if(nBytes > 0 && !readDoubleVector(oFile,
vecLoaded[static_cast<int>(nSeries)], nPointCount))
{
return false;
}
@ -156,6 +341,183 @@ bool readCurve(QFile& oFile,
return true;
}
bool writePointVector(QFile& oFile, const QVector<QPointF>& vecPoints)
{
const quint64 nPointCount = static_cast<quint64>(vecPoints.size());
if(nPointCount > g_nMaximumCurvePoints ||
!writeUInt64(oFile, nPointCount))
{
return false;
}
QByteArray baChunk;
baChunk.reserve(g_nDoubleChunkCount * 2 * static_cast<int>(sizeof(double)));
for(int nStart = 0; nStart < vecPoints.size(); nStart += g_nDoubleChunkCount)
{
baChunk.clear();
const int nEnd = qMin(nStart + g_nDoubleChunkCount, vecPoints.size());
for(int nIndex = nStart; nIndex < nEnd; ++nIndex)
{
if(!isFiniteValue(vecPoints[nIndex].x()) ||
!isFiniteValue(vecPoints[nIndex].y()))
{
return false;
}
appendDouble(baChunk, vecPoints[nIndex].x());
appendDouble(baChunk, vecPoints[nIndex].y());
}
if(!baChunk.isEmpty() && !writeAll(oFile, baChunk.constData(),
static_cast<quint64>(baChunk.size())))
{
return false;
}
}
return true;
}
bool readPointVector(QFile& oFile, QVector<QPointF>& vecPoints,
quint64& nTotalPointCount)
{
quint64 nPointCount = 0;
quint64 nDoubleCount = 0;
quint64 nBytes = 0;
if(!readUInt64(oFile, nPointCount) ||
nPointCount > g_nMaximumCurvePoints ||
nTotalPointCount > g_nMaximumCurvePoints - nPointCount ||
!safeMultiply(nPointCount, 2, nDoubleCount) ||
!safeMultiply(nDoubleCount, sizeof(double), nBytes) ||
nBytes > static_cast<quint64>(oFile.bytesAvailable()) ||
nPointCount > static_cast<quint64>(INT_MAX))
{
return false;
}
nTotalPointCount += nPointCount;
vecPoints.resize(static_cast<int>(nPointCount));
QByteArray baChunk;
for(quint64 nStart = 0; nStart < nPointCount;
nStart += static_cast<quint64>(g_nDoubleChunkCount))
{
const int nCount = static_cast<int>(qMin(
static_cast<quint64>(g_nDoubleChunkCount),
nPointCount - nStart));
baChunk.resize(nCount * 2 * static_cast<int>(sizeof(double)));
if(!readAll(oFile, baChunk.data(),
static_cast<quint64>(baChunk.size())))
{
return false;
}
for(int nOffset = 0; nOffset < nCount; ++nOffset)
{
double dX = 0.0;
double dY = 0.0;
const char* pPoint = baChunk.constData() +
nOffset * 2 * static_cast<int>(sizeof(double));
if(!readDouble(pPoint, dX) ||
!readDouble(pPoint + sizeof(double), dY))
{
return false;
}
vecPoints[static_cast<int>(nStart) + nOffset] = QPointF(dX, dY);
}
}
return true;
}
bool isGaugeStatusValid(NM_GAUGE_RECORD_STATUS eStatus)
{
return eStatus >= NM_GaugeRecord_Usable &&
eStatus <= NM_GaugeRecord_InvalidIdentity;
}
bool isCanonicalInstanceId(const QString& sInstanceId)
{
const QUuid oUuid(sInstanceId);
return !oUuid.isNull() &&
oUuid.toString().remove('{').remove('}') == sInstanceId;
}
bool validateGaugeData(const nmNumericalWellGaugeData& oGaugeData)
{
if(!isCanonicalInstanceId(oGaugeData.m_sWellInstanceId) ||
oGaugeData.m_vecPressureRecords.size() >
static_cast<int>(g_nMaximumGaugeRecordCount) ||
oGaugeData.m_vecFlowRecords.size() >
static_cast<int>(g_nMaximumGaugeRecordCount))
{
return false;
}
quint64 nTotalPointCount = 0;
for(int nIndex = 0;
nIndex < oGaugeData.m_vecPressureRecords.size(); ++nIndex)
{
const nmPressureGaugeRecord& oRecord =
oGaugeData.m_vecPressureRecords[nIndex];
const QByteArray baCode = oRecord.sGaugeCode.toUtf8();
const QByteArray baName = oRecord.sGaugeName.toUtf8();
const QByteArray baTime = oRecord.sGaugeTime.toUtf8();
if(!isGaugeStatusValid(oRecord.eStatus) ||
static_cast<quint64>(baCode.size()) > g_nMaximumStringBytes ||
static_cast<quint64>(baName.size()) > g_nMaximumStringBytes ||
static_cast<quint64>(baTime.size()) > g_nMaximumStringBytes ||
static_cast<quint64>(oRecord.vecPressurePoints.size()) >
g_nMaximumCurvePoints - nTotalPointCount)
{
return false;
}
nTotalPointCount += static_cast<quint64>(
oRecord.vecPressurePoints.size());
for(int nPoint = 0; nPoint < oRecord.vecPressurePoints.size(); ++nPoint)
{
if(!isFiniteValue(oRecord.vecPressurePoints[nPoint].x()) ||
!isFiniteValue(oRecord.vecPressurePoints[nPoint].y()))
{
return false;
}
}
}
for(int nIndex = 0; nIndex < oGaugeData.m_vecFlowRecords.size(); ++nIndex)
{
const nmFlowGaugeRecord& oRecord = oGaugeData.m_vecFlowRecords[nIndex];
const QByteArray baCode = oRecord.sGaugeCode.toUtf8();
const QByteArray baName = oRecord.sGaugeName.toUtf8();
const QByteArray baTime = oRecord.sGaugeTime.toUtf8();
const QVector<QPointF> arrPoints[] = {
oRecord.vecOilPoints, oRecord.vecGasPoints, oRecord.vecWaterPoints
};
if(!isGaugeStatusValid(oRecord.eStatus) ||
static_cast<quint64>(baCode.size()) > g_nMaximumStringBytes ||
static_cast<quint64>(baName.size()) > g_nMaximumStringBytes ||
static_cast<quint64>(baTime.size()) > g_nMaximumStringBytes)
{
return false;
}
for(int nPhase = 0; nPhase < 3; ++nPhase)
{
if(static_cast<quint64>(arrPoints[nPhase].size()) >
g_nMaximumCurvePoints - nTotalPointCount)
{
return false;
}
nTotalPointCount += static_cast<quint64>(arrPoints[nPhase].size());
for(int nPoint = 0; nPoint < arrPoints[nPhase].size(); ++nPoint)
{
if(!isFiniteValue(arrPoints[nPhase][nPoint].x()) ||
!isFiniteValue(arrPoints[nPhase][nPoint].y()))
{
return false;
}
}
}
const int nSegmentCount =
nmDataWellBase::getRawFlowRecordSegmentCount(oRecord);
if(oRecord.nIndexF < 0 ||
(oRecord.nIndexF > 0 && oRecord.nIndexF > nSegmentCount))
{
return false;
}
}
return true;
}
bool hasExactMagic(const char* pActual, const char* pExpected, int nSize)
{
for(int nIndex = 0; nIndex < nSize; ++nIndex)
@ -204,8 +566,8 @@ nmNumericalWindowPayloadReferences::nmNumericalWindowPayloadReferences()
int nmNumericalResultPersistence::projectVersion()
{
// v4 强制保存井别,旧版本不再按默认油相解释流量字段
return 4;
// v5 保存完整 Gauge 副本及输入签名,不再依赖框架当前记录
return 5;
}
quint64 nmNumericalResultPersistence::maximumBinaryPayloadBytes()
@ -382,6 +744,10 @@ bool nmNumericalResultPersistence::writeWellHistory(
{
pError->clear();
}
if(!isSha1(oHistory.m_sGaugeInputSha1))
{
return setError(pError, "Numerical well history Gauge signature is invalid.");
}
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
@ -389,8 +755,9 @@ bool nmNumericalResultPersistence::writeWellHistory(
}
const bool bWritten = writeAll(oFile, g_aWellHistoryMagic, 8) &&
writePod(oFile, g_nWellHistoryFormatVersion) &&
writePod(oFile, g_nEndianMarker) &&
writeUInt32(oFile, g_nWellHistoryFormatVersion) &&
writeUInt32(oFile, g_nEndianMarker) &&
writeString(oFile, oHistory.m_sGaugeInputSha1) &&
writeCurve(oFile, oHistory.m_vecPressure) &&
writeCurve(oFile, oHistory.m_vecLogLog) &&
writeCurve(oFile, oHistory.m_vecSemiLog) &&
@ -434,10 +801,12 @@ bool nmNumericalResultPersistence::readWellHistory(
nmNumericalWellHistoryData oLoaded;
const bool bRead = readAll(oFile, aMagic, 8) &&
hasExactMagic(aMagic, g_aWellHistoryMagic, 8) &&
readPod(oFile, nFormatVersion) &&
readUInt32(oFile, nFormatVersion) &&
nFormatVersion == g_nWellHistoryFormatVersion &&
readPod(oFile, nEndianMarker) &&
readUInt32(oFile, nEndianMarker) &&
nEndianMarker == g_nEndianMarker &&
readString(oFile, oLoaded.m_sGaugeInputSha1) &&
isSha1(oLoaded.m_sGaugeInputSha1) &&
readCurve(oFile, oLoaded.m_vecPressure, nTotalPointCount) &&
readCurve(oFile, oLoaded.m_vecLogLog, nTotalPointCount) &&
readCurve(oFile, oLoaded.m_vecSemiLog, nTotalPointCount) &&
@ -456,6 +825,192 @@ bool nmNumericalResultPersistence::readWellHistory(
}
}
bool nmNumericalResultPersistence::writeWellGaugeData(
const QString& sFilePath,
const nmNumericalWellGaugeData& oGaugeData,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
if(!validateGaugeData(oGaugeData))
{
return setError(pError, "Numerical well Gauge data are invalid.");
}
try
{
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
return setError(pError, "Cannot create numerical well Gauge file.");
}
bool bWritten = writeAll(oFile, g_aWellGaugeMagic, 8) &&
writeUInt32(oFile, g_nWellGaugeFormatVersion) &&
writeUInt32(oFile, g_nEndianMarker) &&
writeString(oFile, oGaugeData.m_sWellInstanceId) &&
writeUInt32(oFile, static_cast<quint32>(
oGaugeData.m_vecPressureRecords.size()));
for(int nIndex = 0;
bWritten && nIndex < oGaugeData.m_vecPressureRecords.size();
++nIndex)
{
const nmPressureGaugeRecord& oRecord =
oGaugeData.m_vecPressureRecords[nIndex];
bWritten = writeString(oFile, oRecord.sGaugeCode) &&
writeString(oFile, oRecord.sGaugeName) &&
writeString(oFile, oRecord.sGaugeTime) &&
writeUInt32(oFile, static_cast<quint32>(oRecord.eStatus)) &&
writePointVector(oFile, oRecord.vecPressurePoints);
}
bWritten = bWritten && writeUInt32(oFile, static_cast<quint32>(
oGaugeData.m_vecFlowRecords.size()));
for(int nIndex = 0;
bWritten && nIndex < oGaugeData.m_vecFlowRecords.size(); ++nIndex)
{
const nmFlowGaugeRecord& oRecord =
oGaugeData.m_vecFlowRecords[nIndex];
bWritten = writeString(oFile, oRecord.sGaugeCode) &&
writeString(oFile, oRecord.sGaugeName) &&
writeString(oFile, oRecord.sGaugeTime) &&
writeUInt32(oFile, oRecord.bMultiPhase ? 1u : 0u) &&
writeUInt32(oFile, static_cast<quint32>(oRecord.eStatus)) &&
writePointVector(oFile, oRecord.vecOilPoints) &&
writePointVector(oFile, oRecord.vecGasPoints) &&
writePointVector(oFile, oRecord.vecWaterPoints) &&
writeUInt32(oFile, static_cast<quint32>(
static_cast<qint32>(oRecord.nIndexF)));
}
bWritten = bWritten && oFile.flush() && oFile.size() > 0 &&
static_cast<quint64>(oFile.size()) <=
maximumBinaryPayloadBytes();
oFile.close();
if(!bWritten)
{
QFile::remove(sFilePath);
return setError(pError,
"Cannot write complete numerical well Gauge file.");
}
return true;
}
catch(const std::bad_alloc&)
{
QFile::remove(sFilePath);
return setError(pError,
"Not enough memory to save numerical well Gauge data.");
}
}
bool nmNumericalResultPersistence::readWellGaugeData(
const QString& sFilePath,
nmNumericalWellGaugeData& oGaugeData,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
const QFileInfo oInfo(sFilePath);
if(!oInfo.isFile() || oInfo.size() <= 0 ||
static_cast<quint64>(oInfo.size()) > maximumBinaryPayloadBytes())
{
return setError(pError,
"Numerical well Gauge payload is missing or too large.");
}
try
{
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::ReadOnly))
{
return setError(pError, "Cannot open numerical well Gauge file.");
}
char aMagic[8] = { 0 };
quint32 nFormatVersion = 0;
quint32 nEndianMarker = 0;
quint32 nPressureCount = 0;
quint32 nFlowCount = 0;
quint64 nTotalPointCount = 0;
nmNumericalWellGaugeData oLoaded;
bool bRead = readAll(oFile, aMagic, 8) &&
hasExactMagic(aMagic, g_aWellGaugeMagic, 8) &&
readUInt32(oFile, nFormatVersion) &&
nFormatVersion == g_nWellGaugeFormatVersion &&
readUInt32(oFile, nEndianMarker) &&
nEndianMarker == g_nEndianMarker &&
readString(oFile, oLoaded.m_sWellInstanceId) &&
isCanonicalInstanceId(oLoaded.m_sWellInstanceId) &&
readUInt32(oFile, nPressureCount) &&
nPressureCount <= g_nMaximumGaugeRecordCount;
oLoaded.m_vecPressureRecords.reserve(
bRead ? static_cast<int>(nPressureCount) : 0);
for(quint32 nIndex = 0; bRead && nIndex < nPressureCount; ++nIndex)
{
nmPressureGaugeRecord oRecord;
quint32 nStatus = 0;
bRead = readString(oFile, oRecord.sGaugeCode) &&
readString(oFile, oRecord.sGaugeName) &&
readString(oFile, oRecord.sGaugeTime) &&
readUInt32(oFile, nStatus) &&
nStatus <= static_cast<quint32>(
NM_GaugeRecord_InvalidIdentity) &&
readPointVector(oFile, oRecord.vecPressurePoints,
nTotalPointCount);
if(bRead)
{
oRecord.eStatus = static_cast<NM_GAUGE_RECORD_STATUS>(nStatus);
oLoaded.m_vecPressureRecords.append(oRecord);
}
}
bRead = bRead && readUInt32(oFile, nFlowCount) &&
nFlowCount <= g_nMaximumGaugeRecordCount;
oLoaded.m_vecFlowRecords.reserve(
bRead ? static_cast<int>(nFlowCount) : 0);
for(quint32 nIndex = 0; bRead && nIndex < nFlowCount; ++nIndex)
{
nmFlowGaugeRecord oRecord;
quint32 nMultiPhase = 0;
quint32 nStatus = 0;
quint32 nIndexF = 0;
bRead = readString(oFile, oRecord.sGaugeCode) &&
readString(oFile, oRecord.sGaugeName) &&
readString(oFile, oRecord.sGaugeTime) &&
readUInt32(oFile, nMultiPhase) && nMultiPhase <= 1u &&
readUInt32(oFile, nStatus) &&
nStatus <= static_cast<quint32>(
NM_GaugeRecord_InvalidIdentity) &&
readPointVector(oFile, oRecord.vecOilPoints,
nTotalPointCount) &&
readPointVector(oFile, oRecord.vecGasPoints,
nTotalPointCount) &&
readPointVector(oFile, oRecord.vecWaterPoints,
nTotalPointCount) &&
readUInt32(oFile, nIndexF) && nIndexF <= INT_MAX;
if(bRead)
{
oRecord.bMultiPhase = nMultiPhase != 0;
oRecord.eStatus = static_cast<NM_GAUGE_RECORD_STATUS>(nStatus);
oRecord.nIndexF = static_cast<int>(nIndexF);
oLoaded.m_vecFlowRecords.append(oRecord);
}
}
bRead = bRead && oFile.atEnd() && validateGaugeData(oLoaded);
oFile.close();
if(!bRead)
{
return setError(pError, "Numerical well Gauge file is damaged.");
}
oGaugeData = oLoaded;
return true;
}
catch(const std::bad_alloc&)
{
return setError(pError,
"Not enough memory to load numerical well Gauge data.");
}
}
bool nmNumericalResultPersistence::writeGrid(
vtkUnstructuredGrid* pGrid,
const QString& sFilePath,

@ -35,6 +35,24 @@ bool isValidInstanceId(const QString& sInstanceId)
return !sInstanceId.isEmpty() && !QUuid(sInstanceId).isNull();
}
bool isGaugeInputSha1(const QString& sSha1)
{
if(sSha1.size() != 40)
{
return false;
}
for(int nIndex = 0; nIndex < sSha1.size(); ++nIndex)
{
const QChar oChar = sSha1[nIndex];
if(!((oChar >= '0' && oChar <= '9') ||
(oChar >= 'a' && oChar <= 'f')))
{
return false;
}
}
return true;
}
bool isFiniteVector(const QVector<double>& vecValues)
{
for(int nIndex = 0; nIndex < vecValues.size(); ++nIndex)
@ -522,6 +540,7 @@ bool nmPebiResultSnapshotBuilder::takeWell(
qSwap(oTarget.m_sWellInstanceId, oWell.m_sWellInstanceId);
qSwap(oTarget.m_sWellCode, oWell.m_sWellCode);
qSwap(oTarget.m_sWellName, oWell.m_sWellName);
qSwap(oTarget.m_sGaugeInputSha1, oWell.m_sGaugeInputSha1);
oTarget.m_eWellType = oWell.m_eWellType;
oTarget.m_eWellCategory = oWell.m_eWellCategory;
oTarget.m_eWellMode = oWell.m_eWellMode;
@ -721,6 +740,7 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
m_pCandidate->m_vecWells[nIndex];
if(!isValidInstanceId(oWell.m_sWellInstanceId) ||
oWell.m_sWellCode.isEmpty() || oWell.m_sWellName.isEmpty() ||
!isGaugeInputSha1(oWell.m_sGaugeInputSha1) ||
oWell.m_eWellType == Unknow_Well ||
!nmIsValidWellCategory(oWell.m_eWellCategory) ||
(oWell.m_eWellMode != NM_CaseWell_RateControlled &&

@ -171,13 +171,18 @@ bool readFileReference(const rapidjson::Value& oParent,
const rapidjson::Value& oJson = oParent[pName];
if(!oJson.HasMember("Path") || !oJson["Path"].IsString() ||
!oJson.HasMember("Length") || !oJson["Length"].IsUint64() ||
!oJson.HasMember("Sha1") || !oJson["Sha1"].IsString())
!oJson.HasMember("Sha1") || !oJson["Sha1"].IsString() ||
oJson["Sha1"].GetStringLength() != 40u)
{
return false;
}
oReference.m_sRelativePath = QString::fromUtf8(oJson["Path"].GetString());
oReference.m_sRelativePath = QString::fromUtf8(
oJson["Path"].GetString(),
static_cast<int>(oJson["Path"].GetStringLength()));
oReference.m_nLength = oJson["Length"].GetUint64();
oReference.m_sSha1 = QString::fromLatin1(oJson["Sha1"].GetString());
oReference.m_sSha1 = QString::fromLatin1(
oJson["Sha1"].GetString(),
static_cast<int>(oJson["Sha1"].GetStringLength()));
return oReference.isValid();
}
@ -528,7 +533,7 @@ bool parseSnapshotJson(
if(!oDocument.IsObject() ||
!oDocument.HasMember("SnapshotFormatVersion") ||
!oDocument["SnapshotFormatVersion"].IsInt() ||
oDocument["SnapshotFormatVersion"].GetInt() != 2 ||
oDocument["SnapshotFormatVersion"].GetInt() != 3 ||
!oDocument.HasMember("ResultCellCount") ||
!oDocument["ResultCellCount"].IsUint64() ||
!oDocument.HasMember("PressureFrameCount") ||
@ -547,9 +552,12 @@ bool parseSnapshotJson(
if(nCellCount == 0 || nCellCount > g_nMaximumCellCount ||
nFrameCount == 0 || nFrameCount > g_nMaximumFrameCount ||
nWellCount > g_nMaximumWellCount ||
oGridReference.m_sRelativePath == oPressureReference.m_sRelativePath ||
oGridReference.m_sRelativePath == oCurvesReference.m_sRelativePath ||
oPressureReference.m_sRelativePath == oCurvesReference.m_sRelativePath)
oGridReference.m_sRelativePath.compare(
oPressureReference.m_sRelativePath, Qt::CaseInsensitive) == 0 ||
oGridReference.m_sRelativePath.compare(
oCurvesReference.m_sRelativePath, Qt::CaseInsensitive) == 0 ||
oPressureReference.m_sRelativePath.compare(
oCurvesReference.m_sRelativePath, Qt::CaseInsensitive) == 0)
{
return setError(pError, "Snapshot.json declares invalid or duplicate payloads.");
}
@ -692,8 +700,8 @@ bool nmPebiResultSnapshotSerializer::save(
oDocument.SetObject();
rapidjson::Document::AllocatorType& oAllocator =
oDocument.GetAllocator();
// v2 增加井别元数据;压力帧和井曲线二进制格式保持不变
oDocument.AddMember("SnapshotFormatVersion", 2, oAllocator);
// v3 强制保存每口结果井的 Gauge 输入签名
oDocument.AddMember("SnapshotFormatVersion", 3, oAllocator);
oDocument.AddMember("SnapshotId",
toJsonString(pSnapshot->m_sSnapshotId, oAllocator), oAllocator);
oDocument.AddMember("GridInputRevision",
@ -748,6 +756,8 @@ bool nmPebiResultSnapshotSerializer::save(
oJson.AddMember("WellCategory", static_cast<int>(
oWell.m_eWellCategory), oAllocator);
oJson.AddMember("WellMode", static_cast<int>(oWell.m_eWellMode), oAllocator);
oJson.AddMember("GaugeInputSha1", toJsonString(
oWell.m_sGaugeInputSha1, oAllocator), oAllocator);
oJson.AddMember("X", oWell.m_oLocation.x(), oAllocator);
oJson.AddMember("Y", oWell.m_oLocation.y(), oAllocator);
oJson.AddMember("HasPerforation", oWell.m_bHasPerforation, oAllocator);
@ -890,6 +900,38 @@ bool nmPebiResultSnapshotSerializer::validate(
return true;
}
bool nmPebiResultSnapshotSerializer::getPayloadRelativePaths(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
QStringList& listRelativePaths,
QString* pError)
{
listRelativePaths.clear();
QString sJsonPath;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oSnapshotJsonReference, &sJsonPath, pError))
{
return false;
}
rapidjson::Document oDocument;
nmNumericalFileReference oGridReference;
nmNumericalFileReference oPressureReference;
nmNumericalFileReference oCurvesReference;
quint64 nCellCount = 0;
quint32 nFrameCount = 0;
quint32 nWellCount = 0;
if(!parseSnapshotJson(sJsonPath, oDocument, oGridReference,
oPressureReference, oCurvesReference, nCellCount,
nFrameCount, nWellCount, pError))
{
return false;
}
listRelativePaths << oGridReference.m_sRelativePath
<< oPressureReference.m_sRelativePath
<< oCurvesReference.m_sRelativePath;
return true;
}
bool nmPebiResultSnapshotSerializer::load(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
@ -1027,6 +1069,9 @@ bool nmPebiResultSnapshotSerializer::load(
!oJson["WellInstanceId"].IsString() ||
!oJson.HasMember("WellCode") || !oJson["WellCode"].IsString() ||
!oJson.HasMember("WellName") || !oJson["WellName"].IsString() ||
!oJson.HasMember("GaugeInputSha1") ||
!oJson["GaugeInputSha1"].IsString() ||
oJson["GaugeInputSha1"].GetStringLength() != 40u ||
!readRequiredInt(oJson, "WellType", nWellType) ||
!readRequiredInt(oJson, "WellCategory", nWellCategory) ||
!readRequiredInt(oJson, "WellMode", nWellMode) ||
@ -1046,6 +1091,10 @@ bool nmPebiResultSnapshotSerializer::load(
oJson["WellInstanceId"].GetString());
oWell.m_sWellCode = QString::fromUtf8(oJson["WellCode"].GetString());
oWell.m_sWellName = QString::fromUtf8(oJson["WellName"].GetString());
oWell.m_sGaugeInputSha1 = QString::fromLatin1(
oJson["GaugeInputSha1"].GetString(),
static_cast<int>(
oJson["GaugeInputSha1"].GetStringLength()));
oWell.m_eWellType = static_cast<NM_WELL_MODEL>(nWellType);
oWell.m_eWellCategory = static_cast<NM_WELL_CATEGORY>(
nWellCategory);

@ -265,11 +265,6 @@ void nmGuiPlot::initDefultGeoObj()
return;
}
// 历史数据
QVector<QVector<double>> vvecHistoryPressureData;
QVector<QVector<double>> vvecHistoryLogData;
QVector<QVector<double>> vvecHistorySemiLogData;
if(ZxBaseUtil::isSameStr(wellClass, "VerticalWell")) {
// 初始化直井默认参数
nmDataWellBase* pWell = pDataManager->createWell(NM_WELL_MODEL::Vertical_Well);
@ -297,13 +292,7 @@ void nmGuiPlot::initDefultGeoObj()
nmDataAnalyzeManager::applyGaugeRecordsToWell(
m_VerticalWell, vecPressureRecords, vecFlowRecords);
// TODO: 计算井的历史双对数/半对数数据
pDataManager->calculationLogData(m_VerticalWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 存储历史数据到井对象
m_VerticalWell->setHistoryPressure(vvecHistoryPressureData);
m_VerticalWell->setHistoryLogLog(vvecHistoryLogData);
m_VerticalWell->setHistorySemiLog(vvecHistorySemiLogData);
pDataManager->updateWellHistoryData(m_VerticalWell);
// 设置为当前查看的井
pDataManager->setCurWellData(m_VerticalWell);
@ -335,13 +324,7 @@ void nmGuiPlot::initDefultGeoObj()
nmDataAnalyzeManager::applyGaugeRecordsToWell(
m_VFracturedWell, vecPressureRecords, vecFlowRecords);
// TODO: 计算井的历史双对数/半对数数据
pDataManager->calculationLogData(m_VFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 存储历史数据到井对象
m_VFracturedWell->setHistoryPressure(vvecHistoryPressureData);
m_VFracturedWell->setHistoryLogLog(vvecHistoryLogData);
m_VFracturedWell->setHistorySemiLog(vvecHistorySemiLogData);
pDataManager->updateWellHistoryData(m_VFracturedWell);
// 更新裂缝位置信息
m_VFracturedWell->setFracs();
@ -376,13 +359,7 @@ void nmGuiPlot::initDefultGeoObj()
nmDataAnalyzeManager::applyGaugeRecordsToWell(
m_HFracturedWell, vecPressureRecords, vecFlowRecords);
// TODO: 计算井的历史双对数/半对数数据
pDataManager->calculationLogData(m_HFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 存储历史数据到井对象
m_HFracturedWell->setHistoryPressure(vvecHistoryPressureData);
m_HFracturedWell->setHistoryLogLog(vvecHistoryLogData);
m_HFracturedWell->setHistorySemiLog(vvecHistorySemiLogData);
pDataManager->updateWellHistoryData(m_HFracturedWell);
// 计算裂缝数据
m_HFracturedWell->setFracs();

@ -1796,14 +1796,10 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic,
}
}
QVector<QVector<double>> vvecHistoryPressureData;
QVector<QVector<double>> vvecHistoryLogData;
QVector<QVector<double>> vvecHistorySemiLogData;
// 初始化历史压力、双对数和半对数数据,保证重新显示后可直接分析。
calculateWellLogData(pData, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
pData->setHistoryPressure(vvecHistoryPressureData);
pData->setHistoryLogLog(vvecHistoryLogData);
pData->setHistorySemiLog(vvecHistorySemiLogData);
if(m_pDataMgr) {
m_pDataMgr->updateWellHistoryData(pData);
}
// 通知 PEBI 面板新增井分组
if (m_pDataMgr) {
@ -1933,15 +1929,6 @@ void nmPlotGraphicBinder::onGraphicWellVisibleChanged(bool bVisible)
pData->setPlotVisible(bVisible);
}
void nmPlotGraphicBinder::calculateWellLogData(nmDataWellBase* pWellData,
QVector<QVector<double>>& pressure,
QVector<QVector<double>>& logLog,
QVector<QVector<double>>& semiLog)
{
if (!m_pDataMgr || !pWellData) return;
m_pDataMgr->calculationLogData(pWellData, pressure, logLog, semiLog);
}
bool nmPlotGraphicBinder::hasLayers() const
{
if (!m_pDataMgr) return false;

@ -387,9 +387,10 @@ bool nmWellEditorSession::copySharedWellData(
pTarget->setUseGasRate(pSource->getUseGasRate());
pTarget->setUseWaterRate(pSource->getUseWaterRate());
pTarget->setIndexF(pSource->getIndexF());
pTarget->setHistoryPressure(pSource->getHistoryPressure());
pTarget->setHistoryLogLog(pSource->getHistoryLogLog());
pTarget->setHistorySemiLog(pSource->getHistorySemiLog());
pTarget->setHistoryData(pSource->getHistoryPressure(),
pSource->getHistoryLogLog(),
pSource->getHistorySemiLog(),
pSource->getHistoryGaugeInputSha1());
// 第三步:保留图元显示属性和跨井型开关。
pTarget->setPlotVisible(pSource->getPlotVisible());

@ -708,18 +708,9 @@ void nmWxEditWellPlot::onOkClicked()
if(bCurveSelectionChanged) {
nmDataWellBase* pUpdatedWell = pManager->findWellByInstanceId(
m_pNmDataWell->getWellInstanceId());
if(pUpdatedWell != nullptr) {
QVector<QVector<double> > vecHistoryPressure;
QVector<QVector<double> > vecHistoryLogLog;
QVector<QVector<double> > vecHistorySemiLog;
pManager->calculationLogData(pUpdatedWell,
vecHistoryPressure,
vecHistoryLogLog,
vecHistorySemiLog);
pUpdatedWell->setHistoryPressure(vecHistoryPressure);
pUpdatedWell->setHistoryLogLog(vecHistoryLogLog);
pUpdatedWell->setHistorySemiLog(vecHistorySemiLog);
}
if(pUpdatedWell != nullptr) {
pManager->updateWellHistoryData(pUpdatedWell);
}
}
// 人工确认后的输入变化必须推进版本,阻止后台旧任务提交过期结果。

@ -1162,16 +1162,7 @@ void nmWxNumericalDesign::onOptionsIconClicked()
pWellData->setIndexF(oWellRow.m_nFlowSegmentIndex);
if(bCurveSelectionChanged) {
QVector<QVector<double> > vecHistoryPressure;
QVector<QVector<double> > vecHistoryLogLog;
QVector<QVector<double> > vecHistorySemiLog;
m_pDataManager->calculationLogData(pWellData,
vecHistoryPressure,
vecHistoryLogLog,
vecHistorySemiLog);
pWellData->setHistoryPressure(vecHistoryPressure);
pWellData->setHistoryLogLog(vecHistoryLogLog);
pWellData->setHistorySemiLog(vecHistorySemiLog);
m_pDataManager->updateWellHistoryData(pWellData);
if(bWasIncluded || bWillBeIncluded) {
bIncludedCurveSelectionChanged = true;
}

Loading…
Cancel
Save