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++

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