OPCFoundation.NetStandard.Opc.Ua是OPC基金会发布的开源OPC UA包,提供Client读写库方便读写OPC 服务器数据。
OPCFoundation.NetStandard.Opc.Ua开源项目地址:https://github.com/OPCFoundation/UA-.NETStandard
使用时有几点需要注意。
1、订阅分组
OPC UA的数据订阅分组,和OPC DA的有所不同,每个组数据项有限,这个值具体是多少与服务器的配置有关,一般几百到上千。每个组的订阅名称也不能相同,需要考虑订阅名称分类。服务器对与订阅资源是有限的,有的服务器必须显式的释放订阅资源,不然会造成订阅资源耗尽而订阅失败,必须重启服务端软件才能解决。
2、连接断开
OPC Client与服务器连接断开有两种情况:主动断开和被动断开
客户端主动断开时,必须先注销订阅的数据分组项,再进行断开。目前这个库不能在连接关闭时自动释放所有服务器的连接资源,这个也是这个库不太友好的地方。
如果是服务端主动断开,很可能是服务器关闭了,这时直接关闭即可。
3、数据读取
数据读取有订阅、同步和异步几种方法。订阅较为复杂但性能高、占用的网络带宽也最少。同步与异步仅调用语法有差异,其他类似。
同步或异步都数据时,读多少数据点,就传入多少,然后到返回值中拿数据,C#可以作比较好的类型转换。
4、数据写入
OPC UA在数据写入时,对数据类型有严格要求,数据类型不会自动转换,会写入失败。如果仅在C#系统内部写入数据,且仅少数点写入这时读有次数据,记录返回的数据类型,固定编码写入。
在数据量较多,且有数据订阅的情况下,也可以记录每个点的数据类型,在写入数据时索引数据类型,然后自动转换。
在把数据接口封装为API的情况下,同一个数据可以解析为很多数据类型,比如数据123,可以是Byte,I2、U2、I4、U4、R4等很多类型,反序列化时一般会解析为Single,直接调用写入会因数据类型不正确而失败。这时除了使用索引,自己处理数据兼容性问题,也可以多写几个wrapper来处理数据类型转换问题。
示例的Controller类代码如下:
[ApiController]
[Route("[controller]/[action]")]
public class OpcUaControlServiceController : ControllerBase
{
private readonly ILogger<OpcUaControlServiceController> _logger;
public OpcUaControlServiceController(ILogger<OpcUaControlServiceController> logger)
{
_logger = logger;
}
/// <summary>
/// 获取服务运行信息
/// </summary>
/// <returns>运行信息概览JSON</returns>
[HttpPost,HttpGet]
public string info()
{
return ApiContentResultMsg.Success(OpcUaControlService.Instance.Info());
}
/// <summary>
/// 启动OpcUa 控制服务
/// </summary>
/// <returns>执行信息JSON</returns>
[HttpPost]
public string Start()
{
OpcUaControlService.Instance.Start();
return ApiContentResultMsg.Success();
}
/// <summary>
/// 停止OpcUa 控制服务
/// </summary>
/// <returns>执行信息JSON</returns>
[HttpPost]
public string ShutDown()
{
OpcUaControlService.Instance.ShutDown();
return ApiContentResultMsg.Success();
}
/// <summary>
/// 写入Int32 类型 OpcUa NodeId数据
/// </summary>
/// <returns>执行信息JSON</returns>
[HttpPost]
public string SyncWriteOpcUaInt32(String nodeId, Int32 value)
{
bool b = OpcUaControlService.Instance.SyncWriteOpcUaInt32(nodeId, value);
return b?ApiContentResultMsg.Success(): ApiContentResultMsg.Error("write fail!");
}
/// <summary>
/// 写入float 类型 OpcUa NodeId数据
/// </summary>
/// <returns>执行信息JSON</returns>
[HttpPost]
public string SyncWriteOpcUaFloat(String nodeId, float value)
{
bool b = OpcUaControlService.Instance.SyncWriteOpcUaFloat(nodeId, value);
return b ? ApiContentResultMsg.Success() : ApiContentResultMsg.Error("write fail!");
}
/// <summary>
/// 写入bool 类型 OpcUa NodeId数据
/// </summary>
/// <returns>执行信息JSON</returns>
[HttpPost]
public string SyncWriteOpcUaBool(String nodeId, bool value)
{
bool b = OpcUaControlService.Instance.SyncWriteOpcUaBool(nodeId, value);
return b ? ApiContentResultMsg.Success() : ApiContentResultMsg.Error("write fail!");
}
/// <summary>
/// 写入String 类型 OpcUa NodeId数据
/// </summary>
/// <returns>执行信息JSON</returns>
[HttpPost]
public string SyncWriteOpcUaString(String nodeId, String value)
{
bool b = OpcUaControlService.Instance.SyncWriteOpcUaString(nodeId, value);
return b ? ApiContentResultMsg.Success() : ApiContentResultMsg.Error("write fail!");
}
}
5、参考资料
日本DeviceXPlorer OPC Server软件,在日系PLC和数控支持上比较友好,适用于1万点以内的采集服务,其自带了有个客户端的Demo,参考如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using Opc.Ua;
using System.Security.Cryptography.X509Certificates;
namespace DxpUaSimpleAPI
{
public class DxpUaSimpleClass
{
public struct ReadItemResults
{
public object m_readResultValue;
public StatusCode m_resultCodes;
public DateTime m_sourceTimeStamp;
public DateTime m_serverTimeStamp;
}
public struct WriteItemResults
{
public StatusCode m_resultCodes;
}
public delegate void onAsyncReadCompleteDelegate(List<string> asyncReadNodeName, List<ReadItemResults> AsyncReadItemResults);
public delegate void onAsyncWriteCompleteDelegate(List<string> asyncWriteNodeName, List<WriteItemResults> asyncResultStatus);
public delegate void monitoredItemNotification(List<string> monitoredItems, List<object> monitoredItemValue);
private Session m_clientSession = null;
private Dictionary<string, Subscription> m_subscriptionMap = new Dictionary<string, Subscription>();
private ApplicationInstance m_application = new ApplicationInstance();
private List<Subscription> m_subscriptions = new List<Subscription>();
public onAsyncReadCompleteDelegate m_ReadEventHandler = null;
public onAsyncWriteCompleteDelegate m_WriteEventHandler = null;
public monitoredItemNotification m_MonitoredEventHandler = null;
public struct CertificateStore
{
public string m_applicationCertificateFileName;
public string m_applicationCertificateStore;
public string m_trustedIssuerCertificatesStore;
public string m_TrustedPeerCertificatesStore;
public string m_RejectedCertificateStore;
}
public struct BrowseNodeResults
{
public QualifiedName m_browseName;
public ExpandedNodeId m_nodeIdList;
public LocalizedText m_displayName;
public StatusCode m_statusCodes;
}
public void Initialize()
{
m_application.ApplicationType = ApplicationType.Client;
m_application.ConfigSectionName = "Opc.Ua.SampleClient";
try
{
m_application.LoadApplicationConfiguration(false);
m_application.CheckApplicationInstanceCertificate(false, 0);
}
catch (Exception ex)
{

本文详细介绍了OPC Foundation的.NetStandard.Opc.Ua开源库在OPC UA客户端应用中的使用方法,包括订阅分组、连接管理、数据读写及异常处理。强调了订阅分组限制、连接断开的处理方式、不同类型的读写操作以及数据类型的匹配问题。此外,还提供了一个C# API控制器的示例代码,展示了如何封装接口以进行数据交互。
2068

被折叠的 条评论
为什么被折叠?



