Files
EJM_Display/DataCenter/OpcUaManager.cpp

382 lines
12 KiB
C++
Raw Normal View History

2025-08-20 23:06:28 +08:00
#include "OpcUaManager.h"
#include <QOpcUaProvider>
#include <QMap>
#include <QDebug>
2025-09-15 22:28:43 +08:00
#include <QDateTime>
#include <GlobalDefinitions/Variable.h>
OpcUaManager* OpcUaManager::m_instance = nullptr;
QMutex OpcUaManager::m_mutex;
2025-08-20 23:06:28 +08:00
OpcUaManager::OpcUaManager(QObject *parent)
: QObject(parent)
, mReconnectTimer(new QTimer(this))
{
2025-09-15 22:28:43 +08:00
connect(mReconnectTimer, &QTimer::timeout, this, &OpcUaManager::tryReconnect);
mReconnectTimer->setInterval(RECONNECT_INTERVAL);
}
OpcUaManager* OpcUaManager::instance(QObject *parent)
{
if (!m_instance) {
QMutexLocker locker(&m_mutex);
if (!m_instance) {
m_instance = new OpcUaManager(parent);
}
}
return m_instance;
2025-08-20 23:06:28 +08:00
}
OpcUaManager::~OpcUaManager()
{
disconnectFromServer();
2025-09-15 22:28:43 +08:00
clearNodeCache();
2025-08-20 23:06:28 +08:00
if (mClient) {
delete mClient;
mClient = nullptr;
}
}
2025-09-15 22:28:43 +08:00
QOpcUaClient* OpcUaManager::getClient() const
{
return mClient;
}
2025-08-20 23:06:28 +08:00
bool OpcUaManager::connectToServer(const QString &url, const QString &username, const QString &password)
{
mServerUrl = QUrl(url);
mUserName = username;
mPassword = password;
if (mClient) {
disconnectFromServer();
delete mClient;
mClient = nullptr;
}
createClient();
if (!mClient) {
emit errorOccurred("无法创建OPC UA客户端");
onClientStateChanged(QOpcUaClient::Disconnected);
return false;
}
mClient->requestEndpoints(mServerUrl);
2025-09-15 22:28:43 +08:00
connect(mClient, &QOpcUaClient::readNodeAttributesFinished,this, &OpcUaManager::onBatchReadFinished);
2025-08-20 23:06:28 +08:00
return true;
}
void OpcUaManager::disconnectFromServer()
{
if (mClient) {
2025-09-15 22:28:43 +08:00
mClient->disconnectFromEndpoint();
2025-08-20 23:06:28 +08:00
}
2025-09-15 22:28:43 +08:00
clearNodeCache();
2025-08-20 23:06:28 +08:00
mCurrentNodeId.clear();
2025-09-15 22:28:43 +08:00
mPendingWriteValue = QVariant();
2025-08-20 23:06:28 +08:00
}
bool OpcUaManager::readNodeValue(const QString &nodeId)
{
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
2025-09-15 22:28:43 +08:00
qWarning()<<("客户端未连接");
2025-08-20 23:06:28 +08:00
return false;
}
2025-09-15 22:28:43 +08:00
QOpcUaNode* node = getCachedNode(nodeId);
if (!node) {
2025-08-20 23:06:28 +08:00
QString error = QString("无法获取节点: %1").arg(nodeId);
2025-09-15 22:28:43 +08:00
qWarning()<<(error);
2025-08-20 23:06:28 +08:00
return false;
}
2025-09-15 22:28:43 +08:00
mCurrentNodeId = nodeId;
disconnect(node, &QOpcUaNode::attributeRead, this, &OpcUaManager::onAttributeRead);
connect(node,
2025-08-20 23:06:28 +08:00
static_cast<void (QOpcUaNode::*)(QOpcUa::NodeAttributes)>(&QOpcUaNode::attributeRead),
this,
&OpcUaManager::onAttributeRead);
2025-09-15 22:28:43 +08:00
node->readAttributes(
QOpcUa::NodeAttribute::Value |
QOpcUa::NodeAttribute::AccessLevel
2025-08-20 23:06:28 +08:00
);
return true;
}
2025-09-15 22:28:43 +08:00
void OpcUaManager::onAttributeRead(QOpcUa::NodeAttributes attrs)
{
QOpcUaNode* node = qobject_cast<QOpcUaNode*>(sender());
if (!node) return;
QString nodeId = node->nodeId();
if (attrs.testFlag(QOpcUa::NodeAttribute::Value)) {
gOPC_NodeValue[nodeId] = node->attribute(QOpcUa::NodeAttribute::Value);
2025-08-20 23:06:28 +08:00
2025-09-15 22:28:43 +08:00
} else {
gOPC_NodeValue[nodeId] = "";
}
}
bool OpcUaManager::readNodesValue(const QList<QString>& nodeIds)
2025-08-20 23:06:28 +08:00
{
2025-09-15 22:28:43 +08:00
// 前置校验:客户端连接状态 + 节点列表非空 + 避免并发读取
2025-08-20 23:06:28 +08:00
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
2025-09-15 22:28:43 +08:00
qWarning()<<("批量读取失败OPC客户端未连接");
2025-08-20 23:06:28 +08:00
return false;
}
2025-09-15 22:28:43 +08:00
if (nodeIds.isEmpty()) {
qWarning()<<("批量读取失败:节点列表为空");
return false;
2025-08-20 23:06:28 +08:00
}
2025-09-15 22:28:43 +08:00
// 1. 构建批量读取请求正确构造QOpcUaReadItem
QVector<QOpcUaReadItem> readItems;
for (const QString& nodeId : nodeIds) {
QOpcUaReadItem item;
item.setNodeId(nodeId);
readItems.append(item);
2025-08-20 23:06:28 +08:00
}
2025-09-15 22:28:43 +08:00
// 发送批量读取请求
if(!mClient->readNodeAttributes(readItems)){
qDebug()<<("批量读取请求发送失败");
return false;
}
2025-08-20 23:06:28 +08:00
return true;
}
2025-09-15 22:28:43 +08:00
// 批量读取结果处理槽函数(参数需与信号匹配)
void OpcUaManager::onBatchReadFinished(const QVector<QOpcUaReadResult>& results)
{
// 遍历结果使用QOpcUaReadResult的方法获取数据
for (const auto& result : results) {
if (result.statusCode() == QOpcUa::Good) {
gOPC_NodeValue[result.nodeId()] = result.value();
emit readCompleted(result.value(), result.nodeId());
} else {
gOPC_NodeValue[result.nodeId()] = "";
}
}
}
//bool OpcUaManager::writeNodeValue(const QString &nodeId, const QVariant &value, int maxRetry)
//{
// if (!mClient || mClient->state() != QOpcUaClient::Connected) {
// emit errorOccurred("客户端未连接");
// return false;
// }
// QOpcUaNode* node = getCachedNode(nodeId);
// if (!node) {
// emit errorOccurred(QString("无法获取节点: %1").arg(nodeId));
// return false;
// }
// WriteRequest request;
// request.nodeId = nodeId;
// request.value = value;
// request.retryCount = maxRetry;
// mWriteRequests[node] = request;
// disconnect(node, &QOpcUaNode::attributeRead, this, &OpcUaManager::onPreWriteValueTypeRead);
// connect(node,
// static_cast<void (QOpcUaNode::*)(QOpcUa::NodeAttributes)>(&QOpcUaNode::attributeRead),
// this,
// &OpcUaManager::onPreWriteValueTypeRead);
// node->readAttributes(QOpcUa::NodeAttribute::Value);
// return writeNodeValue(nodeId,value,maxRetry,100);
//}
bool OpcUaManager::writeNodeValue(const QString &nodeId,
const QVariant &value,
int maxRetry /*= 3*/,
int retryIntervalMs /*= 200*/)
{
2025-10-16 17:40:00 +08:00
QString mNodeID = nodeId;
if(!mNodeID.contains("ns=6;s=::AsGlobalPV:"))
mNodeID = "ns=6;s=::AsGlobalPV:"+mNodeID;
2025-09-15 22:28:43 +08:00
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
2025-10-16 17:40:00 +08:00
qWarning().noquote() << "[OPC] 客户端未连接,写入" << mNodeID << "失败";
2025-09-15 22:28:43 +08:00
return false;
}
2025-10-16 17:40:00 +08:00
QOpcUaNode *node = mClient->node(mNodeID);
2025-09-15 22:28:43 +08:00
if (!node) {
2025-10-16 17:40:00 +08:00
qWarning().noquote() << "[OPC] 无法写入节点:" << mNodeID;
2025-09-15 22:28:43 +08:00
return false;
}
for (int attempt = 1; attempt <= maxRetry; ++attempt) {
/* ---------- 1. 先读当前值 ---------- */
2025-10-16 17:40:00 +08:00
QVariant cur = gOPC_NodeValue[mNodeID];
2025-09-15 22:28:43 +08:00
if (!cur.isValid()) {
2025-10-16 17:40:00 +08:00
//qWarning().noquote() << "读到无效值:" << mNodeID<<gOPC_NodeValue[mNodeID];
2025-09-15 22:28:43 +08:00
}
/* ---------- 2. 类型转换 ---------- */
QVariant v = value;
QOpcUa::Types typeHint = QOpcUa::Types::Undefined;
QString tn = cur.typeName();
if (tn == "int") { v.convert(QMetaType::Int); typeHint = QOpcUa::Types::Int32; }
else if (tn == "float") { v.convert(QMetaType::Float); typeHint = QOpcUa::Types::Float; }
else if (tn == "double"){ v.convert(QMetaType::Double); typeHint = QOpcUa::Types::Double; }
else if (tn == "ushort"){ v.convert(QMetaType::UShort); typeHint = QOpcUa::Types::UInt16; }
/* ---------- 3. 同步写 ---------- */
struct WriteWait : QObject {
bool ok = false;
QEventLoop loop;
} waiter;
connect(node,
static_cast<void(QOpcUaNode::*)(QOpcUa::NodeAttribute, QOpcUa::UaStatusCode)>(
&QOpcUaNode::attributeWritten),
&waiter, [&waiter](QOpcUa::NodeAttribute, QOpcUa::UaStatusCode code) mutable {
waiter.ok = (code == QOpcUa::UaStatusCode::Good);
waiter.loop.quit();
}, Qt::QueuedConnection);
node->writeAttribute(QOpcUa::NodeAttribute::Value, v, typeHint);
QTimer::singleShot(2000, &waiter.loop, &QEventLoop::quit);
waiter.loop.exec();
if (waiter.ok) {
return true;
}
/* ---------- 4. 失败日志 & 重试 ---------- */
// qWarning().noquote() << "[OPC] 第" << attempt << "/" << maxRetry
2025-10-16 17:40:00 +08:00
// << "次写入失败:" << mNodeID << " 等待重试 ...";
2025-09-15 22:28:43 +08:00
if (attempt < maxRetry)
QThread::msleep(retryIntervalMs);
}
2025-10-16 17:40:00 +08:00
qCritical().noquote() << "[OPC] 最终写入失败:" << mNodeID;
2025-09-15 22:28:43 +08:00
return false;
}
2025-08-20 23:06:28 +08:00
QOpcUaClient::ClientState OpcUaManager::connectionState() const
{
if (mClient) {
return mClient->state();
}
return QOpcUaClient::Disconnected;
}
void OpcUaManager::createClient()
{
QOpcUaProvider provider;
QMap<QString, QVariant> clientParams;
// 设置认证信息
if (!mUserName.isEmpty() && !mPassword.isEmpty()) {
clientParams["username"] = mUserName;
clientParams["password"] = mPassword;
}
2025-09-15 22:28:43 +08:00
// 禁用安全策略(根据实际需求调整)
2025-08-20 23:06:28 +08:00
clientParams["securityMode"] = "None";
clientParams["securityPolicy"] = "None";
2025-09-15 22:28:43 +08:00
// 性能参数设置
clientParams["requestTimeout"] = 5000;
clientParams["maxArrayLength"] = 10000;
// 创建客户端使用open62541后端
2025-08-20 23:06:28 +08:00
mClient = provider.createClient("open62541", clientParams);
if (!mClient) {
return;
}
mClient->setParent(this);
// 连接客户端信号
connect(mClient,
static_cast<void (QOpcUaClient::*)(QVector<QOpcUaEndpointDescription>,
QOpcUa::UaStatusCode,
QUrl)>(&QOpcUaClient::endpointsRequestFinished),
this,
&OpcUaManager::onEndpointsRequestFinished);
connect(mClient,
static_cast<void (QOpcUaClient::*)(QOpcUaClient::ClientState)>(&QOpcUaClient::stateChanged),
this,
&OpcUaManager::onClientStateChanged);
}
2025-09-15 22:28:43 +08:00
2025-08-20 23:06:28 +08:00
void OpcUaManager::tryReconnect()
{
if (mClient && mClient->state() == QOpcUaClient::Disconnected) {
2025-09-15 22:28:43 +08:00
qint64 now = QDateTime::currentMSecsSinceEpoch();
// 限制重连频率
if (now - mLastReconnectAttempt > 1000) {
mLastReconnectAttempt = now;
qDebug() << "正在执行第" << mReconnectCount << "次重连...";
connectToServer(mServerUrl.toString(), mUserName, mPassword);
mReconnectCount++;
// 重连次数过多时增加间隔时间
if (mReconnectCount > 10) {
mReconnectTimer->setInterval(RECONNECT_INTERVAL * 2);
}
}
2025-08-20 23:06:28 +08:00
}
}
2025-09-15 22:28:43 +08:00
2025-08-20 23:06:28 +08:00
void OpcUaManager::onEndpointsRequestFinished(QVector<QOpcUaEndpointDescription> endpoints,
QOpcUa::UaStatusCode statusCode,
QUrl requestedUrl)
{
Q_UNUSED(requestedUrl);
if (statusCode != QOpcUa::UaStatusCode::Good || endpoints.isEmpty()) {
QString error = QString("获取端点失败,状态码: %1").arg(static_cast<int>(statusCode));
emit errorOccurred(error);
mReconnectTimer->start(RECONNECT_INTERVAL);
return;
}
mClient->connectToEndpoint(endpoints.first());
}
void OpcUaManager::onClientStateChanged(QOpcUaClient::ClientState state)
{
2025-09-15 22:28:43 +08:00
emit stateChanged(state);
2025-08-20 23:06:28 +08:00
if (state == QOpcUaClient::Disconnected) {
2025-09-15 22:28:43 +08:00
qDebug() << "OPC UA 连接断开," << RECONNECT_INTERVAL << "ms 后自动重连...";
2025-08-20 23:06:28 +08:00
mReconnectTimer->start(RECONNECT_INTERVAL);
} else if (state == QOpcUaClient::Connected) {
mReconnectTimer->stop();
2025-09-15 22:28:43 +08:00
mReconnectCount = 0; // 重置计数器
mReconnectTimer->setInterval(RECONNECT_INTERVAL); // 重置间隔
emit reconnected();
2025-08-20 23:06:28 +08:00
}
}
2025-09-15 22:28:43 +08:00
QOpcUaNode* OpcUaManager::getCachedNode(const QString& nodeId)
2025-08-20 23:06:28 +08:00
{
2025-09-15 22:28:43 +08:00
if (mNodeCache.contains(nodeId)) {
return mNodeCache[nodeId];
2025-08-20 23:06:28 +08:00
}
2025-09-15 22:28:43 +08:00
if (!mClient) return nullptr;
2025-08-20 23:06:28 +08:00
2025-09-15 22:28:43 +08:00
QOpcUaNode* node = mClient->node(nodeId);
if (node) {
node->setParent(this);
mNodeCache[nodeId] = node;
2025-08-20 23:06:28 +08:00
}
2025-09-15 22:28:43 +08:00
return node;
2025-08-20 23:06:28 +08:00
}
2025-09-15 22:28:43 +08:00
void OpcUaManager::clearNodeCache()
2025-08-20 23:06:28 +08:00
{
2025-09-15 22:28:43 +08:00
qDeleteAll(mNodeCache.values());
mNodeCache.clear();
mWriteRequests.clear();
2025-08-20 23:06:28 +08:00
}