如何对open62541.h/open62541.c的UA_Client进行状态(在线/掉线)监控

1.背景

目前在利用open62541.h/open62541.c编写了一个与PLC进行OPCUA通讯的上位机程序。
上位机这边会定时对PLC的某个opcua变量进行写操作。但是假如PLC离线或者说拔掉网线,上位机就会直接崩溃死机,并且报如下的错误:

[2024-08-12 10:07:25.528 (UTC+0800)] warn/channel	Connection 2516 | SecureChannel 28 | Receiving the response failed with StatusCode BadConnectionClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/client	Received Publish Response with code BadSecureChannelClosed
[2024-08-12 10:07:25.528 (UTC+0800)] warn/channel	Connection 0 | SecureChannel 0 | Could not receive with StatusCode BadConnectionClosed
[2024-08-12 10:07:25.528 (UTC+0800)] info/client	Client Status: ChannelState: Closed, SessionState: Created, ConnectStatus: Good

有没有什么办法可以监控client的状态,了解其是什么时候掉线,掉线了我就不写就行了。

2.解决方案

幸亏,是有这么一个回调函数的,参考官方的例子【open62541/examples/client_async.c】可以发现,我们可以对UA_ClientConfig中的stateCallback进行赋值,也就是注册一个状态回调函数,从而获取客户端的状态变化。

static void
onConnect(UA_Client *client, UA_SecureChannelState channelState,
          UA_SessionState sessionState, UA_StatusCode connectStatus) {
    printf("Async connect returned with status code %s\n",
           UA_StatusCode_name(connectStatus));
}

---

 UA_ClientConfig *cc = UA_Client_getConfig(client);
 cc->stateCallback = onConnect;

一般检测channelState就行

// 状态变化回调函数,可以通过这个监测客户端是否断开连接
// 此回调函数的线程,貌似就是UA_Client_run_iterate所在的线程?起始应该不是,恐怕是在哪个线程调用了UA_Client相关的函数,就在那个线程;
static void
onStateChanged(UA_Client *client,
          UA_SecureChannelState channelState,
          UA_SessionState sessionState,
          UA_StatusCode connectStatus)
{
    if(channelState == UA_SECURECHANNELSTATE_CLOSED) // 连接已断开
    {
        qDebug() << "callback thread:" << QThread::currentThread();
        qDebug() << "连接已断开--------" << QDateTime::currentDateTime();

       ---
       
    }
}

3.异步连接

在实际使用中,假如opcua服务端是离线的话,执行UA_Client_connect会导致线程卡住很长一段时间。假如不希望卡住的话,得使用异步连接:UA_Client_connectAsync
使用UA_Client_connectAsync后,需要在stateCallback函数中判断是否连接上。完全连接上的话,
UA_SessionState sessionState会等于UA_SESSIONSTATE_ACTIVATED;

static void
onStateChanged(UA_Client *client,
          UA_SecureChannelState channelState,
          UA_SessionState sessionState,
          UA_StatusCode connectStatus)
{
}

假如你在onStateChanged中将channelState、sessionState打印出来的话,会发现channelState先从上往下满足下面的顺序:
在这里插入图片描述
然后是sessionState从上往下满足下面的顺序:
在这里插入图片描述
此外,一定要记得在某个地方周期地调用UA_Client_run_iterate,否则,不会进入回调函数。

4.注意事项

4.1.线程问题

这个opcua库对多线程的处理比较差,很容易造成冲突。一定要自己加个互斥锁,否则程序很容易就崩掉。

4.2.UA_Client_run_iterate

一定要在某个地方周期调用。但是最好不是在子线程中调用,除非你有把握控制好线程冲突(比如加互斥锁之类)。


参考:
【open62541/examples/client_async.c】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值