261 lines
7.5 KiB
C++
261 lines
7.5 KiB
C++
// HttpAdapter.cpp
|
||
#include "httpChannel.h"
|
||
#include <QNetworkRequest>
|
||
#include <QNetworkProxy>
|
||
#include <QJsonDocument>
|
||
#include <QJsonObject>
|
||
#include <QJsonArray>
|
||
#include <QTimer>
|
||
#include <QDebug>
|
||
|
||
HttpAdapter::HttpAdapter(const ChannelConfig& config, QObject* parent)
|
||
: CommunicationChannel(config, parent)
|
||
, m_networkManager(new QNetworkAccessManager(this))
|
||
{
|
||
// 配置SSL
|
||
if (m_config.sslConfig.isNull()) {
|
||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||
m_config.sslConfig = sslConfig;
|
||
}
|
||
|
||
// 连接信号
|
||
connect(m_networkManager, &QNetworkAccessManager::authenticationRequired,
|
||
this, &HttpAdapter::onAuthenticationRequired);
|
||
|
||
qDebug() << "HttpAdapter created for" << m_config.channelId;
|
||
}
|
||
|
||
HttpAdapter::~HttpAdapter()
|
||
{
|
||
// 取消所有未完成的请求
|
||
for (auto reply : m_pendingRequests.keys()) {
|
||
reply->abort();
|
||
reply->deleteLater();
|
||
}
|
||
m_pendingRequests.clear();
|
||
}
|
||
|
||
bool HttpAdapter::connectToHost()
|
||
{
|
||
// HTTP协议无需建立持久连接,只需验证端点是否可达
|
||
if (m_config.endpoint.isEmpty() || !m_config.endpoint.isValid()) {
|
||
emit errorOccurred("Invalid endpoint URL: " + m_config.endpoint.toString());
|
||
return false;
|
||
}
|
||
|
||
// 发送一个测试请求验证连接
|
||
HttpRequest testRequest;
|
||
testRequest.method = GET;
|
||
|
||
QNetworkRequest request(m_config.endpoint);
|
||
request.setSslConfiguration(m_config.sslConfig);
|
||
request.setRawHeader("User-Agent", "PowerSCADA/1.0");
|
||
|
||
QNetworkReply* reply = m_networkManager->get(request);
|
||
connect(reply, &QNetworkReply::finished,
|
||
this, [this, reply]() {
|
||
onReplyFinished(reply);
|
||
});
|
||
|
||
m_pendingRequests.insert(reply, testRequest);
|
||
|
||
// 设置超时
|
||
QTimer::singleShot(m_config.timeout, this, [this, reply]() {
|
||
if (reply && reply->isRunning()) {
|
||
reply->abort();
|
||
emit errorOccurred("Connection timeout");
|
||
}
|
||
});
|
||
|
||
emit connected();
|
||
return true;
|
||
}
|
||
|
||
bool HttpAdapter::disconnectFromHost()
|
||
{
|
||
// 取消所有未完成的请求
|
||
for (auto reply : m_pendingRequests.keys()) {
|
||
reply->abort();
|
||
reply->deleteLater();
|
||
}
|
||
m_pendingRequests.clear();
|
||
|
||
emit disconnected();
|
||
return true;
|
||
}
|
||
|
||
bool HttpAdapter::sendData(const QByteArray& data)
|
||
{
|
||
HttpRequest request;
|
||
request.method = POST;
|
||
request.data = data;
|
||
request.contentType = "application/octet-stream";
|
||
|
||
return sendRequest(request);
|
||
}
|
||
|
||
bool HttpAdapter::isConnected() const
|
||
{
|
||
// HTTP是无状态协议,总是返回true表示可以发送请求
|
||
return m_config.endpoint.isValid();
|
||
}
|
||
|
||
bool HttpAdapter::sendRequest(const HttpRequest& request)
|
||
{
|
||
if (!m_config.endpoint.isValid()) {
|
||
emit errorOccurred("Invalid endpoint URL");
|
||
return false;
|
||
}
|
||
|
||
QNetworkRequest networkRequest(m_config.endpoint);
|
||
networkRequest.setSslConfiguration(m_config.sslConfig);
|
||
networkRequest.setRawHeader("User-Agent", "PowerSCADA/1.0");
|
||
networkRequest.setRawHeader("Content-Type", request.contentType.toUtf8());
|
||
|
||
// 添加自定义头
|
||
for (auto it = request.headers.begin(); it != request.headers.end(); ++it) {
|
||
networkRequest.setRawHeader(it.key().toUtf8(), it.value().toUtf8());
|
||
}
|
||
|
||
QNetworkReply* reply = nullptr;
|
||
|
||
switch (request.method) {
|
||
case GET:
|
||
reply = m_networkManager->get(networkRequest);
|
||
break;
|
||
case POST:
|
||
reply = m_networkManager->post(networkRequest, request.data);
|
||
break;
|
||
case PUT:
|
||
reply = m_networkManager->put(networkRequest, request.data);
|
||
break;
|
||
case DELETE:
|
||
reply = m_networkManager->deleteResource(networkRequest);
|
||
break;
|
||
default:
|
||
emit errorOccurred("Unsupported HTTP method");
|
||
return false;
|
||
}
|
||
|
||
if (!reply) {
|
||
emit errorOccurred("Failed to create network request");
|
||
return false;
|
||
}
|
||
|
||
// 设置请求超时
|
||
QTimer* timeoutTimer = new QTimer(this);
|
||
timeoutTimer->setSingleShot(true);
|
||
connect(timeoutTimer, &QTimer::timeout, this, [this, reply]() {
|
||
if (reply && reply->isRunning()) {
|
||
reply->abort();
|
||
emit errorOccurred("Request timeout");
|
||
}
|
||
});
|
||
timeoutTimer->start(m_config.timeout);
|
||
|
||
connect(reply, &QNetworkReply::finished, this, [this, reply, timeoutTimer]() {
|
||
timeoutTimer->stop();
|
||
timeoutTimer->deleteLater();
|
||
onReplyFinished(reply);
|
||
});
|
||
|
||
m_pendingRequests.insert(reply, request);
|
||
return true;
|
||
}
|
||
|
||
void HttpAdapter::setAuthentication(const QString& username, const QString& password)
|
||
{
|
||
m_username = username;
|
||
m_password = password;
|
||
}
|
||
|
||
void HttpAdapter::setProxy(const QNetworkProxy& proxy)
|
||
{
|
||
m_networkManager->setProxy(proxy);
|
||
}
|
||
|
||
bool HttpAdapter::readDataPoints(const QStringList& pointIds)
|
||
{
|
||
QJsonObject requestObj;
|
||
requestObj["command"] = "read";
|
||
requestObj["points"] = QJsonArray::fromStringList(pointIds);
|
||
requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
|
||
|
||
HttpRequest request;
|
||
request.method = POST;
|
||
request.data = QJsonDocument(requestObj).toJson();
|
||
request.contentType = "application/json";
|
||
|
||
return sendRequest(request);
|
||
}
|
||
|
||
bool HttpAdapter::writeDataPoint(const QString& pointId, const QVariant& value)
|
||
{
|
||
QJsonObject requestObj;
|
||
requestObj["command"] = "write";
|
||
requestObj["point"] = pointId;
|
||
requestObj["value"] = QJsonValue::fromVariant(value);
|
||
requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
|
||
|
||
HttpRequest request;
|
||
request.method = POST;
|
||
request.data = QJsonDocument(requestObj).toJson();
|
||
request.contentType = "application/json";
|
||
|
||
return sendRequest(request);
|
||
}
|
||
|
||
bool HttpAdapter::sendControlCommand(const QString& deviceId, int command, const QVariant& param)
|
||
{
|
||
QJsonObject requestObj;
|
||
requestObj["command"] = "control";
|
||
requestObj["device"] = deviceId;
|
||
requestObj["cmd"] = command;
|
||
requestObj["param"] = QJsonValue::fromVariant(param);
|
||
requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
|
||
|
||
HttpRequest request;
|
||
request.method = POST;
|
||
request.data = QJsonDocument(requestObj).toJson();
|
||
request.contentType = "application/json";
|
||
|
||
return sendRequest(request);
|
||
}
|
||
|
||
void HttpAdapter::onReplyFinished(QNetworkReply* reply)
|
||
{
|
||
if (!reply || !m_pendingRequests.contains(reply)) {
|
||
return;
|
||
}
|
||
|
||
auto request = m_pendingRequests.take(reply);
|
||
|
||
if (reply->error() != QNetworkReply::NoError) {
|
||
QString errorMsg = QString("HTTP error: %1 - %2")
|
||
.arg(reply->error())
|
||
.arg(reply->errorString());
|
||
emit errorOccurred(errorMsg);
|
||
} else {
|
||
QByteArray responseData = reply->readAll();
|
||
emit dataReceived(responseData);
|
||
|
||
// 记录日志
|
||
qDebug() << "HTTP response received:" << reply->url().toString()
|
||
<< "Status:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()
|
||
<< "Size:" << responseData.size() << "bytes";
|
||
}
|
||
|
||
reply->deleteLater();
|
||
}
|
||
|
||
void HttpAdapter::onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator)
|
||
{
|
||
if (!m_username.isEmpty() && !m_password.isEmpty()) {
|
||
authenticator->setUser(m_username);
|
||
authenticator->setPassword(m_password);
|
||
} else {
|
||
qWarning() << "Authentication required but no credentials provided";
|
||
}
|
||
}
|