|
|
|
|
|
#include "zxLogInstance.h"
|
|
|
|
|
|
#include "iDataIoUtilsEx.h"
|
|
|
|
|
|
#include "ZxDataProject.h"
|
|
|
|
|
|
#include "ZxDataWell.h"
|
|
|
|
|
|
#include "nmDataDemo.h"
|
|
|
|
|
|
#include "nmDataUtils.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
#include <QFile>
|
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
|
#include <QMutex>
|
|
|
|
|
|
#include <QMutexLocker>
|
|
|
|
|
|
#include <QStringList>
|
|
|
|
|
|
#include <QTextStream>
|
|
|
|
|
|
|
|
|
|
|
|
#include <exception>
|
|
|
|
|
|
#include <float.h>
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <Windows.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
|
{
|
|
|
|
|
|
typedef std::vector<std::vector<double> > 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<double>& flowDurations,
|
|
|
|
|
|
const QVector<double>& rates,
|
|
|
|
|
|
const QVector<double>& pressureTimes,
|
|
|
|
|
|
const QVector<double>& pressures,
|
|
|
|
|
|
int flowSectionIndex,
|
|
|
|
|
|
const QString& licensePath,
|
|
|
|
|
|
QVector<QVector<double> >& logLog,
|
|
|
|
|
|
QVector<QVector<double> >& 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<HxNwtmPlotFunction>(
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|