更新UI
This commit is contained in:
@@ -96,18 +96,21 @@ void DataCenter::addMonitoredNode(const QString &nodeId, const QString &nodeName
|
||||
const QString &varName, const QString &tableName,
|
||||
const QString &fieldName)
|
||||
{
|
||||
if (gOPC_NodeName.contains(nodeId)) {
|
||||
qWarning() << "节点已存在:" << nodeId;
|
||||
QString mNodeID = nodeId;
|
||||
if(!mNodeID.contains("ns=6;s=::AsGlobalPV:"))
|
||||
mNodeID = "ns=6;s=::AsGlobalPV:"+mNodeID;
|
||||
if (gOPC_NodeName.contains(mNodeID)) {
|
||||
qWarning() << "节点已存在:" << mNodeID;
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用全局变量存储
|
||||
gOPC_NodeName[nodeId] = nodeName.isEmpty() ? nodeId : nodeName;
|
||||
gOPC_VarName[nodeId] = varName.isEmpty() ? nodeId : varName;
|
||||
gOPC_SqlTable[nodeId] = tableName;
|
||||
gOPC_SqlField[nodeId] = fieldName;
|
||||
gOPC_NodeList.append(nodeId);
|
||||
gOPC_NodeValue[nodeId] = QVariant();
|
||||
gOPC_NodeName[mNodeID] = nodeName.isEmpty() ? mNodeID : nodeName;
|
||||
gOPC_VarName[mNodeID] = varName.isEmpty() ? mNodeID : varName;
|
||||
gOPC_SqlTable[mNodeID] = tableName;
|
||||
gOPC_SqlField[mNodeID] = fieldName;
|
||||
gOPC_NodeList.append(mNodeID);
|
||||
gOPC_NodeValue[mNodeID] = QVariant();
|
||||
}
|
||||
|
||||
void DataCenter::browseRecursive(const QString &nodeId)
|
||||
|
||||
231
DataCenter/DataCenter.cpp.bak
Normal file
231
DataCenter/DataCenter.cpp.bak
Normal file
@@ -0,0 +1,231 @@
|
||||
#include "DataCenter.h"
|
||||
#include <QDebug>
|
||||
#include <FileOperation/ConfigFiles.h>
|
||||
#include <PublicFunctions/Basic.h>
|
||||
#include <QCoreApplication>
|
||||
DataCenter* DataCenter::m_instance = nullptr;
|
||||
QMutex DataCenter::m_mutex; // 仅用于单例模式的线程安全
|
||||
|
||||
DataCenter::DataCenter(OpcUaManager *opcManager, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_opcManager(opcManager)
|
||||
, m_batchSize(10)
|
||||
, m_currentReadIndex(0)
|
||||
{
|
||||
Q_ASSERT(opcManager != nullptr);
|
||||
|
||||
// 创建专用线程和工作对象
|
||||
m_opcThread = new QThread(this);
|
||||
m_worker = new OpcWorker(opcManager); // 不设置父对象
|
||||
|
||||
// 将工作对象移动到线程
|
||||
m_worker->moveToThread(m_opcThread);
|
||||
|
||||
// 连接定时器信号到工作对象的槽函数
|
||||
connect(&m_worker->m_cyclicTimer, &QTimer::timeout,
|
||||
m_worker, &OpcWorker::onTimerTimeout, Qt::DirectConnection);
|
||||
|
||||
// 连接工作对象信号到DataCenter
|
||||
connect(m_worker, &OpcWorker::timeout, this, &DataCenter::onTimerTimeout, Qt::QueuedConnection);
|
||||
// 连接OPC管理器信号到工作对象
|
||||
connect(m_opcManager, &OpcUaManager::reconnected, this, &DataCenter::refreshAllNodes, Qt::QueuedConnection);
|
||||
|
||||
// 启动线程
|
||||
m_opcThread->start();
|
||||
}
|
||||
|
||||
DataCenter::~DataCenter()
|
||||
{
|
||||
if (m_opcThread && m_opcThread->isRunning()) {
|
||||
m_opcThread->quit();
|
||||
if (!m_opcThread->wait(500)) { // 等待500ms
|
||||
m_opcThread->terminate();
|
||||
m_opcThread->wait();
|
||||
}
|
||||
}
|
||||
delete m_worker; // 释放工作对象
|
||||
m_worker = nullptr;
|
||||
}
|
||||
|
||||
DataCenter* DataCenter::instance(QObject *parent)
|
||||
{
|
||||
if (!m_instance) {
|
||||
QMutexLocker locker(&m_mutex); // 单例创建时的线程安全锁
|
||||
if (!m_instance) {
|
||||
OpcUaManager* opc = OpcUaManager::instance(parent);
|
||||
m_instance = new DataCenter(opc, parent);
|
||||
}
|
||||
}
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void DataCenter::initData()
|
||||
{
|
||||
// 清空全局变量
|
||||
gOPC_NodeList.clear();
|
||||
gOPC_NodeName.clear();
|
||||
gOPC_NodeValue.clear();
|
||||
gOPC_VarName.clear();
|
||||
gOPC_SqlTable.clear();
|
||||
gOPC_SqlField.clear();
|
||||
m_initNodeIds.clear();
|
||||
|
||||
// 读取初始化节点配置
|
||||
QList<QStringList> fileDataInit = ConfigFiles().ReadFile_Csv(ConfigurationPath + "OpcUa配置/OpcUA_初始化读取节点_配置.csv");
|
||||
for (int row = 1; row < fileDataInit.size()-1; row++) {
|
||||
if (fileDataInit.at(row).size() >= 4) {
|
||||
addMonitoredNode(fileDataInit.at(row)[3], fileDataInit.at(row)[0],
|
||||
fileDataInit.at(row)[2], fileDataInit.at(row)[1],
|
||||
fileDataInit.at(row)[2]);
|
||||
m_initNodeIds.append(fileDataInit.at(row)[3]);
|
||||
}
|
||||
}
|
||||
// 读取循环节点配置
|
||||
QList<QStringList> fileData = ConfigFiles().ReadFile_Csv(ConfigurationPath + "OpcUa配置/OpcUA_循环读取节点_配置.csv");
|
||||
for (int row = 1; row < fileData.size()-1; row++) {
|
||||
if (fileData.at(row).size() >= 4) {
|
||||
addMonitoredNode(fileData.at(row)[3], fileData.at(row)[0],
|
||||
fileData.at(row)[2], fileData.at(row)[1],
|
||||
fileData.at(row)[2]);
|
||||
}
|
||||
}
|
||||
qDebug() << "数据初始化完成,总条目数:"<<fileData.size()<<" 导入节点总数:" << gOPC_NodeList.size();
|
||||
}
|
||||
|
||||
void DataCenter::addMonitoredNode(const QString &nodeId, const QString &nodeName,
|
||||
const QString &varName, const QString &tableName,
|
||||
const QString &fieldName)
|
||||
{
|
||||
if (gOPC_NodeName.contains(nodeId)) {
|
||||
qWarning() << "节点已存在:" << nodeId;
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用全局变量存储
|
||||
gOPC_NodeName[nodeId] = nodeName.isEmpty() ? nodeId : nodeName;
|
||||
gOPC_VarName[nodeId] = varName.isEmpty() ? nodeId : varName;
|
||||
gOPC_SqlTable[nodeId] = tableName;
|
||||
gOPC_SqlField[nodeId] = fieldName;
|
||||
gOPC_NodeList.append(nodeId);
|
||||
gOPC_NodeValue[nodeId] = QVariant();
|
||||
}
|
||||
|
||||
void DataCenter::browseRecursive(const QString &nodeId)
|
||||
{
|
||||
doBrowse(nodeId, 6, "");
|
||||
}
|
||||
|
||||
void DataCenter::doBrowse(const QString &nodeId, int depth, QString prefix)
|
||||
{
|
||||
if (!m_opcManager->getClient() || m_opcManager->getClient()->state() != QOpcUaClient::Connected) {
|
||||
qDebug() << "客户端未连接";
|
||||
return;
|
||||
}
|
||||
|
||||
// 命中目标节点就添加并返回
|
||||
if (nodeId.startsWith("ns=6;s=::", Qt::CaseInsensitive) && !nodeId.contains("#")) {
|
||||
addMonitoredNode(nodeId, "", "", "", "");
|
||||
return;
|
||||
}
|
||||
|
||||
QOpcUaNode *node = m_opcManager->getCachedNode(nodeId);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 浏览子节点(使用QueuedConnection确保跨线程安全)
|
||||
connect(node, &QOpcUaNode::browseFinished,
|
||||
this, [this, depth, prefix](QVector<QOpcUaReferenceDescription> refs, QOpcUa::UaStatusCode status){
|
||||
if (status != QOpcUa::UaStatusCode::Good) {
|
||||
qDebug() << "浏览失败,状态码:" << status;
|
||||
return;
|
||||
}
|
||||
|
||||
if (refs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &ref : refs) {
|
||||
QString childId = ref.targetNodeId().nodeId();
|
||||
doBrowse(childId, depth + 1, "└─ ");
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
node->browseChildren(QOpcUa::ReferenceTypeId::HierarchicalReferences,
|
||||
QOpcUa::NodeClass::Undefined);
|
||||
}
|
||||
|
||||
void DataCenter::startCyclicRead(int intervalMs)
|
||||
{
|
||||
if (gOPC_NodeList.isEmpty()) {
|
||||
qWarning()<<"没有需要监控的节点,请先添加节点";
|
||||
return;
|
||||
}
|
||||
|
||||
int actualInterval = qMax(intervalMs, 20); // 最小间隔10ms
|
||||
QMetaObject::invokeMethod(m_worker, "startCyclicTimer",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(int, actualInterval));
|
||||
m_currentReadIndex = 0;
|
||||
}
|
||||
|
||||
void DataCenter::stopCyclicRead()
|
||||
{
|
||||
QMetaObject::invokeMethod(m_worker, "stopCyclicTimer", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DataCenter::refreshAllNodes()
|
||||
{
|
||||
if (gOPC_NodeList.isEmpty())
|
||||
return;
|
||||
|
||||
m_currentReadIndex = 0;
|
||||
|
||||
// 检查定时器状态并启动
|
||||
QMetaObject::invokeMethod(m_worker, [this]() {
|
||||
if (!m_worker->m_cyclicTimer.isActive()) {
|
||||
m_worker->startCyclicTimer(10); // 使用默认间隔
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
onTimerTimeout();
|
||||
}
|
||||
|
||||
QVariant DataCenter::getNodeValue(const QString &nodeId) const
|
||||
{
|
||||
return gOPC_NodeValue.value(nodeId, QVariant());
|
||||
}
|
||||
|
||||
QVariant DataCenter::getNodeValueByName(const QString &nodeName) const
|
||||
{
|
||||
for (auto it = gOPC_NodeName.constBegin(); it != gOPC_NodeName.constEnd(); ++it) {
|
||||
if (it.value() == nodeName) {
|
||||
return gOPC_NodeValue.value(it.key(), QVariant());
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void DataCenter::onTimerTimeout()
|
||||
{
|
||||
|
||||
if (gOPC_NodeList.isEmpty()) return;
|
||||
|
||||
// 准备批量读取的节点
|
||||
QList<QString> batchNodes;
|
||||
int remaining = gOPC_NodeList.size() - m_currentReadIndex;
|
||||
int takeCount = qMin(m_batchSize, remaining);
|
||||
for (int i = 0; i < takeCount; ++i) {
|
||||
batchNodes.append(gOPC_NodeList[m_currentReadIndex + i]);
|
||||
}
|
||||
m_currentReadIndex += takeCount;
|
||||
// 循环索引处理
|
||||
if (m_currentReadIndex >= gOPC_NodeList.size()) {
|
||||
m_currentReadIndex = 0;
|
||||
}
|
||||
if (!batchNodes.isEmpty()) {
|
||||
QMetaObject::invokeMethod(m_opcManager, "readNodesValue",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QList<QString>, batchNodes));
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ bool MySQL::open(const QString &host,
|
||||
const QString &pwd,
|
||||
const QString &dbName)
|
||||
{
|
||||
isConnectOK = false;
|
||||
if (QSqlDatabase::contains("qt_mysql_conn"))
|
||||
db_ = QSqlDatabase::database("qt_mysql_conn");
|
||||
else
|
||||
@@ -32,9 +33,11 @@ bool MySQL::open(const QString &host,
|
||||
|
||||
if (!db_.open()) {
|
||||
qWarning() << timeStamp() << "【错误】连接失败:" << db_.lastError().text();
|
||||
isConnectOK = false;
|
||||
return false;
|
||||
}
|
||||
qInfo() << timeStamp() << "【信息】数据库连接成功";
|
||||
isConnectOK = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
QList<QVariantList> selectLatest(const QString &tableName,
|
||||
const QStringList &fieldNames,
|
||||
int maxRows);
|
||||
bool isConnectOK = false;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
@@ -61,9 +61,11 @@ bool OpcUaManager::connectToServer(const QString &url, const QString &username,
|
||||
return false;
|
||||
}
|
||||
|
||||
mClient->requestEndpoints(mServerUrl);
|
||||
|
||||
connect(mClient, &QOpcUaClient::readNodeAttributesFinished,this, &OpcUaManager::onBatchReadFinished);
|
||||
return true;
|
||||
bool isOK = mClient->requestEndpoints(mServerUrl);
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
void OpcUaManager::disconnectFromServer()
|
||||
@@ -196,22 +198,25 @@ bool OpcUaManager::writeNodeValue(const QString &nodeId,
|
||||
int maxRetry /*= 3*/,
|
||||
int retryIntervalMs /*= 200*/)
|
||||
{
|
||||
QString mNodeID = nodeId;
|
||||
if(!mNodeID.contains("ns=6;s=::AsGlobalPV:"))
|
||||
mNodeID = "ns=6;s=::AsGlobalPV:"+mNodeID;
|
||||
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
|
||||
qWarning().noquote() << "[OPC] 客户端未连接,写入" << nodeId << "失败";
|
||||
qWarning().noquote() << "[OPC] 客户端未连接,写入" << mNodeID << "失败";
|
||||
return false;
|
||||
}
|
||||
|
||||
QOpcUaNode *node = mClient->node(nodeId);
|
||||
QOpcUaNode *node = mClient->node(mNodeID);
|
||||
if (!node) {
|
||||
qWarning().noquote() << "[OPC] 无法写入节点:" << nodeId;
|
||||
qWarning().noquote() << "[OPC] 无法写入节点:" << mNodeID;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int attempt = 1; attempt <= maxRetry; ++attempt) {
|
||||
/* ---------- 1. 先读当前值 ---------- */
|
||||
QVariant cur = gOPC_NodeValue[nodeId];
|
||||
QVariant cur = gOPC_NodeValue[mNodeID];
|
||||
if (!cur.isValid()) {
|
||||
//qWarning().noquote() << "读到无效值:" << nodeId<<gOPC_NodeValue[nodeId];
|
||||
//qWarning().noquote() << "读到无效值:" << mNodeID<<gOPC_NodeValue[mNodeID];
|
||||
}
|
||||
|
||||
/* ---------- 2. 类型转换 ---------- */
|
||||
@@ -247,16 +252,17 @@ bool OpcUaManager::writeNodeValue(const QString &nodeId,
|
||||
|
||||
/* ---------- 4. 失败日志 & 重试 ---------- */
|
||||
// qWarning().noquote() << "[OPC] 第" << attempt << "/" << maxRetry
|
||||
// << "次写入失败:" << nodeId << " 等待重试 ...";
|
||||
// << "次写入失败:" << mNodeID << " 等待重试 ...";
|
||||
if (attempt < maxRetry)
|
||||
QThread::msleep(retryIntervalMs);
|
||||
}
|
||||
|
||||
qCritical().noquote() << "[OPC] 最终写入失败:" << nodeId;
|
||||
qCritical().noquote() << "[OPC] 最终写入失败:" << mNodeID;
|
||||
return false;
|
||||
}
|
||||
QOpcUaClient::ClientState OpcUaManager::connectionState() const
|
||||
{
|
||||
qDebug()<<"实际状态:"<<mClient->state();
|
||||
if (mClient) {
|
||||
return mClient->state();
|
||||
}
|
||||
|
||||
378
DataCenter/OpcUaManager.cpp.bak
Normal file
378
DataCenter/OpcUaManager.cpp.bak
Normal file
@@ -0,0 +1,378 @@
|
||||
#include "OpcUaManager.h"
|
||||
#include <QOpcUaProvider>
|
||||
#include <QMap>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <GlobalDefinitions/Variable.h>
|
||||
OpcUaManager* OpcUaManager::m_instance = nullptr;
|
||||
QMutex OpcUaManager::m_mutex;
|
||||
|
||||
OpcUaManager::OpcUaManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, mReconnectTimer(new QTimer(this))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
OpcUaManager::~OpcUaManager()
|
||||
{
|
||||
disconnectFromServer();
|
||||
clearNodeCache();
|
||||
if (mClient) {
|
||||
delete mClient;
|
||||
mClient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QOpcUaClient* OpcUaManager::getClient() const
|
||||
{
|
||||
return mClient;
|
||||
}
|
||||
|
||||
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);
|
||||
connect(mClient, &QOpcUaClient::readNodeAttributesFinished,this, &OpcUaManager::onBatchReadFinished);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpcUaManager::disconnectFromServer()
|
||||
{
|
||||
if (mClient) {
|
||||
mClient->disconnectFromEndpoint();
|
||||
}
|
||||
|
||||
clearNodeCache();
|
||||
mCurrentNodeId.clear();
|
||||
mPendingWriteValue = QVariant();
|
||||
}
|
||||
|
||||
bool OpcUaManager::readNodeValue(const QString &nodeId)
|
||||
{
|
||||
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
|
||||
qWarning()<<("客户端未连接");
|
||||
return false;
|
||||
}
|
||||
|
||||
QOpcUaNode* node = getCachedNode(nodeId);
|
||||
if (!node) {
|
||||
QString error = QString("无法获取节点: %1").arg(nodeId);
|
||||
qWarning()<<(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
mCurrentNodeId = nodeId;
|
||||
|
||||
disconnect(node, &QOpcUaNode::attributeRead, this, &OpcUaManager::onAttributeRead);
|
||||
connect(node,
|
||||
static_cast<void (QOpcUaNode::*)(QOpcUa::NodeAttributes)>(&QOpcUaNode::attributeRead),
|
||||
this,
|
||||
&OpcUaManager::onAttributeRead);
|
||||
|
||||
node->readAttributes(
|
||||
QOpcUa::NodeAttribute::Value |
|
||||
QOpcUa::NodeAttribute::AccessLevel
|
||||
);
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
|
||||
} else {
|
||||
gOPC_NodeValue[nodeId] = "";
|
||||
}
|
||||
}
|
||||
bool OpcUaManager::readNodesValue(const QList<QString>& nodeIds)
|
||||
{
|
||||
// 前置校验:客户端连接状态 + 节点列表非空 + 避免并发读取
|
||||
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
|
||||
qWarning()<<("批量读取失败:OPC客户端未连接");
|
||||
return false;
|
||||
}
|
||||
if (nodeIds.isEmpty()) {
|
||||
qWarning()<<("批量读取失败:节点列表为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 1. 构建批量读取请求(正确构造QOpcUaReadItem)
|
||||
QVector<QOpcUaReadItem> readItems;
|
||||
for (const QString& nodeId : nodeIds) {
|
||||
QOpcUaReadItem item;
|
||||
item.setNodeId(nodeId);
|
||||
readItems.append(item);
|
||||
}
|
||||
|
||||
// 发送批量读取请求
|
||||
if(!mClient->readNodeAttributes(readItems)){
|
||||
qDebug()<<("批量读取请求发送失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 批量读取结果处理槽函数(参数需与信号匹配)
|
||||
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*/)
|
||||
{
|
||||
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
|
||||
qWarning().noquote() << "[OPC] 客户端未连接,写入" << nodeId << "失败";
|
||||
return false;
|
||||
}
|
||||
|
||||
QOpcUaNode *node = mClient->node(nodeId);
|
||||
if (!node) {
|
||||
qWarning().noquote() << "[OPC] 无法写入节点:" << nodeId;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int attempt = 1; attempt <= maxRetry; ++attempt) {
|
||||
/* ---------- 1. 先读当前值 ---------- */
|
||||
QVariant cur = gOPC_NodeValue[nodeId];
|
||||
if (!cur.isValid()) {
|
||||
//qWarning().noquote() << "读到无效值:" << nodeId<<gOPC_NodeValue[nodeId];
|
||||
}
|
||||
|
||||
/* ---------- 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
|
||||
// << "次写入失败:" << nodeId << " 等待重试 ...";
|
||||
if (attempt < maxRetry)
|
||||
QThread::msleep(retryIntervalMs);
|
||||
}
|
||||
|
||||
qCritical().noquote() << "[OPC] 最终写入失败:" << nodeId;
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 禁用安全策略(根据实际需求调整)
|
||||
clientParams["securityMode"] = "None";
|
||||
clientParams["securityPolicy"] = "None";
|
||||
|
||||
// 性能参数设置
|
||||
clientParams["requestTimeout"] = 5000;
|
||||
clientParams["maxArrayLength"] = 10000;
|
||||
|
||||
// 创建客户端(使用open62541后端)
|
||||
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);
|
||||
}
|
||||
|
||||
void OpcUaManager::tryReconnect()
|
||||
{
|
||||
if (mClient && mClient->state() == QOpcUaClient::Disconnected) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
emit stateChanged(state);
|
||||
|
||||
if (state == QOpcUaClient::Disconnected) {
|
||||
qDebug() << "OPC UA 连接断开," << RECONNECT_INTERVAL << "ms 后自动重连...";
|
||||
mReconnectTimer->start(RECONNECT_INTERVAL);
|
||||
} else if (state == QOpcUaClient::Connected) {
|
||||
mReconnectTimer->stop();
|
||||
mReconnectCount = 0; // 重置计数器
|
||||
mReconnectTimer->setInterval(RECONNECT_INTERVAL); // 重置间隔
|
||||
emit reconnected();
|
||||
}
|
||||
}
|
||||
|
||||
QOpcUaNode* OpcUaManager::getCachedNode(const QString& nodeId)
|
||||
{
|
||||
if (mNodeCache.contains(nodeId)) {
|
||||
return mNodeCache[nodeId];
|
||||
}
|
||||
|
||||
if (!mClient) return nullptr;
|
||||
|
||||
QOpcUaNode* node = mClient->node(nodeId);
|
||||
if (node) {
|
||||
node->setParent(this);
|
||||
mNodeCache[nodeId] = node;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void OpcUaManager::clearNodeCache()
|
||||
{
|
||||
qDeleteAll(mNodeCache.values());
|
||||
mNodeCache.clear();
|
||||
mWriteRequests.clear();
|
||||
}
|
||||
Reference in New Issue
Block a user