#include "zxLogInstance.h" #include "iDataIoUtilsEx.h" #include "ZxDataProject.h" #include "ZxDataWell.h" #include "nmDataDemo.h" #include "nmDataUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace { typedef std::vector > nmHxPlotMatrix; /** * @brief 与算法方 HX_PLOT_DATA 二进制布局一致的本地结构。 * * 正式接口头文件当前使用 GBK 编码。这里保留最小 ABI 镜像,避免为了一个新增 * 结构整体转码第三方头文件;函数通过 GetProcAddress 动态解析,不参与静态链接。 */ struct nmHxPlotData { nmHxPlotMatrix loglog; nmHxPlotMatrix semilog; }; // HX_NWTM.dll 内部存在进程级共享状态,建网、求解、克里金和绘图必须共用同一把锁。 QMutex s_oHxNwtmDllMutex; void setHxPlotError(QString* pErrorMessage, const QString& sMessage) { if(pErrorMessage != 0) { *pErrorMessage = sMessage; } } bool isReasonableHxPlotValue(double dValue) { const double dMaximumAbsoluteValue = 1.0e12; return _finite(dValue) != 0 && dValue >= -dMaximumAbsoluteValue && dValue <= dMaximumAbsoluteValue; } } nmDataUtils::nmDataUtils() { } nmDataUtils::~nmDataUtils() { } QMutex* nmDataUtils::getHxNwtmDllMutex() { return &s_oHxNwtmDllMutex; } bool nmDataUtils::calculateHxNwtmPlot( const QVector& flowDurations, const QVector& rates, const QVector& pressureTimes, const QVector& pressures, int flowSectionIndex, const QString& licensePath, QVector >& logLog, QVector >& semiLog, bool* interfaceAvailable, QString* errorMessage) { logLog.clear(); semiLog.clear(); if(interfaceAvailable != 0) { *interfaceAvailable = false; } if(errorMessage != 0) { errorMessage->clear(); } // Rate 和 Press 都是两行矩阵;两行长度不一致时不能交给第三方 DLL。 if(flowDurations.isEmpty() || flowDurations.size() != rates.size() || pressureTimes.isEmpty() || pressureTimes.size() != pressures.size() || flowSectionIndex < 1 || flowSectionIndex > flowDurations.size()) { setHxPlotError(errorMessage, QString::fromLatin1("Invalid HX_NWTM_PLOT input arrays.")); return false; } // 项目保存的是逐段持续时间,算法接口需要当前段的累计起止时刻。 double dSectionStart = 0.0; for(int nIndex = 0; nIndex < flowSectionIndex - 1; ++nIndex) { if(!isReasonableHxPlotValue(flowDurations[nIndex]) || flowDurations[nIndex] < 0.0) { setHxPlotError(errorMessage, QString::fromLatin1("Invalid flow-section duration.")); return false; } dSectionStart += flowDurations[nIndex]; } const double dSelectedDuration = flowDurations[flowSectionIndex - 1]; if(!isReasonableHxPlotValue(dSelectedDuration) || dSelectedDuration <= 0.0 || !isReasonableHxPlotValue(dSectionStart + dSelectedDuration)) { setHxPlotError(errorMessage, QString::fromLatin1("Invalid selected flow-section range.")); return false; } const double dSectionEnd = dSectionStart + dSelectedDuration; // 算法方约定:Rate[0] 为各段时间,Rate[1] 为对应流量。 nmHxPlotMatrix oRate(2); oRate[0].reserve(flowDurations.size()); oRate[1].reserve(rates.size()); for(int nIndex = 0; nIndex < flowDurations.size(); ++nIndex) { if(!isReasonableHxPlotValue(flowDurations[nIndex]) || !isReasonableHxPlotValue(rates[nIndex])) { setHxPlotError(errorMessage, QString::fromLatin1("Invalid rate history value.")); return false; } oRate[0].push_back(flowDurations[nIndex]); oRate[1].push_back(rates[nIndex]); } // 算法方约定:Press[0] 为压力时间,Press[1] 为压力或气井拟压力。 nmHxPlotMatrix oPressure(2); oPressure[0].reserve(pressureTimes.size()); oPressure[1].reserve(pressures.size()); for(int nIndex = 0; nIndex < pressureTimes.size(); ++nIndex) { if(!isReasonableHxPlotValue(pressureTimes[nIndex]) || !isReasonableHxPlotValue(pressures[nIndex])) { setHxPlotError(errorMessage, QString::fromLatin1("Invalid pressure history value.")); return false; } oPressure[0].push_back(pressureTimes[nIndex]); oPressure[1].push_back(pressures[nIndex]); } QMutexLocker oLocker(getHxNwtmDllMutex()); HMODULE hModule = LoadLibrary(L"HX_NWTM.dll"); if(hModule == NULL) { setHxPlotError(errorMessage, QString::fromLatin1("Failed to load HX_NWTM.dll.")); return false; } typedef void (*HxNwtmPlotFunction)( nmHxPlotData&, const nmHxPlotMatrix&, const nmHxPlotMatrix&, const double, const double, std::string); HxNwtmPlotFunction pfnPlot = reinterpret_cast( GetProcAddress(hModule, "HX_NWTM_PLOT")); if(pfnPlot == NULL) { // 兼容当前尚未提供绘图接口的旧版 HX_NWTM.dll,调用方可继续走旧算法回退。 FreeLibrary(hModule); return false; } if(interfaceAvailable != 0) { *interfaceAvailable = true; } // 已确认新导出存在后再校验许可证。这样许可证错误不会被误判成“接口不存在”, // 调用方也就不会错误地回退到旧算法。 if(licensePath.isEmpty() || !QFileInfo(licensePath).exists()) { FreeLibrary(hModule); setHxPlotError(errorMessage, QString::fromLatin1("HX_NWTM license file was not found.")); return false; } nmHxPlotData oResult; // 当前版本 DLL 会直接访问输出矩阵的固定行号,不会先扩展外层 vector。 // 按接口约定预建双对数 3 行、半对数 2 行,避免空外层数组导致访问冲突。 oResult.loglog.resize(3); oResult.semilog.resize(2); try { pfnPlot(oResult, oRate, oPressure, dSectionStart, dSectionEnd, licensePath.toStdString()); } catch(const std::exception& oException) { FreeLibrary(hModule); setHxPlotError(errorMessage, QString::fromLatin1("HX_NWTM_PLOT exception: %1") .arg(QString::fromLocal8Bit(oException.what()))); return false; } catch(...) { FreeLibrary(hModule); setHxPlotError(errorMessage, QString::fromLatin1("HX_NWTM_PLOT raised an unknown exception.")); return false; } FreeLibrary(hModule); // 输出按列存放,任何列数或点数不匹配都视为第三方接口返回不完整。 if(oResult.loglog.size() < 3 || oResult.semilog.size() < 2 || oResult.loglog[0].size() != oResult.loglog[1].size() || oResult.loglog[0].size() != oResult.loglog[2].size() || oResult.semilog[0].size() != oResult.semilog[1].size()) { setHxPlotError(errorMessage, QString::fromLatin1("HX_NWTM_PLOT returned malformed arrays.")); return false; } // 双对数坐标要求时间和导数为正;统一过滤首尾占位点和非数值点。 logLog.resize(3); for(size_t nIndex = 0; nIndex < oResult.loglog[0].size(); ++nIndex) { const double dTime = oResult.loglog[0][nIndex]; const double dPressureDifference = oResult.loglog[1][nIndex]; const double dDerivative = oResult.loglog[2][nIndex]; if(!isReasonableHxPlotValue(dTime) || dTime <= 0.0 || !isReasonableHxPlotValue(dPressureDifference) || !isReasonableHxPlotValue(dDerivative) || dDerivative < DBL_EPSILON) { continue; } logLog[0].append(dTime); logLog[1].append(dPressureDifference); logLog[2].append(dDerivative); } semiLog.resize(2); for(size_t nIndex = 0; nIndex < oResult.semilog[0].size(); ++nIndex) { const double dTime = oResult.semilog[0][nIndex]; const double dPressure = oResult.semilog[1][nIndex]; if(!isReasonableHxPlotValue(dTime) || dTime <= 0.0 || !isReasonableHxPlotValue(dPressure)) { continue; } semiLog[0].append(dTime); semiLog[1].append(dPressure); } if(logLog[0].isEmpty() || semiLog[0].isEmpty()) { logLog.clear(); semiLog.clear(); setHxPlotError(errorMessage, QString::fromLatin1("HX_NWTM_PLOT returned no valid plot points.")); return false; } return true; } bool nmDataUtils::extendChildrenOfProj(ZxDataProject* pDataProj) { Q_ASSERT (nullptr != pDataProj); /* 如下代码只是参照,请根据具体需求咨询框架负责人员后进行处理 pDataObj->clearChildren(iDataModelType::sTypePvtInfo); // load Anal PVTs QString sSql = "Select PvtID, PvtName, JobID from V_PVT_INFO"; sSql += QString(" order by PvtID"); VVecVariant vvec; QString sLog = ""; if (_load(sSql, vvec, sLog, false, true)) { for (int i = 0; i < vvec.count(); i++) { ZxDataPvtInfo* p = new ZxDataPvtInfo(); Q_ASSERT (nullptr != p); p->setCode(vvec.at(i).at(0).toString()); p->setName(vvec.at(i).at(1).toString()); p->setAnalID(vvec.at(i).at(2).toString()); p->createChildren(); appendChild(p); p->setLoaded(false); } } else if (!sLog.isEmpty()) { zxLogRunW(QObject::tr("Failed to load PVTInfo with \r\n '%1'").arg(sLog)); return false; }//*/ return true; } bool nmDataUtils::extendChildrenOfWell(ZxDataWell* pDataWell) { Q_ASSERT (nullptr != pDataWell); // 此处仅仅以获取样表举例 Alias="n数值样例数据表" Name="N_DATA_DEMO" QString sWellCode = pDataWell->getCode(); QString sSql = "Select distinct ID from N_DATA_DEMO"; sSql += QString(" where WellCode = '%1'").arg(sWellCode); sSql += QString(" order by ID"); VVecVariant vvec; QString sLog = ""; if (pDataWell->loadData(sSql, vvec, sLog, false)) { for (int i = 0; i < vvec.count(); i++) { nmDataDemo* p = new nmDataDemo(); Q_ASSERT (nullptr != p); p->setCode(vvec.at(i).at(0).toString()); p->setWellCode(sWellCode); pDataWell->appendChild(p); } } else if (!sLog.isEmpty()) { zxLogRunW(QObject::tr("Failed to load nmDataDemo with \r\n '%1'").arg(sLog)); return false; } return true; } bool nmDataUtils::deleteChildrenOfWell(ZxDataWell* pDataWell) { Q_ASSERT(nullptr != pDataWell); QString sWellCode = pDataWell->getCode(); // 涉及到哪些表(此处只是举例,需要根据表格结构进行处理) QStringList listTables; listTables << "N_DATA_DEMO"; //listTables << "N_DATA_DEMO"; 其它 QStringList listSqls; listSqls.clear(); for (int i = 0; i < listTables.count(); i++) { listSqls << QString("delete from %1 where WellCode='%2'").arg(listTables[i]).arg(sWellCode); } bool bResult = true; foreach (QString s, listSqls) { iDataIoUtilsEx o; bool b = o.execSqlOnly(s); if (!b) { zxLogRunW(o.getLastError()); } bResult &= b; } // TODO 说明 // 理论上,此处还应该把子数据对象,从pDataWell中Remove,但是 // 考虑到这是删除井,在主程序中,会统一把pDataWell所有子数据对象删除,所以此处无需额外处理 return bResult; } QStringList nmDataUtils::readNmDataFile(const QString &filePath) { QStringList content; // 打开文件 QFile file(filePath); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << "Failed to open file:" << filePath; return content; } // 使用QTextStream读取文件 QTextStream in(&file); while(!in.atEnd()) { // 读取一行 QString line = in.readLine(); // 将行内容放入QStringList content.append(line); } // 关闭文件 file.close(); return content; }