|
|
#include "nmCalculationUtils.h"
|
|
|
|
|
|
#include <QFile>
|
|
|
#include <QFileInfo>
|
|
|
#include <QDir>
|
|
|
#include <QDebug>
|
|
|
#include <QCoreApplication>
|
|
|
#include <QDateTime>
|
|
|
#include <QMutex>
|
|
|
#include <QMutexLocker>
|
|
|
#include <QRegExp>
|
|
|
#include <fstream>
|
|
|
#include <float.h>
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
#include <windows.h>
|
|
|
#else
|
|
|
#include <errno.h>
|
|
|
#include <signal.h>
|
|
|
#include <sys/types.h>
|
|
|
#endif
|
|
|
|
|
|
namespace
|
|
|
{
|
|
|
// HX_NWTM.dll 的配置和结果查询接口使用进程级共享状态,所有入口共用此锁。
|
|
|
QMutex s_oHxNwtmDllMutex;
|
|
|
|
|
|
bool isKrigingCancellationRequested(const QAtomicInt* pCancelRequested)
|
|
|
{
|
|
|
return pCancelRequested != NULL &&
|
|
|
static_cast<int>(*pCancelRequested) != 0;
|
|
|
}
|
|
|
|
|
|
class nmKrigingMutexLocker
|
|
|
{
|
|
|
public:
|
|
|
nmKrigingMutexLocker()
|
|
|
: m_pMutex(NULL),
|
|
|
m_bLocked(false)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
~nmKrigingMutexLocker()
|
|
|
{
|
|
|
if(m_bLocked && m_pMutex != NULL) {
|
|
|
m_pMutex->unlock();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool lock(QMutex* pMutex, const QAtomicInt* pCancelRequested)
|
|
|
{
|
|
|
if(pMutex == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
if(pCancelRequested == NULL) {
|
|
|
pMutex->lock();
|
|
|
} else {
|
|
|
// 只改变等待方式,不改变 DLL 全局锁的保护范围和互斥语义。
|
|
|
while(!pMutex->tryLock(100)) {
|
|
|
if(isKrigingCancellationRequested(pCancelRequested)) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
m_pMutex = pMutex;
|
|
|
m_bLocked = true;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
QMutex* m_pMutex;
|
|
|
bool m_bLocked;
|
|
|
};
|
|
|
|
|
|
QString krigingText(const char* sourceText)
|
|
|
{
|
|
|
return QCoreApplication::translate("nmCalculationUtils", sourceText);
|
|
|
}
|
|
|
|
|
|
bool isFiniteValue(double value)
|
|
|
{
|
|
|
#ifdef _MSC_VER
|
|
|
return _finite(value) != 0;
|
|
|
#else
|
|
|
return std::isfinite(value);
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
// 从自动拟合运行目录名中解析进程 ID。
|
|
|
bool parseAutoFitRunDirectoryProcessId(const QString& directoryName,
|
|
|
qint64* processId)
|
|
|
{
|
|
|
// 名称为 <pid>-<yyyyMMdd_hhmmss_zzz>,极端重名时追加 -<counter>。
|
|
|
QRegExp runDirectoryPattern(
|
|
|
"^([0-9]+)-[0-9]{8}_[0-9]{6}_[0-9]{3}(-[0-9]+)?$");
|
|
|
if(!runDirectoryPattern.exactMatch(directoryName)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool conversionSucceeded = false;
|
|
|
const qint64 parsedProcessId =
|
|
|
runDirectoryPattern.cap(1).toLongLong(&conversionSucceeded);
|
|
|
if(!conversionSucceeded || parsedProcessId <= 0) {
|
|
|
return false;
|
|
|
}
|
|
|
if(processId != NULL) {
|
|
|
*processId = parsedProcessId;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool isAutoFitRunProcessActive(const QString& directoryName)
|
|
|
{
|
|
|
// 判断该运行目录所属的进程是否仍在运行。
|
|
|
qint64 processId = 0;
|
|
|
if(!parseAutoFitRunDirectoryProcessId(directoryName, &processId)) {
|
|
|
return false;
|
|
|
}
|
|
|
if(processId == QCoreApplication::applicationPid()) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
if(static_cast<quint64>(processId) > 0xFFFFFFFFULL) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
HANDLE processHandle = OpenProcess(
|
|
|
SYNCHRONIZE, FALSE, static_cast<DWORD>(processId));
|
|
|
if(processHandle == NULL) {
|
|
|
// ERROR_INVALID_PARAMETER 表示 PID 已不存在;其他错误(例如拒绝访问)
|
|
|
// 无法证明进程已退出,保守地保留目录。
|
|
|
return GetLastError() != ERROR_INVALID_PARAMETER;
|
|
|
}
|
|
|
|
|
|
const DWORD waitResult = WaitForSingleObject(processHandle, 0);
|
|
|
CloseHandle(processHandle);
|
|
|
return waitResult == WAIT_TIMEOUT || waitResult == WAIT_FAILED;
|
|
|
#else
|
|
|
const int result = kill(static_cast<pid_t>(processId), 0);
|
|
|
return result == 0 || errno == EPERM;
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
bool isOwnedAutoFitTemporaryDirectory(const QString& directoryPath)
|
|
|
{
|
|
|
if(directoryPath.trimmed().isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 校验路径是否为本程序创建的自动拟合临时目录。
|
|
|
const QString rootPath = nmCalculationUtils::autoFitTemporaryRootPath();
|
|
|
const QFileInfo directoryInfo(QDir::cleanPath(
|
|
|
QFileInfo(directoryPath).absoluteFilePath()));
|
|
|
const QString parentPath = QDir::cleanPath(directoryInfo.absolutePath());
|
|
|
#ifdef Q_OS_WIN
|
|
|
const Qt::CaseSensitivity pathCaseSensitivity = Qt::CaseInsensitive;
|
|
|
#else
|
|
|
const Qt::CaseSensitivity pathCaseSensitivity = Qt::CaseSensitive;
|
|
|
#endif
|
|
|
if(parentPath.compare(rootPath, pathCaseSensitivity) != 0 ||
|
|
|
directoryInfo.isSymLink()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return parseAutoFitRunDirectoryProcessId(directoryInfo.fileName(), NULL);
|
|
|
}
|
|
|
|
|
|
bool removeDirectoryRecursively(const QString& directoryPath)
|
|
|
{
|
|
|
QDir directory(directoryPath);
|
|
|
if(!directory.exists()) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
const QFileInfoList entries = directory.entryInfoList(
|
|
|
QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden | QDir::System);
|
|
|
bool allRemoved = true;
|
|
|
for(int i = 0; i < entries.size(); ++i) {
|
|
|
const QFileInfo& entry = entries[i];
|
|
|
if(entry.isSymLink()) {
|
|
|
// 符号链接只删除链接本身。
|
|
|
if(!QFile::remove(entry.absoluteFilePath())) {
|
|
|
allRemoved = false;
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
|
if(entry.isDir()) {
|
|
|
if(!removeDirectoryRecursively(entry.absoluteFilePath())) {
|
|
|
allRemoved = false;
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
QFile file(entry.absoluteFilePath());
|
|
|
if(!file.permissions().testFlag(QFile::WriteUser)) {
|
|
|
file.setPermissions(file.permissions() | QFile::WriteUser);
|
|
|
}
|
|
|
if(!file.remove()) {
|
|
|
allRemoved = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return allRemoved && directory.rmdir(directory.absolutePath());
|
|
|
}
|
|
|
|
|
|
void setKrigingError(QString* errorMessage, const QString& message)
|
|
|
{
|
|
|
if(errorMessage != NULL) {
|
|
|
*errorMessage = message;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nmCalculationUtils::nmCalculationUtils() {
|
|
|
}
|
|
|
|
|
|
QString nmCalculationUtils::autoFitTemporaryRootPath()
|
|
|
{
|
|
|
// 返回系统临时目录下的自动拟合根目录。
|
|
|
return QDir::cleanPath(
|
|
|
QDir(QDir::tempPath()).absoluteFilePath("WTAI/AutoFit"));
|
|
|
}
|
|
|
|
|
|
QString nmCalculationUtils::createAutoFitTemporaryDirectory()
|
|
|
{
|
|
|
const QString rootPath = autoFitTemporaryRootPath();
|
|
|
if(!QDir().mkpath(rootPath)) {
|
|
|
qWarning() << "Cannot create auto-fit temporary root:" << rootPath;
|
|
|
return QString();
|
|
|
}
|
|
|
|
|
|
QDir rootDirectory(rootPath);
|
|
|
// 使用进程 ID 和时间戳创建本次拟合的独立目录。
|
|
|
const QString baseName = QString("%1-%2")
|
|
|
.arg(QString::number(QCoreApplication::applicationPid()))
|
|
|
.arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz"));
|
|
|
for(int counter = 0; counter < 1000; ++counter) {
|
|
|
const QString directoryName = counter == 0
|
|
|
? baseName
|
|
|
: QString("%1-%2").arg(baseName).arg(counter);
|
|
|
if(rootDirectory.mkdir(directoryName)) {
|
|
|
return QDir::cleanPath(rootDirectory.absoluteFilePath(directoryName));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
qWarning() << "Cannot create unique auto-fit temporary directory in:" << rootPath;
|
|
|
return QString();
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::removeAutoFitTemporaryDirectory(
|
|
|
const QString& directoryPath)
|
|
|
{
|
|
|
if(!isOwnedAutoFitTemporaryDirectory(directoryPath)) {
|
|
|
qWarning() << "Refusing to remove non auto-fit directory:" << directoryPath;
|
|
|
return false;
|
|
|
}
|
|
|
return removeDirectoryRecursively(QDir::cleanPath(directoryPath));
|
|
|
}
|
|
|
|
|
|
void nmCalculationUtils::cleanupStaleAutoFitTemporaryDirectories(
|
|
|
int maximumAgeHours)
|
|
|
{
|
|
|
// 清理超过保留时间且所属进程已经退出的残留目录。
|
|
|
if(maximumAgeHours <= 0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const QString rootPath = autoFitTemporaryRootPath();
|
|
|
QDir rootDirectory(rootPath);
|
|
|
if(!rootDirectory.exists()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const qint64 maximumAgeSeconds =
|
|
|
static_cast<qint64>(maximumAgeHours) * 60 * 60;
|
|
|
const QDateTime now = QDateTime::currentDateTime();
|
|
|
const QFileInfoList directories = rootDirectory.entryInfoList(
|
|
|
QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
|
|
|
for(int i = 0; i < directories.size(); ++i) {
|
|
|
const QFileInfo& directoryInfo = directories[i];
|
|
|
if(!isOwnedAutoFitTemporaryDirectory(directoryInfo.absoluteFilePath())) {
|
|
|
continue;
|
|
|
}
|
|
|
if(directoryInfo.lastModified().secsTo(now) < maximumAgeSeconds) {
|
|
|
continue;
|
|
|
}
|
|
|
if(isAutoFitRunProcessActive(directoryInfo.fileName())) {
|
|
|
continue;
|
|
|
}
|
|
|
if(!removeAutoFitTemporaryDirectory(directoryInfo.absoluteFilePath())) {
|
|
|
qWarning() << "Failed to remove stale auto-fit directory:"
|
|
|
<< directoryInfo.absoluteFilePath();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QMutex* nmCalculationUtils::getHxNwtmDllMutex()
|
|
|
{
|
|
|
return &s_oHxNwtmDllMutex;
|
|
|
}
|
|
|
|
|
|
void nmCalculationUtils::cleanupPebiGridDebugFiles()
|
|
|
{
|
|
|
const QDir workingDirectory(QDir::currentPath());
|
|
|
QStringList fileNames;
|
|
|
fileNames << "TRI_cell.csv" << "PEBI_cell.csv";
|
|
|
|
|
|
for(int i = 0; i < fileNames.size(); ++i) {
|
|
|
const QString filePath = workingDirectory.absoluteFilePath(fileNames[i]);
|
|
|
if(QFile::exists(filePath) && !QFile::remove(filePath)) {
|
|
|
qWarning() << "Failed to remove PEBI grid debug file:" << filePath;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
double nmCalculationUtils::milliDarcyToDarcy(double dPermeabilityMilliDarcy)
|
|
|
{
|
|
|
// PEBI输入结构使用D,数据层统一使用mD,因此仅在求解器边界换算。
|
|
|
return dPermeabilityMilliDarcy / 1000.0;
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::copyFileToDir(QString filePath, QString destDir) {
|
|
|
// 如果文件不存在,则退出
|
|
|
if(!QFile::exists(filePath)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 如果目标目录不存在,则创建
|
|
|
if(!QDir(destDir).exists()) {
|
|
|
if(!QDir().mkdir(destDir)) {
|
|
|
qWarning() << "cannot mkdir:" << destDir;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 获取源文件的文件名
|
|
|
QString fileName = QFileInfo(filePath).fileName();
|
|
|
// 构造目标文件的完整路径
|
|
|
QString destinationFilePath = QDir(destDir).filePath(fileName);
|
|
|
|
|
|
// 如果目标文件已存在,则删除
|
|
|
if(QFile::exists(destinationFilePath)) {
|
|
|
QFile destinationFile(destinationFilePath);
|
|
|
|
|
|
if(!destinationFile.remove()) {
|
|
|
qWarning() << "cannot delete file:" << destinationFilePath;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 拷贝文件
|
|
|
if(QFile::copy(filePath, destinationFilePath)) {
|
|
|
qDebug() << "file copy success:" << destinationFilePath;
|
|
|
return true;
|
|
|
} else {
|
|
|
qWarning() << "file copy failed:" << destinationFilePath;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::createDirectoryIfNotExists(QString directoryPath) {
|
|
|
QDir dir(directoryPath);
|
|
|
|
|
|
// 检查目录是否存在
|
|
|
if(dir.exists()) {
|
|
|
// 目录已存在
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 尝试创建目录
|
|
|
if(dir.mkpath(directoryPath)) {
|
|
|
// 创建成功
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 创建失败
|
|
|
qDebug() << "创建目录失败,错误代码。";
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::copyFile(const std::string& sourcePath, const std::string& destinationPath) {
|
|
|
std::ifstream src(sourcePath, std::ios::binary); // 以二进制模式打开源文件
|
|
|
std::ofstream dst(destinationPath, std::ios::binary); // 以二进制模式打开目标文件
|
|
|
|
|
|
if(!src || !dst) {
|
|
|
std::cerr << "文件打开失败" << std::endl;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
dst << src.rdbuf(); // 拷贝文件内容
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::removeDirectory(const QString &dirPath) {
|
|
|
QDir dir(dirPath);
|
|
|
|
|
|
if(!dir.exists()) {
|
|
|
qDebug() << "Directory does not exist.";
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 获取目录中的所有文件和目录
|
|
|
QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden);
|
|
|
|
|
|
foreach(QFileInfo fileInfo, fileList) {
|
|
|
if(fileInfo.isDir()) {
|
|
|
// 递归删除子目录
|
|
|
if(!removeDirectory(fileInfo.absoluteFilePath())) {
|
|
|
return false;
|
|
|
}
|
|
|
} else {
|
|
|
// 删除文件
|
|
|
if(!QFile::remove(fileInfo.absoluteFilePath())) {
|
|
|
qDebug() << "Failed to remove file:" << fileInfo.absoluteFilePath();
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 删除目录自身
|
|
|
if(!dir.rmdir(dirPath)) {
|
|
|
qDebug() << "Failed to remove directory:" << dirPath;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::writeFile(const QStringList& content, const QString &filePath) {
|
|
|
QFile file(filePath);
|
|
|
|
|
|
//检查
|
|
|
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
|
qWarning("Cannot open file for writing: %s", qPrintable(file.errorString()));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
QTextStream out(&file);
|
|
|
out << content.join("\n");
|
|
|
file.close();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
QStringList nmCalculationUtils::readFile(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;
|
|
|
}
|
|
|
|
|
|
bool nmCalculationUtils::calculateKriging(
|
|
|
const QVector<QPointF>& targetPoints,
|
|
|
const QVector<QPointF>& measurementPoints,
|
|
|
const QVector<double>& measurementValues,
|
|
|
double nugget,
|
|
|
double sill,
|
|
|
double range,
|
|
|
int model,
|
|
|
const QString& licensePath,
|
|
|
QVector<double>& outputValues,
|
|
|
QString* errorMessage,
|
|
|
const QAtomicInt* pCancelRequested)
|
|
|
{
|
|
|
outputValues.clear();
|
|
|
setKrigingError(errorMessage, QString());
|
|
|
|
|
|
if(isKrigingCancellationRequested(pCancelRequested)) {
|
|
|
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(targetPoints.isEmpty()) {
|
|
|
setKrigingError(errorMessage, krigingText("No interpolation points are available."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(measurementPoints.size() < 2) {
|
|
|
setKrigingError(errorMessage,
|
|
|
krigingText("At least two measurement points are required."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(measurementPoints.size() != measurementValues.size()) {
|
|
|
setKrigingError(errorMessage,
|
|
|
krigingText("Measurement point coordinates and values do not match."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(!isFiniteValue(nugget) || !isFiniteValue(sill) ||
|
|
|
!isFiniteValue(range) || nugget < 0.0 || sill <= 0.0 || range <= 0.0) {
|
|
|
setKrigingError(errorMessage, krigingText("Kriging parameters are invalid."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(model < 0 || model > 2) {
|
|
|
setKrigingError(errorMessage, krigingText("The Kriging model is invalid."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
for(int i = 0; i < targetPoints.size(); ++i) {
|
|
|
if((i % 256) == 0 &&
|
|
|
isKrigingCancellationRequested(pCancelRequested)) {
|
|
|
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
|
|
|
return false;
|
|
|
}
|
|
|
if(!isFiniteValue(targetPoints[i].x()) || !isFiniteValue(targetPoints[i].y())) {
|
|
|
setKrigingError(errorMessage,
|
|
|
krigingText("An interpolation point contains an invalid coordinate."));
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for(int i = 0; i < measurementPoints.size(); ++i) {
|
|
|
if(isKrigingCancellationRequested(pCancelRequested)) {
|
|
|
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
|
|
|
return false;
|
|
|
}
|
|
|
if(!isFiniteValue(measurementPoints[i].x()) ||
|
|
|
!isFiniteValue(measurementPoints[i].y()) ||
|
|
|
!isFiniteValue(measurementValues[i])) {
|
|
|
setKrigingError(errorMessage,
|
|
|
krigingText("A measurement point contains invalid data."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
for(int j = 0; j < i; ++j) {
|
|
|
if(measurementPoints[i] == measurementPoints[j]) {
|
|
|
setKrigingError(errorMessage,
|
|
|
krigingText("Measurement point coordinates cannot be duplicated."));
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(licensePath.isEmpty() || !QFileInfo(licensePath).exists()) {
|
|
|
setKrigingError(errorMessage, krigingText("The solver license file was not found."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// Kriging 与建网、模型求解来自同一个 DLL,不能在不同线程中并发进入。
|
|
|
nmKrigingMutexLocker oDllLocker;
|
|
|
if(!oDllLocker.lock(getHxNwtmDllMutex(), pCancelRequested)) {
|
|
|
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
|
|
|
return false;
|
|
|
}
|
|
|
HMODULE dll = LoadLibrary(L"HX_NWTM.dll");
|
|
|
if(dll == NULL) {
|
|
|
setKrigingError(errorMessage, krigingText("Failed to load HX_NWTM.dll."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
typedef void (*HX_NWTM_KRINGING_Func)(HX_KRING_OUTPUT&,
|
|
|
const HX_KRING_INPUT,
|
|
|
std::string);
|
|
|
HX_NWTM_KRINGING_Func krigingFunction =
|
|
|
(HX_NWTM_KRINGING_Func)GetProcAddress(dll, "HX_NWTM_KRINGING");
|
|
|
if(krigingFunction == NULL) {
|
|
|
FreeLibrary(dll);
|
|
|
setKrigingError(errorMessage,
|
|
|
krigingText("The Kriging interface was not found in HX_NWTM.dll."));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 求解器p参数每行保存{x, y},并保持targetPoints的原始顺序.
|
|
|
dVec2 targetData;
|
|
|
targetData.reserve(targetPoints.size());
|
|
|
for(int i = 0; i < targetPoints.size(); ++i) {
|
|
|
dVec1 point(2, 0.0);
|
|
|
point[0] = targetPoints[i].x();
|
|
|
point[1] = targetPoints[i].y();
|
|
|
targetData.push_back(point);
|
|
|
}
|
|
|
|
|
|
// 求解器v参数每行保存{x, y, value},坐标和值按相同下标配对.
|
|
|
dVec2 measurementData;
|
|
|
measurementData.reserve(measurementPoints.size());
|
|
|
for(int i = 0; i < measurementPoints.size(); ++i) {
|
|
|
dVec1 point(3, 0.0);
|
|
|
point[0] = measurementPoints[i].x();
|
|
|
point[1] = measurementPoints[i].y();
|
|
|
point[2] = measurementValues[i];
|
|
|
measurementData.push_back(point);
|
|
|
}
|
|
|
|
|
|
bool calculationSucceeded = true;
|
|
|
QString calculationError;
|
|
|
|
|
|
// 局部作用域保证求解器输入、输出对象在FreeLibrary前析构.
|
|
|
{
|
|
|
HX_KRING_INPUT input(nugget, sill, range, model,
|
|
|
targetData, measurementData);
|
|
|
HX_KRING_OUTPUT output;
|
|
|
|
|
|
try {
|
|
|
krigingFunction(output, input, licensePath.toStdString());
|
|
|
} catch(const std::exception& exception) {
|
|
|
calculationSucceeded = false;
|
|
|
calculationError = krigingText("Kriging calculation failed: %1")
|
|
|
.arg(QString::fromLocal8Bit(exception.what()));
|
|
|
} catch(...) {
|
|
|
calculationSucceeded = false;
|
|
|
calculationError = krigingText("Kriging calculation failed.");
|
|
|
}
|
|
|
|
|
|
// DLL 无取消入口;调用期间收到停止请求时只丢弃返回值,不提交插值结果。
|
|
|
if(calculationSucceeded &&
|
|
|
isKrigingCancellationRequested(pCancelRequested)) {
|
|
|
calculationSucceeded = false;
|
|
|
calculationError = krigingText("Kriging calculation was cancelled.");
|
|
|
}
|
|
|
|
|
|
if(calculationSucceeded &&
|
|
|
output.v.size() != static_cast<size_t>(targetPoints.size())) {
|
|
|
calculationSucceeded = false;
|
|
|
calculationError = krigingText(
|
|
|
"The Kriging result count does not match the interpolation points.");
|
|
|
}
|
|
|
|
|
|
if(calculationSucceeded) {
|
|
|
outputValues.reserve(targetPoints.size());
|
|
|
for(size_t i = 0; i < output.v.size(); ++i) {
|
|
|
if((i % 256) == 0 &&
|
|
|
isKrigingCancellationRequested(pCancelRequested)) {
|
|
|
calculationSucceeded = false;
|
|
|
calculationError = krigingText("Kriging calculation was cancelled.");
|
|
|
break;
|
|
|
}
|
|
|
if(!isFiniteValue(output.v[i])) {
|
|
|
calculationSucceeded = false;
|
|
|
calculationError = krigingText(
|
|
|
"The Kriging result contains an invalid value.");
|
|
|
break;
|
|
|
}
|
|
|
outputValues.append(output.v[i]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
FreeLibrary(dll);
|
|
|
if(!calculationSucceeded) {
|
|
|
outputValues.clear();
|
|
|
setKrigingError(errorMessage, calculationError);
|
|
|
}
|
|
|
return calculationSucceeded;
|
|
|
}
|