initial commit

This commit is contained in:
2026-04-07 20:55:30 +08:00
commit 81d1fb7856
84 changed files with 11929 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
#include "net/ModelServerClient.h"
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QTimer>
#include <QUrl>
namespace core {
ModelServerClient::ModelServerClient(QObject* parent)
: QObject(parent)
, m_nam(new QNetworkAccessManager(this)) {
}
void ModelServerClient::setBaseUrl(const QUrl& baseUrl) {
m_baseUrl = baseUrl;
}
QUrl ModelServerClient::baseUrl() const {
return m_baseUrl;
}
QNetworkReply* ModelServerClient::computeDepthPng8Async(const QByteArray& imageBytes, QString* outImmediateError) {
if (outImmediateError) {
outImmediateError->clear();
}
if (!m_baseUrl.isValid() || m_baseUrl.isEmpty()) {
if (outImmediateError) *outImmediateError = QStringLiteral("后端地址无效。");
return nullptr;
}
if (imageBytes.isEmpty()) {
if (outImmediateError) *outImmediateError = QStringLiteral("输入图像为空。");
return nullptr;
}
const QUrl url = m_baseUrl.resolved(QUrl(QStringLiteral("/depth")));
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
const QByteArray imageB64 = imageBytes.toBase64();
const QJsonObject payload{
{QStringLiteral("image_b64"), QString::fromLatin1(imageB64)},
};
const QByteArray body = QJsonDocument(payload).toJson(QJsonDocument::Compact);
return m_nam->post(req, body);
}
bool ModelServerClient::computeDepthPng8(
const QByteArray& imageBytes,
QByteArray& outPngBytes,
QString& outError,
int timeoutMs
) {
outPngBytes.clear();
outError.clear();
if (!m_baseUrl.isValid() || m_baseUrl.isEmpty()) {
outError = QStringLiteral("后端地址无效。");
return false;
}
if (imageBytes.isEmpty()) {
outError = QStringLiteral("输入图像为空。");
return false;
}
const QUrl url = m_baseUrl.resolved(QUrl(QStringLiteral("/depth")));
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
const QByteArray imageB64 = imageBytes.toBase64();
const QJsonObject payload{
{QStringLiteral("image_b64"), QString::fromLatin1(imageB64)},
};
const QByteArray body = QJsonDocument(payload).toJson(QJsonDocument::Compact);
QNetworkReply* reply = m_nam->post(req, body);
if (!reply) {
outError = QStringLiteral("创建网络请求失败。");
return false;
}
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
const int t = (timeoutMs <= 0) ? 30000 : timeoutMs;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start(t);
loop.exec();
if (timer.isActive() == false && reply->isFinished() == false) {
reply->abort();
reply->deleteLater();
outError = QStringLiteral("请求超时(%1ms").arg(t);
return false;
}
const int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
const QByteArray raw = reply->readAll();
const auto netErr = reply->error();
const QString netErrStr = reply->errorString();
reply->deleteLater();
if (netErr != QNetworkReply::NoError) {
outError = QStringLiteral("网络错误:%1").arg(netErrStr);
return false;
}
if (httpStatus != 200) {
// FastAPI HTTPException 默认返回 {"detail": "..."}
QString detail;
const QJsonDocument jd = QJsonDocument::fromJson(raw);
if (jd.isObject()) {
const auto obj = jd.object();
detail = obj.value(QStringLiteral("detail")).toString();
}
outError = detail.isEmpty()
? QStringLiteral("后端返回HTTP %1。").arg(httpStatus)
: QStringLiteral("后端错误HTTP %1%2").arg(httpStatus).arg(detail);
return false;
}
if (raw.isEmpty()) {
outError = QStringLiteral("后端返回空数据。");
return false;
}
outPngBytes = raw;
return true;
}
} // namespace core

View File

@@ -0,0 +1,36 @@
#pragma once
#include <QByteArray>
#include <QObject>
#include <QString>
#include <QUrl>
#include <QNetworkReply>
class QNetworkAccessManager;
class QUrl;
class QNetworkReply;
namespace core {
class ModelServerClient final : public QObject {
Q_OBJECT
public:
explicit ModelServerClient(QObject* parent = nullptr);
void setBaseUrl(const QUrl& baseUrl);
QUrl baseUrl() const;
// 同步调用:向后端 POST /depth 发送背景图,成功返回 PNG(8-bit 灰度) 的二进制数据。
// timeoutMs<=0 表示使用默认超时30s
bool computeDepthPng8(const QByteArray& imageBytes, QByteArray& outPngBytes, QString& outError, int timeoutMs = 30000);
// 异步调用:发起 POST /depth返回 reply由 Qt 管理生命周期;调用方负责连接 finished/错误处理)。
// 返回 nullptr 表示参数/URL 非法导致无法发起。
QNetworkReply* computeDepthPng8Async(const QByteArray& imageBytes, QString* outImmediateError = nullptr);
private:
QNetworkAccessManager* m_nam = nullptr;
QUrl m_baseUrl;
};
} // namespace core