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/nmData/nmNumericalResultPersistenc...

1077 lines
34 KiB
C++

#include "nmNumericalResultPersistence.h"
#include "nmDataWellBase.h"
#include <QByteArray>
#include <QCryptographicHash>
#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>
#include <vtkXMLUnstructuredGridReader.h>
#include <vtkXMLUnstructuredGridWriter.h>
namespace {
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)
{
if(pError != NULL)
{
*pError = sError;
}
return false;
}
bool writeAll(QFile& oFile, const char* pData, quint64 nBytes)
{
quint64 nWritten = 0;
while(nWritten < nBytes)
{
const qint64 nChunk = static_cast<qint64>(qMin(
static_cast<quint64>(g_nStreamChunkBytes),
nBytes - nWritten));
const qint64 nResult = oFile.write(pData + nWritten, nChunk);
if(nResult != nChunk)
{
return false;
}
nWritten += static_cast<quint64>(nResult);
}
return true;
}
bool readAll(QFile& oFile, char* pData, quint64 nBytes)
{
quint64 nRead = 0;
while(nRead < nBytes)
{
const qint64 nChunk = static_cast<qint64>(qMin(
static_cast<quint64>(g_nStreamChunkBytes),
nBytes - nRead));
const qint64 nResult = oFile.read(pData + nRead, nChunk);
if(nResult != nChunk)
{
return false;
}
nRead += static_cast<quint64>(nResult);
}
return true;
}
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)
{
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;
}
bool writeDoubleVector(QFile& oFile, const QVector<double>& vecValues)
{
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)
{
if(nLeft != 0 && nRight > (~static_cast<quint64>(0)) / nLeft)
{
return false;
}
nProduct = nLeft * nRight;
return true;
}
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(!writeUInt32(oFile, nSeriesCount))
{
return false;
}
for(int nSeries = 0; nSeries < vecCurve.size(); ++nSeries)
{
const QVector<double>& vecValues = vecCurve[nSeries];
const quint64 nPointCount = static_cast<quint64>(vecValues.size());
quint64 nBytes = 0;
if(!safeMultiply(nPointCount, sizeof(double), nBytes) ||
nPointCount > g_nMaximumCurvePoints ||
!writeUInt64(oFile, nPointCount) ||
(nBytes > 0 && !writeDoubleVector(oFile, vecValues)))
{
return false;
}
}
return true;
}
bool readCurve(QFile& oFile,
QVector<QVector<double> >& vecCurve,
quint64& nTotalPointCount)
{
quint32 nSeriesCount = 0;
if(!readUInt32(oFile, nSeriesCount) ||
nSeriesCount > g_nMaximumCurveSeries)
{
return false;
}
QVector<QVector<double> > vecLoaded;
vecLoaded.resize(static_cast<int>(nSeriesCount));
for(quint32 nSeries = 0; nSeries < nSeriesCount; ++nSeries)
{
quint64 nPointCount = 0;
quint64 nBytes = 0;
if(!readUInt64(oFile, nPointCount) ||
nPointCount > g_nMaximumCurvePoints ||
nTotalPointCount > g_nMaximumCurvePoints - nPointCount ||
!safeMultiply(nPointCount, sizeof(double), nBytes) ||
nBytes > static_cast<quint64>(oFile.bytesAvailable()) ||
nPointCount > static_cast<quint64>(INT_MAX))
{
return false;
}
nTotalPointCount += nPointCount;
if(nBytes > 0 && !readDoubleVector(oFile,
vecLoaded[static_cast<int>(nSeries)], nPointCount))
{
return false;
}
}
vecCurve.swap(vecLoaded);
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)
{
if(pActual[nIndex] != pExpected[nIndex])
{
return false;
}
}
return true;
}
}
nmNumericalFileReference::nmNumericalFileReference()
: m_nLength(0)
{
}
bool nmNumericalFileReference::isValid() const
{
QString sNormalizedPath;
if(!nmNumericalResultPersistence::normalizeRelativePath(
m_sRelativePath, sNormalizedPath) ||
sNormalizedPath != m_sRelativePath || m_sSha1.size() != 40)
{
return false;
}
for(int nIndex = 0; nIndex < m_sSha1.size(); ++nIndex)
{
const QChar oChar = m_sSha1[nIndex];
if(!((oChar >= '0' && oChar <= '9') ||
(oChar >= 'a' && oChar <= 'f')))
{
return false;
}
}
return true;
}
nmNumericalWindowPayloadReferences::nmNumericalWindowPayloadReferences()
: m_bHasCurrentGrid(false),
m_bHasSnapshot(false)
{
}
int nmNumericalResultPersistence::projectVersion()
{
// v5 保存完整 Gauge 副本及输入签名,不再依赖框架当前记录。
return 5;
}
quint64 nmNumericalResultPersistence::maximumBinaryPayloadBytes()
{
return 512ull * 1024ull * 1024ull;
}
bool nmNumericalResultPersistence::normalizeRelativePath(
const QString& sPath,
QString& sNormalizedPath)
{
sNormalizedPath.clear();
if(sPath.isEmpty() || QDir::isAbsolutePath(sPath) ||
sPath.contains(':'))
{
return false;
}
QString sForwardPath = sPath;
sForwardPath.replace('\\', '/');
const QStringList listParts = sForwardPath.split(
'/', QString::KeepEmptyParts);
if(listParts.isEmpty())
{
return false;
}
for(int nIndex = 0; nIndex < listParts.size(); ++nIndex)
{
if(listParts[nIndex].isEmpty() || listParts[nIndex] == "." ||
listParts[nIndex] == "..")
{
return false;
}
}
sNormalizedPath = listParts.join("/");
return !sNormalizedPath.isEmpty();
}
bool nmNumericalResultPersistence::resolveReferencedPath(
const QString& sRootDirectory,
const QString& sRelativePath,
QString& sAbsolutePath)
{
QString sBaseCanonical;
QString sFileCanonical;
sAbsolutePath.clear();
QString sNormalizedPath;
if(!normalizeRelativePath(sRelativePath, sNormalizedPath))
{
return false;
}
QString sRoot = QDir::cleanPath(QFileInfo(sRootDirectory).absoluteFilePath());
QString sTarget = QDir::cleanPath(
QDir(sRoot).absoluteFilePath(sNormalizedPath));
sRoot.replace('\\', '/');
sTarget.replace('\\', '/');
const QString sRootPrefix = sRoot.endsWith('/') ? sRoot : sRoot + "/";
if(!sTarget.startsWith(sRootPrefix, Qt::CaseInsensitive))
{
return false;
}
// 已存在文件再核对规范路径,防止目录联接或符号链接逃逸成果根目录。
sBaseCanonical = QFileInfo(sRoot).canonicalFilePath();
sFileCanonical = QFileInfo(sTarget).canonicalFilePath();
if(!sBaseCanonical.isEmpty() && !sFileCanonical.isEmpty())
{
QString sCanonicalPrefix = QDir::fromNativeSeparators(sBaseCanonical);
if(!sCanonicalPrefix.endsWith('/'))
{
sCanonicalPrefix += "/";
}
if(!QDir::fromNativeSeparators(sFileCanonical).startsWith(
sCanonicalPrefix, Qt::CaseInsensitive))
{
return false;
}
}
sAbsolutePath = QDir::toNativeSeparators(sTarget);
return true;
}
bool nmNumericalResultPersistence::buildFileReference(
const QString& sAbsolutePath,
const QString& sRelativePath,
nmNumericalFileReference& oReference)
{
QString sNormalizedPath;
if(!normalizeRelativePath(sRelativePath, sNormalizedPath))
{
return false;
}
QFile oFile(sAbsolutePath);
if(!oFile.open(QIODevice::ReadOnly))
{
return false;
}
QCryptographicHash oHash(QCryptographicHash::Sha1);
while(!oFile.atEnd())
{
const QByteArray baChunk = oFile.read(g_nStreamChunkBytes);
if(baChunk.isEmpty() && oFile.error() != QFile::NoError)
{
oFile.close();
return false;
}
oHash.addData(baChunk);
}
const qint64 nSize = oFile.size();
oFile.close();
if(nSize < 0)
{
return false;
}
oReference.m_sRelativePath = sNormalizedPath;
oReference.m_nLength = static_cast<quint64>(nSize);
oReference.m_sSha1 = QString::fromLatin1(oHash.result().toHex());
return oReference.isValid();
}
bool nmNumericalResultPersistence::validateFileReference(
const QString& sRootDirectory,
const nmNumericalFileReference& oReference,
QString* pAbsolutePath,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
if(!oReference.isValid())
{
return setError(pError, "Numerical file reference is invalid.");
}
QString sFilePath;
if(!resolveReferencedPath(sRootDirectory,
oReference.m_sRelativePath, sFilePath))
{
return setError(pError, "Numerical file reference leaves its root directory.");
}
const QFileInfo oInfo(sFilePath);
if(!oInfo.exists() || !oInfo.isFile() || oInfo.size() < 0 ||
static_cast<quint64>(oInfo.size()) != oReference.m_nLength)
{
return setError(pError, "Numerical file length does not match its manifest.");
}
nmNumericalFileReference oActual;
if(!buildFileReference(sFilePath, oReference.m_sRelativePath, oActual) ||
oActual.m_nLength != oReference.m_nLength ||
oActual.m_sSha1 != oReference.m_sSha1)
{
return setError(pError, "Numerical file SHA-1 does not match its manifest.");
}
if(pAbsolutePath != NULL)
{
*pAbsolutePath = sFilePath;
}
return true;
}
bool nmNumericalResultPersistence::writeWellHistory(
const QString& sFilePath,
const nmNumericalWellHistoryData& oHistory,
QString* pError)
{
if(pError != NULL)
{
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))
{
return setError(pError, "Cannot create numerical well history file.");
}
const bool bWritten = writeAll(oFile, g_aWellHistoryMagic, 8) &&
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) &&
oFile.flush();
oFile.close();
if(!bWritten)
{
QFile::remove(sFilePath);
return setError(pError, "Cannot write complete numerical well history file.");
}
return true;
}
bool nmNumericalResultPersistence::readWellHistory(
const QString& sFilePath,
nmNumericalWellHistoryData& oHistory,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
const QFileInfo oInfo(sFilePath);
if(!oInfo.exists() || oInfo.size() < 0 ||
static_cast<quint64>(oInfo.size()) > maximumBinaryPayloadBytes())
{
return setError(pError, "Numerical well history payload is missing or too large.");
}
try
{
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::ReadOnly))
{
return setError(pError, "Cannot open numerical well history file.");
}
char aMagic[8] = { 0 };
quint32 nFormatVersion = 0;
quint32 nEndianMarker = 0;
quint64 nTotalPointCount = 0;
nmNumericalWellHistoryData oLoaded;
const bool bRead = readAll(oFile, aMagic, 8) &&
hasExactMagic(aMagic, g_aWellHistoryMagic, 8) &&
readUInt32(oFile, nFormatVersion) &&
nFormatVersion == g_nWellHistoryFormatVersion &&
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) &&
oFile.atEnd();
oFile.close();
if(!bRead)
{
return setError(pError, "Numerical well history file is damaged.");
}
oHistory = oLoaded;
return true;
}
catch(const std::bad_alloc&)
{
return setError(pError, "Not enough memory to load numerical well history.");
}
}
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,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
if(pGrid == NULL || pGrid->GetNumberOfPoints() <= 0 ||
pGrid->GetNumberOfCells() <= 0)
{
return setError(pError, "Numerical grid is empty.");
}
vtkNew<vtkXMLUnstructuredGridWriter> pWriter;
pWriter->SetInputData(pGrid);
pWriter->SetFileName(sFilePath.toLocal8Bit().constData());
pWriter->SetDataModeToBinary();
if(pWriter->Write() == 0 || !QFileInfo(sFilePath).isFile() ||
QFileInfo(sFilePath).size() <= 0)
{
QFile::remove(sFilePath);
return setError(pError, "Cannot write numerical VTU grid.");
}
return true;
}
bool nmNumericalResultPersistence::readGrid(
const QString& sFilePath,
vtkSmartPointer<vtkUnstructuredGrid>& pGrid,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
pGrid = NULL;
if(!QFileInfo(sFilePath).isFile())
{
return setError(pError, "Numerical VTU grid is missing.");
}
try
{
vtkNew<vtkXMLUnstructuredGridReader> pReader;
pReader->SetFileName(sFilePath.toLocal8Bit().constData());
pReader->Update();
vtkUnstructuredGrid* pOutput = pReader->GetOutput();
if(pOutput == NULL || pOutput->GetNumberOfPoints() <= 0 ||
pOutput->GetNumberOfCells() <= 0)
{
return setError(pError, "Numerical VTU grid is invalid.");
}
pGrid = pOutput;
return pGrid != NULL;
}
catch(const std::bad_alloc&)
{
pGrid = NULL;
return setError(pError, "Not enough memory to load numerical VTU grid.");
}
}