#include "nmNumericalResultPersistence.h" #include "nmDataWellBase.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(qMin( static_cast(g_nStreamChunkBytes), nBytes - nWritten)); const qint64 nResult = oFile.write(pData + nWritten, nChunk); if(nResult != nChunk) { return false; } nWritten += static_cast(nResult); } return true; } bool readAll(QFile& oFile, char* pData, quint64 nBytes) { quint64 nRead = 0; while(nRead < nBytes) { const qint64 nChunk = static_cast(qMin( static_cast(g_nStreamChunkBytes), nBytes - nRead)); const qint64 nResult = oFile.read(pData + nRead, nChunk); if(nResult != nChunk) { return false; } nRead += static_cast(nResult); } return true; } bool writeUInt32(QFile& oFile, quint32 nValue) { char aBytes[4]; for(int nIndex = 0; nIndex < 4; ++nIndex) { aBytes[nIndex] = static_cast((nValue >> (nIndex * 8)) & 0xffu); } return writeAll(oFile, aBytes, 4); } bool readUInt32(QFile& oFile, quint32& nValue) { unsigned char aBytes[4]; if(!readAll(oFile, reinterpret_cast(aBytes), 4)) { return false; } nValue = 0; for(int nIndex = 0; nIndex < 4; ++nIndex) { nValue |= static_cast(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((nValue >> (nIndex * 8)) & 0xffu); } return writeAll(oFile, aBytes, 8); } bool readUInt64(QFile& oFile, quint64& nValue) { unsigned char aBytes[8]; if(!readAll(oFile, reinterpret_cast(aBytes), 8)) { return false; } nValue = 0; for(int nIndex = 0; nIndex < 8; ++nIndex) { nValue |= static_cast(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((nBits >> (nIndex * 8)) & 0xffu)); } } bool readDouble(const char* pBytes, double& dValue) { quint64 nBits = 0; for(int nIndex = 0; nIndex < 8; ++nIndex) { nBits |= static_cast( static_cast(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(baValue.size()) <= g_nMaximumStringBytes && writeUInt32(oFile, static_cast(baValue.size())) && (baValue.isEmpty() || writeAll(oFile, baValue.constData(), static_cast(baValue.size()))); } bool readString(QFile& oFile, QString& sValue) { quint32 nBytes = 0; if(!readUInt32(oFile, nBytes) || nBytes > g_nMaximumStringBytes || static_cast(nBytes) > static_cast(oFile.bytesAvailable()) || nBytes > static_cast(INT_MAX)) { return false; } QByteArray baValue; baValue.resize(static_cast(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& vecValues) { QByteArray baChunk; baChunk.reserve(g_nDoubleChunkCount * static_cast(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(baChunk.size()))) { return false; } } return true; } bool readDoubleVector(QFile& oFile, QVector& vecValues, quint64 nPointCount) { if(nPointCount > static_cast(INT_MAX)) { return false; } vecValues.resize(static_cast(nPointCount)); QByteArray baChunk; for(quint64 nStart = 0; nStart < nPointCount; nStart += static_cast(g_nDoubleChunkCount)) { const int nCount = static_cast(qMin( static_cast(g_nDoubleChunkCount), nPointCount - nStart)); baChunk.resize(nCount * static_cast(sizeof(double))); if(!readAll(oFile, baChunk.data(), static_cast(baChunk.size()))) { return false; } for(int nOffset = 0; nOffset < nCount; ++nOffset) { if(!readDouble(baChunk.constData() + nOffset * static_cast(sizeof(double)), vecValues[static_cast(nStart) + nOffset])) { return false; } } } return true; } bool safeMultiply(quint64 nLeft, quint64 nRight, quint64& nProduct) { if(nLeft != 0 && nRight > (~static_cast(0)) / nLeft) { return false; } nProduct = nLeft * nRight; return true; } bool writeCurve(QFile& oFile, const QVector >& vecCurve) { if(static_cast(vecCurve.size()) > g_nMaximumCurveSeries) { return false; } const quint32 nSeriesCount = static_cast(vecCurve.size()); if(!writeUInt32(oFile, nSeriesCount)) { return false; } for(int nSeries = 0; nSeries < vecCurve.size(); ++nSeries) { const QVector& vecValues = vecCurve[nSeries]; const quint64 nPointCount = static_cast(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 >& vecCurve, quint64& nTotalPointCount) { quint32 nSeriesCount = 0; if(!readUInt32(oFile, nSeriesCount) || nSeriesCount > g_nMaximumCurveSeries) { return false; } QVector > vecLoaded; vecLoaded.resize(static_cast(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(oFile.bytesAvailable()) || nPointCount > static_cast(INT_MAX)) { return false; } nTotalPointCount += nPointCount; if(nBytes > 0 && !readDoubleVector(oFile, vecLoaded[static_cast(nSeries)], nPointCount)) { return false; } } vecCurve.swap(vecLoaded); return true; } bool writePointVector(QFile& oFile, const QVector& vecPoints) { const quint64 nPointCount = static_cast(vecPoints.size()); if(nPointCount > g_nMaximumCurvePoints || !writeUInt64(oFile, nPointCount)) { return false; } QByteArray baChunk; baChunk.reserve(g_nDoubleChunkCount * 2 * static_cast(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(baChunk.size()))) { return false; } } return true; } bool readPointVector(QFile& oFile, QVector& 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(oFile.bytesAvailable()) || nPointCount > static_cast(INT_MAX)) { return false; } nTotalPointCount += nPointCount; vecPoints.resize(static_cast(nPointCount)); QByteArray baChunk; for(quint64 nStart = 0; nStart < nPointCount; nStart += static_cast(g_nDoubleChunkCount)) { const int nCount = static_cast(qMin( static_cast(g_nDoubleChunkCount), nPointCount - nStart)); baChunk.resize(nCount * 2 * static_cast(sizeof(double))); if(!readAll(oFile, baChunk.data(), static_cast(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(sizeof(double)); if(!readDouble(pPoint, dX) || !readDouble(pPoint + sizeof(double), dY)) { return false; } vecPoints[static_cast(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(g_nMaximumGaugeRecordCount) || oGaugeData.m_vecFlowRecords.size() > static_cast(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(baCode.size()) > g_nMaximumStringBytes || static_cast(baName.size()) > g_nMaximumStringBytes || static_cast(baTime.size()) > g_nMaximumStringBytes || static_cast(oRecord.vecPressurePoints.size()) > g_nMaximumCurvePoints - nTotalPointCount) { return false; } nTotalPointCount += static_cast( 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 arrPoints[] = { oRecord.vecOilPoints, oRecord.vecGasPoints, oRecord.vecWaterPoints }; if(!isGaugeStatusValid(oRecord.eStatus) || static_cast(baCode.size()) > g_nMaximumStringBytes || static_cast(baName.size()) > g_nMaximumStringBytes || static_cast(baTime.size()) > g_nMaximumStringBytes) { return false; } for(int nPhase = 0; nPhase < 3; ++nPhase) { if(static_cast(arrPoints[nPhase].size()) > g_nMaximumCurvePoints - nTotalPointCount) { return false; } nTotalPointCount += static_cast(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(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(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(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( 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(oRecord.eStatus)) && writePointVector(oFile, oRecord.vecPressurePoints); } bWritten = bWritten && writeUInt32(oFile, static_cast( 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(oRecord.eStatus)) && writePointVector(oFile, oRecord.vecOilPoints) && writePointVector(oFile, oRecord.vecGasPoints) && writePointVector(oFile, oRecord.vecWaterPoints) && writeUInt32(oFile, static_cast( static_cast(oRecord.nIndexF))); } bWritten = bWritten && oFile.flush() && oFile.size() > 0 && static_cast(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(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(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( NM_GaugeRecord_InvalidIdentity) && readPointVector(oFile, oRecord.vecPressurePoints, nTotalPointCount); if(bRead) { oRecord.eStatus = static_cast(nStatus); oLoaded.m_vecPressureRecords.append(oRecord); } } bRead = bRead && readUInt32(oFile, nFlowCount) && nFlowCount <= g_nMaximumGaugeRecordCount; oLoaded.m_vecFlowRecords.reserve( bRead ? static_cast(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( 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(nStatus); oRecord.nIndexF = static_cast(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 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& pGrid, QString* pError) { if(pError != NULL) { pError->clear(); } pGrid = NULL; if(!QFileInfo(sFilePath).isFile()) { return setError(pError, "Numerical VTU grid is missing."); } try { vtkNew 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."); } }