第一次上传
This commit is contained in:
139
DataCenter/DataCenter.cpp
Normal file
139
DataCenter/DataCenter.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "DataCenter.h"
|
||||
#include <QDebug>
|
||||
#include <FileOperation/ConfigFiles.h>
|
||||
|
||||
DataCenter::DataCenter(OpcUaManager *opcManager, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_opcManager(opcManager)
|
||||
, m_cyclicTimer(new QTimer(this))
|
||||
, m_currentReadIndex(0)
|
||||
{
|
||||
Q_ASSERT(opcManager != nullptr); // 确保OPC管理器有效
|
||||
// 连接定时器
|
||||
connect(m_cyclicTimer, &QTimer::timeout, this, &DataCenter::onTimerTimeout);
|
||||
|
||||
// 连接OPC读取信号(仅处理数据读取逻辑)
|
||||
connect(m_opcManager, &OpcUaManager::readCompleted,
|
||||
this, &DataCenter::handleReadCompleted);
|
||||
|
||||
// 转发错误信号
|
||||
connect(m_opcManager, &OpcUaManager::errorOccurred,
|
||||
this, &DataCenter::errorOccurred);
|
||||
|
||||
connect(m_opcManager, &OpcUaManager::reconnected,
|
||||
this, &DataCenter::refreshAllNodes);
|
||||
}
|
||||
//
|
||||
void DataCenter::InitData(){
|
||||
QList<QStringList> FileData = ConfigFiles().ReadFile_Csv("./ProgramConfig/OpcUA_Node_Config.csv");
|
||||
for (int row = 1; row < FileData.size()-1; ++row)
|
||||
{
|
||||
addMonitoredNode(FileData.at(row)[3], FileData.at(row)[0],FileData.at(row)[2],FileData.at(row)[1],FileData.at(row)[2]);
|
||||
}
|
||||
}
|
||||
// 添加监控节点(仅数据层操作,不涉及UI)
|
||||
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;
|
||||
}
|
||||
|
||||
QString NName = nodeName.isEmpty() ? nodeId : nodeName;
|
||||
QString VName = varName.isEmpty() ? nodeId : varName;
|
||||
gOPC_NodeName[nodeId] = NName;
|
||||
gOPC_VarName[nodeId] = VName;
|
||||
gOPC_SqlTable[nodeId] = TableName;
|
||||
gOPC_SqlField[nodeId] = FieldName;
|
||||
gOPC_NodeList.append(nodeId);
|
||||
gOPC_NodeValue[nodeId] = QVariant(); // 初始化值
|
||||
}
|
||||
|
||||
// 开始循环读取(纯数据操作)
|
||||
void DataCenter::startCyclicRead(int intervalMs)
|
||||
{
|
||||
if (gOPC_NodeList.isEmpty()) {
|
||||
emit errorOccurred("没有需要监控的节点,请先添加节点");
|
||||
return;
|
||||
}
|
||||
|
||||
int actualInterval = qMax(intervalMs, 3); // 最小间隔100ms
|
||||
m_cyclicTimer->stop(); // <-- 关键:先停
|
||||
m_cyclicTimer->start(actualInterval); // <-- 再启
|
||||
m_currentReadIndex = 0;
|
||||
}
|
||||
|
||||
// 停止循环读取
|
||||
void DataCenter::stopCyclicRead()
|
||||
{
|
||||
m_cyclicTimer->stop();
|
||||
}
|
||||
void DataCenter::refreshAllNodes()
|
||||
{
|
||||
if (gOPC_NodeList.isEmpty())
|
||||
return;
|
||||
|
||||
// 重置索引
|
||||
m_currentReadIndex = 0;
|
||||
|
||||
// 如果定时器没开,重新启动它
|
||||
if (!m_cyclicTimer->isActive()) {
|
||||
m_cyclicTimer->start(); // 使用之前设置的间隔
|
||||
}
|
||||
|
||||
// 立即触发一次读取(可选)
|
||||
onTimerTimeout();
|
||||
}
|
||||
// 通过节点ID获取值
|
||||
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;
|
||||
|
||||
// 循环索引处理
|
||||
if (m_currentReadIndex >= gOPC_NodeList.size()) {
|
||||
m_currentReadIndex = 0;
|
||||
}
|
||||
|
||||
// 读取当前节点(仅数据操作)
|
||||
QString nodeId = gOPC_NodeList[m_currentReadIndex];
|
||||
if (!m_opcManager->readNodeValue(nodeId)) {
|
||||
emit errorOccurred("读取节点失败: " + nodeId);
|
||||
m_currentReadIndex++; // 无论成功与否都移动到下一个
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 处理读取结果并缓存(仅数据处理)
|
||||
void DataCenter::handleReadCompleted(const QVariant &value, const QString &nodeId)
|
||||
{
|
||||
if (!gOPC_NodeName.contains(nodeId)) return;
|
||||
|
||||
// 更新缓存值
|
||||
gOPC_NodeValue[nodeId] = value;
|
||||
|
||||
// 发送更新信号(仅传递数据,不涉及UI)
|
||||
emit nodeValueUpdated(nodeId, gOPC_NodeName[nodeId],gOPC_VarName[nodeId], value);
|
||||
|
||||
// 移动到下一个节点
|
||||
m_currentReadIndex++;
|
||||
// if(DC_SetST.funts_SetSysInitOjb(gSysInfo,gOPC_VarName[nodeId],value)) return;
|
||||
// else if (DC_SetST.funts_SetSysInitOjb(gSysInfo,gOPC_VarName[nodeId],value)) return;
|
||||
}
|
||||
62
DataCenter/DataCenter.h
Normal file
62
DataCenter/DataCenter.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef DATACENTER_H
|
||||
#define DATACENTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QTimer>
|
||||
#include <QMap>
|
||||
#include <QStringList>
|
||||
#include "OpcUaManager.h"
|
||||
#include <GlobalDefinitions/Variable.h>
|
||||
|
||||
class DataCenter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DataCenter(OpcUaManager *opcManager, QObject *parent = nullptr);
|
||||
|
||||
// 添加需要监控的节点
|
||||
void addMonitoredNode(const QString &nodeId, const QString &nodeName,const QString &varName,const QString &TableName,const QString &FieldName);
|
||||
|
||||
// 开始/停止循环读取
|
||||
void startCyclicRead(int intervalMs);
|
||||
void stopCyclicRead();
|
||||
|
||||
// 获取最新的节点值
|
||||
QVariant getNodeValue(const QString &nodeId) const;
|
||||
QVariant getNodeValueByName(const QString &nodeName) const;
|
||||
|
||||
// 获取所有节点信息
|
||||
QMap<QString, QString> getAllNodeNames() const { return gOPC_NodeName; }
|
||||
|
||||
void InitData();
|
||||
signals:
|
||||
// 节点值更新信号(供UI层订阅)
|
||||
void nodeValueUpdated(const QString &nodeId, const QString &nodeName,const QString &varName, const QVariant &value);
|
||||
// 错误信号
|
||||
void errorOccurred(const QString &errorString);
|
||||
|
||||
private slots:
|
||||
// 定时器触发,执行循环读取
|
||||
void onTimerTimeout();
|
||||
// 处理OPC读取结果
|
||||
void handleReadCompleted(const QVariant &value, const QString &nodeId);
|
||||
void refreshAllNodes();
|
||||
private:
|
||||
/** @brief OPC操作实例(不负责所有权) */
|
||||
OpcUaManager *m_opcManager;
|
||||
|
||||
/** @brief 循环读取定时器 */
|
||||
QTimer *m_cyclicTimer;
|
||||
|
||||
/** @brief 当前读取索引 */
|
||||
int m_currentReadIndex;
|
||||
|
||||
|
||||
|
||||
|
||||
//GlobalType DC_SetST; // 数据中心,给变量赋值
|
||||
|
||||
};
|
||||
|
||||
#endif // DATACENTER_H
|
||||
350
DataCenter/OpcUaManager.cpp
Normal file
350
DataCenter/OpcUaManager.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
#include "OpcUaManager.h"
|
||||
#include <QOpcUaProvider>
|
||||
#include <QMap>
|
||||
#include <QDebug>
|
||||
|
||||
// OPC UA错误码常量定义
|
||||
const qint32 bad_user_access_denied = -2139881472; // 用户访问被拒绝
|
||||
const qint32 bad_type_mismatch = -2147483053; // 数据类型不匹配
|
||||
|
||||
// 构造函数
|
||||
OpcUaManager::OpcUaManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, mClient(nullptr)
|
||||
, mCurrentNode(nullptr)
|
||||
, mReconnectTimer(new QTimer(this))
|
||||
{
|
||||
connect(mReconnectTimer, &QTimer::timeout, // 3. 铃声(timeout)连到槽
|
||||
this, &OpcUaManager::tryReconnect); // 到时就会执行 tryReconnect()
|
||||
}
|
||||
|
||||
// 析构函数
|
||||
OpcUaManager::~OpcUaManager()
|
||||
{
|
||||
disconnectFromServer();
|
||||
if (mClient) {
|
||||
delete mClient;
|
||||
mClient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 连接到OPC UA服务器
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 断开与服务器的连接
|
||||
void OpcUaManager::disconnectFromServer()
|
||||
{
|
||||
if (mClient) {
|
||||
mClient->disconnectFromEndpoint(); // 断开端点连接
|
||||
}
|
||||
|
||||
// 清理当前节点
|
||||
if (mCurrentNode) {
|
||||
mCurrentNode->disconnect(this);
|
||||
mCurrentNode = nullptr;
|
||||
}
|
||||
|
||||
mCurrentNodeId.clear();
|
||||
mPendingWriteValue = QVariant(); // 清空待写入值
|
||||
}
|
||||
|
||||
// 读取节点值
|
||||
bool OpcUaManager::readNodeValue(const QString &nodeId)
|
||||
{
|
||||
// 检查客户端状态
|
||||
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
|
||||
emit errorOccurred("客户端未连接");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清理旧节点
|
||||
if (mCurrentNode) {
|
||||
mCurrentNode->disconnect(this);
|
||||
mCurrentNode = nullptr;
|
||||
}
|
||||
|
||||
// 获取目标节点
|
||||
mCurrentNodeId = nodeId;
|
||||
mCurrentNode = mClient->node(nodeId);
|
||||
if (!mCurrentNode) {
|
||||
QString error = QString("无法获取节点: %1").arg(nodeId);
|
||||
emit errorOccurred(error);
|
||||
return false;
|
||||
}
|
||||
mCurrentNode->setParent(this);
|
||||
|
||||
// 连接读取信号
|
||||
connect(mCurrentNode,
|
||||
static_cast<void (QOpcUaNode::*)(QOpcUa::NodeAttributes)>(&QOpcUaNode::attributeRead),
|
||||
this,
|
||||
&OpcUaManager::onAttributeRead);
|
||||
|
||||
// 读取节点值和访问级别
|
||||
mCurrentNode->readAttributes(
|
||||
QOpcUa::NodeAttribute::Value | // 节点值
|
||||
QOpcUa::NodeAttribute::AccessLevel // 访问级别(可读/可写)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 写入节点值(动态适配类型)
|
||||
bool OpcUaManager::writeNodeValue(const QString &nodeId, const QVariant &value)
|
||||
{
|
||||
// 检查客户端状态
|
||||
if (!mClient || mClient->state() != QOpcUaClient::Connected) {
|
||||
emit errorOccurred("客户端未连接");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清理旧节点
|
||||
if (mCurrentNode) {
|
||||
mCurrentNode->disconnect(this);
|
||||
mCurrentNode = nullptr;
|
||||
}
|
||||
|
||||
// 保存待写入值和节点ID
|
||||
mPendingWriteValue = value;
|
||||
mCurrentNodeId = nodeId;
|
||||
|
||||
// 获取目标节点
|
||||
mCurrentNode = mClient->node(nodeId);
|
||||
if (!mCurrentNode) {
|
||||
QString error = QString("无法获取节点: %1").arg(nodeId);
|
||||
emit errorOccurred(error);
|
||||
return false;
|
||||
}
|
||||
mCurrentNode->setParent(this);
|
||||
|
||||
// 连接类型读取信号(写入前先读类型)
|
||||
connect(mCurrentNode,
|
||||
static_cast<void (QOpcUaNode::*)(QOpcUa::NodeAttributes)>(&QOpcUaNode::attributeRead),
|
||||
this,
|
||||
&OpcUaManager::onPreWriteValueTypeRead);
|
||||
|
||||
// 读取节点当前值(用于判断类型)
|
||||
mCurrentNode->readAttributes(QOpcUa::NodeAttribute::Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取当前连接状态
|
||||
QOpcUaClient::ClientState OpcUaManager::connectionState() const
|
||||
{
|
||||
if (mClient) {
|
||||
return mClient->state();
|
||||
}
|
||||
return QOpcUaClient::Disconnected;
|
||||
}
|
||||
|
||||
// 创建OPC UA客户端
|
||||
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";
|
||||
|
||||
// 创建客户端(使用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);
|
||||
}
|
||||
uint32_t mReconnectCount =0;
|
||||
void OpcUaManager::tryReconnect()
|
||||
{
|
||||
if (mClient && mClient->state() == QOpcUaClient::Disconnected) {
|
||||
qDebug() << "正在执行第" << mReconnectCount << "次重连...";
|
||||
connectToServer(mServerUrl.toString(), mUserName, mPassword);
|
||||
}
|
||||
mReconnectCount ++;
|
||||
}
|
||||
// 端点请求完成处理
|
||||
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();
|
||||
emit reconnected(); // 广播重连成功
|
||||
}
|
||||
}
|
||||
|
||||
// 属性读取完成处理(普通读取)
|
||||
void OpcUaManager::onAttributeRead(QOpcUa::NodeAttributes attrs)
|
||||
{
|
||||
if (attrs.testFlag(QOpcUa::NodeAttribute::Value) && mCurrentNode) {
|
||||
// 读取值并转发
|
||||
QVariant value = mCurrentNode->attribute(QOpcUa::NodeAttribute::Value);
|
||||
emit readCompleted(value, mCurrentNodeId);
|
||||
|
||||
// // 检查访问级别
|
||||
// if (attrs.testFlag(QOpcUa::NodeAttribute::AccessLevel)) {
|
||||
// quint8 accessLevel = mCurrentNode->attribute(QOpcUa::NodeAttribute::AccessLevel).toUInt();
|
||||
|
||||
// bool canWrite = (accessLevel & 0x02); // 0x02 = 可写
|
||||
// bool canRead = (accessLevel & 0x01); // 0x01 = 可读
|
||||
// qDebug() << "节点访问级别: " << accessLevel
|
||||
// << ",可读: " << canRead
|
||||
// << ",可写: " << canWrite;
|
||||
|
||||
// if (!canWrite) {
|
||||
// emit errorOccurred("节点当前不可写(服务器限制)");
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
emit readCompleted(QVariant(), mCurrentNodeId);
|
||||
emit errorOccurred(QString("读取节点 %1 失败").arg(mCurrentNodeId));
|
||||
}
|
||||
}
|
||||
|
||||
// 写入前读取值类型(使用QOpcUa::Types枚举强制指定类型)
|
||||
void OpcUaManager::onPreWriteValueTypeRead(QOpcUa::NodeAttributes attrs)
|
||||
{
|
||||
if (!mCurrentNode || !attrs.testFlag(QOpcUa::NodeAttribute::Value)) {
|
||||
emit errorOccurred("读取节点当前值失败,无法判断类型");
|
||||
return;
|
||||
}
|
||||
|
||||
// 断开类型读取信号(避免重复触发)
|
||||
disconnect(mCurrentNode, &QOpcUaNode::attributeRead, this, &OpcUaManager::onPreWriteValueTypeRead);
|
||||
|
||||
// 获取当前值的类型
|
||||
QVariant currentValue = mCurrentNode->attribute(QOpcUa::NodeAttribute::Value);
|
||||
QString typeName = currentValue.typeName();
|
||||
qDebug() << "节点当前值类型:" << typeName;
|
||||
|
||||
// 转换值并指定QOpcUa类型枚举(核心修复)
|
||||
QVariant convertedValue;
|
||||
QOpcUa::Types targetType = QOpcUa::Types::Undefined; // 使用枚举类型
|
||||
|
||||
if (typeName == "int") {
|
||||
convertedValue = mPendingWriteValue.toInt();
|
||||
targetType = QOpcUa::Types::Int32; // int对应Int32
|
||||
} else if (typeName == "float") {
|
||||
convertedValue = static_cast<float>(mPendingWriteValue.toDouble());
|
||||
targetType = QOpcUa::Types::Float; // float对应Float
|
||||
} else if (typeName == "double") {
|
||||
convertedValue = mPendingWriteValue.toDouble();
|
||||
targetType = QOpcUa::Types::Double; // double对应Double
|
||||
} else if (typeName == "ushort") {
|
||||
quint16 value = static_cast<quint16>(mPendingWriteValue.toUInt());
|
||||
if (value > 65535) {
|
||||
value = 65535;
|
||||
qWarning() << "值超过ushort最大值65535,已自动截断";
|
||||
}
|
||||
convertedValue = value;
|
||||
targetType = QOpcUa::Types::UInt16; // ushort对应UInt16
|
||||
} else {
|
||||
convertedValue = mPendingWriteValue;
|
||||
targetType = QOpcUa::Types::Undefined; // 自动类型
|
||||
}
|
||||
|
||||
qDebug() << "转换为" << typeName << "类型写入,QOpcUa枚举值:" << static_cast<int>(targetType);
|
||||
|
||||
// 连接写入信号并执行写入
|
||||
connect(mCurrentNode,
|
||||
static_cast<void (QOpcUaNode::*)(QOpcUa::NodeAttribute, QOpcUa::UaStatusCode)>(&QOpcUaNode::attributeWritten),
|
||||
this,
|
||||
&OpcUaManager::onAttributeWritten);
|
||||
|
||||
// 关键:使用QOpcUa::Types枚举作为参数,解决类型不匹配
|
||||
mCurrentNode->writeAttribute(
|
||||
QOpcUa::NodeAttribute::Value,
|
||||
convertedValue,
|
||||
targetType
|
||||
);
|
||||
}
|
||||
|
||||
// 属性写入完成处理
|
||||
void OpcUaManager::onAttributeWritten(QOpcUa::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode)
|
||||
{
|
||||
if (attribute == QOpcUa::NodeAttribute::Value) {
|
||||
bool success = (statusCode == QOpcUa::UaStatusCode::Good);
|
||||
emit writeCompleted(success, mCurrentNodeId);
|
||||
|
||||
if (!success) {
|
||||
QString errorMsg;
|
||||
// 根据错误码解析原因
|
||||
switch (static_cast<qint32>(statusCode)) {
|
||||
case bad_user_access_denied:
|
||||
errorMsg = QString("写入节点 %1 失败:用户没有写入权限(可能是类型不匹配导致的误报)").arg(mCurrentNodeId);
|
||||
break;
|
||||
case bad_type_mismatch:
|
||||
errorMsg = QString("写入节点 %1 失败:数据类型不匹配").arg(mCurrentNodeId);
|
||||
break;
|
||||
default:
|
||||
errorMsg = QString("写入节点 %1 失败,状态码: %2")
|
||||
.arg(mCurrentNodeId)
|
||||
.arg(static_cast<int>(statusCode));
|
||||
}
|
||||
emit errorOccurred(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
DataCenter/OpcUaManager.h
Normal file
89
DataCenter/OpcUaManager.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef OPCUAMANAGER_H
|
||||
#define OPCUAMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QOpcUaClient>
|
||||
#include <QOpcUaNode>
|
||||
#include <QOpcUaEndpointDescription>
|
||||
#include <QVector>
|
||||
#include <QUrl>
|
||||
#include <QTimer>
|
||||
class OpcUaManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OpcUaManager(QObject *parent = nullptr);
|
||||
~OpcUaManager();
|
||||
|
||||
// 连接服务器
|
||||
bool connectToServer(const QString &url, const QString &username, const QString &password);
|
||||
// 断开连接
|
||||
void disconnectFromServer();
|
||||
// 读取节点值
|
||||
bool readNodeValue(const QString &nodeId);
|
||||
// 写入节点值
|
||||
bool writeNodeValue(const QString &nodeId, const QVariant &value);
|
||||
// 获取连接状态
|
||||
QOpcUaClient::ClientState connectionState() const;
|
||||
|
||||
signals:
|
||||
// 状态变化信号
|
||||
void stateChanged(QOpcUaClient::ClientState state);
|
||||
// 读取完成信号(返回值和节点ID)
|
||||
void readCompleted(const QVariant &value, const QString &nodeId);
|
||||
// 写入完成信号(返回是否成功和节点ID)
|
||||
void writeCompleted(bool success, const QString &nodeId);
|
||||
// 错误信号(返回错误信息)
|
||||
void errorOccurred(const QString &errorString);
|
||||
void reconnected(); // 新增:重连成功后广播
|
||||
private slots:
|
||||
// 端点请求完成处理
|
||||
void onEndpointsRequestFinished(QVector<QOpcUaEndpointDescription> endpoints,
|
||||
QOpcUa::UaStatusCode statusCode,
|
||||
QUrl requestedUrl);
|
||||
// 客户端状态变化处理
|
||||
void onClientStateChanged(QOpcUaClient::ClientState state);
|
||||
// 属性读取完成处理(用于普通读取)
|
||||
void onAttributeRead(QOpcUa::NodeAttributes attrs);
|
||||
// 属性写入完成处理
|
||||
void onAttributeWritten(QOpcUa::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode);
|
||||
// 写入前读取值类型(用于动态类型适配)
|
||||
void onPreWriteValueTypeRead(QOpcUa::NodeAttributes attrs);
|
||||
|
||||
void tryReconnect(); // 新增:真正执行重连
|
||||
private:
|
||||
// 创建客户端实例
|
||||
void createClient();
|
||||
|
||||
/** @brief OPC UA客户端实例 */
|
||||
QOpcUaClient *mClient;
|
||||
|
||||
/** @brief 当前操作的节点 */
|
||||
QOpcUaNode *mCurrentNode;
|
||||
|
||||
/** @brief 当前节点ID */
|
||||
QString mCurrentNodeId;
|
||||
|
||||
/** @brief 服务器地址 */
|
||||
QUrl mServerUrl;
|
||||
|
||||
/** @brief 登录用户名 */
|
||||
QString mUserName;
|
||||
|
||||
/** @brief 登录密码 */
|
||||
QString mPassword;
|
||||
|
||||
/** @brief 节点数据类型(备用) */
|
||||
int mNodeDataType;
|
||||
|
||||
/** @brief 待写入的值(临时存储) */
|
||||
QVariant mPendingWriteValue;
|
||||
|
||||
/** @brief 重连定时器 */
|
||||
QTimer *mReconnectTimer = nullptr;
|
||||
|
||||
/** @brief 每次重连的时间间隔 */
|
||||
const int RECONNECT_INTERVAL = 3000; //
|
||||
};
|
||||
|
||||
#endif // OPCUAMANAGER_H
|
||||
Reference in New Issue
Block a user