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/nmDataUtils.cpp

391 lines
13 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}