451 lines
15 KiB
C++
451 lines
15 KiB
C++
|
|
#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::m_instance = nullptr; // 初始化为空
|
|||
|
|
QMutex OpcUaManager::m_mutex; // 初始化互斥锁
|
|||
|
|
|
|||
|
|
|
|||
|
|
OpcUaManager::OpcUaManager(QObject *parent)
|
|||
|
|
: QObject(parent)
|
|||
|
|
, mClient(nullptr)
|
|||
|
|
, mCurrentNode(nullptr)
|
|||
|
|
, mReconnectTimer(new QTimer(this))
|
|||
|
|
{
|
|||
|
|
connect(mReconnectTimer, &QTimer::timeout, this, &OpcUaManager::tryReconnect);
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
OpcUaManager* OpcUaManager::instance(QObject *parent)
|
|||
|
|
{
|
|||
|
|
// 双重检查锁:1. 先判断实例是否存在(避免频繁加锁);2. 加锁后再次判断(确保线程安全)
|
|||
|
|
if (!m_instance) {
|
|||
|
|
QMutexLocker locker(&m_mutex); // 加锁(自动解锁,避免死锁)
|
|||
|
|
if (!m_instance) {
|
|||
|
|
m_instance = new OpcUaManager(parent); // 创建唯一实例
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return m_instance;
|
|||
|
|
}
|
|||
|
|
// 析构函数(原有逻辑不变)
|
|||
|
|
OpcUaManager::~OpcUaManager()
|
|||
|
|
{
|
|||
|
|
disconnectFromServer();
|
|||
|
|
if (mClient) {
|
|||
|
|
delete mClient;
|
|||
|
|
mClient = nullptr;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
QOpcUaClient* OpcUaManager::getClient() const
|
|||
|
|
{
|
|||
|
|
return mClient;
|
|||
|
|
}
|
|||
|
|
// 连接到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
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#include <QDebug>
|
|||
|
|
#include <QEventLoop>
|
|||
|
|
#include <QTimer>
|
|||
|
|
#include <QThread>
|
|||
|
|
|
|||
|
|
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. 先读当前值 ---------- */
|
|||
|
|
QEventLoop readLoop;
|
|||
|
|
bool readOk = false;
|
|||
|
|
connect(node, &QOpcUaNode::attributeRead,
|
|||
|
|
[&](QOpcUa::NodeAttributes) { readOk = true; readLoop.quit(); });
|
|||
|
|
node->readAttributes(QOpcUa::NodeAttribute::Value);
|
|||
|
|
QTimer::singleShot(2000, &readLoop, &QEventLoop::quit);
|
|||
|
|
readLoop.exec();
|
|||
|
|
|
|||
|
|
if (!readOk) {
|
|||
|
|
qWarning().noquote() << "[OPC] 第" << attempt << "次读取超时:" << nodeId;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QVariant cur = node->attribute(QOpcUa::NodeAttribute::Value);
|
|||
|
|
if (!cur.isValid()) {
|
|||
|
|
qWarning().noquote() << "[OPC] 第" << attempt << "次读到无效值:" << nodeId;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ---------- 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;
|
|||
|
|
}
|
|||
|
|
// 属性写入完成处理
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|