C# OPC UA 客户端开发
OPC UA(OPC Unified Architecture)是为了在工业自动化等行业安全可靠地进行数据交换而制定的开放式国际标准规格。由OPC Foundation于2008年发布,之后制定了国际标准IEC62541.跨越设备的种类、操作系统(OS)、制造商的壁垒,可以安全地进行高可靠性的数据交换,作为标准通信协议得到了工业 4.0的推荐。https://opcfoundation.org/
OPC UA开发首先需要一个服务器端:这里使用KEPServer来模拟。首先下载并安装KepServer,基本一路下一步。配置OPC UA:
在桌面右下角右键点击“OPC UA配置”
选择网卡和端口号,修改成自己想要的地址。
服务端测试:下载softing OPC Client 工具并安装。如下图所示连接KepServer
数据节点读写测试:
有了以上准备后,利用C# 开发自己的OPC UA helper:
我使用的VS2019,新建.net framework类库项目:修改.net framework版本4.6.2以上,nuget 包管理器里搜索opc.ua.client.选择opc 基金会的开发组件:如下图所示:
新建类OPCUAClient.cs
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace hg.opc.client
{public class OPCUAClient{#region Private Fieldsprivate ApplicationConfiguration appConfig;private Session clientSession;private bool isConnected = false; //是否已经连接过private int reconnectPeriod = 10; // 重连状态private SessionReconnectHandler sessionReconnectHandler;private EventHandler onConnectedEventHandler;private Dictionary<string, Subscription> subscriptionNodes=new Dictionary<string, Subscription>(); // 系统所有的节点信息#endregion Private Fields#region Public Memberspublic IUserIdentity UserIdentity { get; set; }= new UserIdentity(new AnonymousIdentityToken());public bool IsConnected { get => isConnected; private set => isConnected = value; }public Dictionary<string, Subscription> SubscriptionNodes { get => subscriptionNodes; set => subscriptionNodes = value; }public event EventHandler OnConnectedEvent{add { onConnectedEventHandler += value; }remove { onConnectedEventHandler -= value; }}#endregion#region 构造函数public OPCUAClient(){var certificateValidator = new CertificateValidator();certificateValidator.CertificateValidation += (sender, eventArgs) =>{if (ServiceResult.IsGood(eventArgs.Error))eventArgs.Accept = true;else if (eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted)eventArgs.Accept = true;elsethrow new Exception(string.Format("证书验证错误: {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));};certificateValidator.Update(new SecurityConfiguration{AutoAcceptUntrustedCertificates = true,RejectSHA1SignedCertificates = false,MinimumCertificateKeySize = 1024,});var configuration = new ApplicationConfiguration{ApplicationName = "MyOpc_Client",ApplicationType = ApplicationType.Client,CertificateValidator = certificateValidator,ApplicationUri = string.Empty, ServerConfiguration = new ServerConfiguration{MaxSubscriptionCount = 100000,MaxMessageQueueSize = 100000,MaxNotificationQueueSize = 100000,MaxPublishRequestCount = 100000,},SecurityConfiguration = new SecurityConfiguration{AutoAcceptUntrustedCertificates = true,RejectSHA1SignedCertificates = false,MinimumCertificateKeySize = 1024,SuppressNonceValidationErrors = true,ApplicationCertificate = new CertificateIdentifier{StoreType = CertificateStoreType.X509Store,StorePath = "CurrentUser\\My",},TrustedIssuerCertificates = new CertificateTrustList{StoreType = CertificateStoreType.X509Store,StorePath = "CurrentUser\\Root",},TrustedPeerCertificates = new CertificateTrustList{StoreType = CertificateStoreType.X509Store,StorePath = "CurrentUser\\Root",}},TransportQuotas = new TransportQuotas{OperationTimeout = 6000000,MaxStringLength = int.MaxValue,MaxByteStringLength = int.MaxValue,MaxArrayLength = 65535,MaxMessageSize = 419430400,MaxBufferSize = 65535,ChannelLifetime = -1,SecurityTokenLifetime = -1},ClientConfiguration = new ClientConfiguration{DefaultSessionTimeout = -1,MinSubscriptionLifetime = -1,},DisableHiResClock = true};configuration.Validate(ApplicationType.Client);appConfig = configuration;}#endregion#region 连接public async Task ConnectServer(string serverUrl){Disconnect();var endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, false);var endpointConfiguration = EndpointConfiguration.Create(appConfig);var endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);clientSession = await Task.Run(async ()=> {return await Session.Create(appConfig,endpoint,false,false,appConfig.ApplicationName,60000,UserIdentity,new string[] { });});clientSession.KeepAlive += Session_KeepAlive;IsConnected = true;onConnectedEventHandler?.Invoke(this, EventArgs.Empty);}private void Session_KeepAlive(Session session, KeepAliveEventArgs e){try{if (!object.ReferenceEquals(session, this.clientSession)){return;}if (ServiceResult.IsBad(e.Status)){if (reconnectPeriod <= 0){return;}if (sessionReconnectHandler == null){sessionReconnectHandler = new SessionReconnectHandler();sessionReconnectHandler.BeginReconnect(session, reconnectPeriod * 1000, Server_ReconnectComplete);}return;}}catch (Exception exception){throw exception;}}private void Server_ReconnectComplete(object sender, EventArgs e){try{if (!ReferenceEquals(sender, sessionReconnectHandler)){return;}clientSession = sessionReconnectHandler.Session;sessionReconnectHandler.Dispose();sessionReconnectHandler = null;}catch (Exception exception){throw exception;}}public void Disconnect(){if (sessionReconnectHandler != null){sessionReconnectHandler.Dispose();sessionReconnectHandler = null;}if (clientSession != null){clientSession.Close(10000);clientSession = null;}IsConnected = false;}#endregion#region 浏览节点public ReferenceDescriptionCollection BrowserNode2(NodeId nodeId){var browser = new Browser(clientSession);return browser.Browse(nodeId);}public ReferenceDescriptionCollection BrowserNode(NodeId nodeId){ var browseDescriptions = new BrowseDescriptionCollection();var browserDesc = new BrowseDescription();browserDesc.NodeId = nodeId;browserDesc.BrowseDirection = BrowseDirection.Forward;browserDesc.ReferenceTypeId = ReferenceTypeIds.Aggregates;browserDesc.IncludeSubtypes = true;browserDesc.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method | NodeClass.ReferenceType | NodeClass.ObjectType | NodeClass.View | NodeClass.VariableType | NodeClass.DataType);browserDesc.ResultMask = (uint)BrowseResultMask.All;var browseDesc2 = new BrowseDescription();browseDesc2.NodeId = nodeId;browseDesc2.BrowseDirection = BrowseDirection.Forward;browseDesc2.ReferenceTypeId = ReferenceTypeIds.Organizes;browseDesc2.IncludeSubtypes = true;browseDesc2.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method | NodeClass.View | NodeClass.ReferenceType | NodeClass.ObjectType | NodeClass.VariableType | NodeClass.DataType);browseDesc2.ResultMask = (uint)BrowseResultMask.All;browseDescriptions.Add(browserDesc);browseDescriptions.Add(browseDesc2);ReferenceDescriptionCollection references = GetReferenceDescriptionCollection(browseDescriptions);return references;}public ReferenceDescriptionCollection GetReferenceDescriptionCollection(BrowseDescriptionCollection browseDescriptions){try{var referenceDescriptions = new ReferenceDescriptionCollection();var unprocessedOperations = new BrowseDescriptionCollection();while (browseDescriptions.Count > 0){BrowseResultCollection results = null;DiagnosticInfoCollection diagnosticInfos = null;clientSession.Browse(null, null, 0, browseDescriptions,out results,out diagnosticInfos);ClientBase.ValidateResponse(results, browseDescriptions);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, browseDescriptions);ByteStringCollection continuationPoints = new ByteStringCollection();for (int i = 0; i < browseDescriptions.Count; i++){if (StatusCode.IsBad(results[i].StatusCode)){if (results[i].StatusCode == StatusCodes.BadNoContinuationPoints){unprocessedOperations.Add(browseDescriptions[i]);}continue;}// check if all references have been fetched.if (results[i].References.Count == 0){continue;}referenceDescriptions.AddRange(results[i].References);if (results[i].ContinuationPoint != null){continuationPoints.Add(results[i].ContinuationPoint);}}ByteStringCollection revisedContiuationPoints = new ByteStringCollection();while (continuationPoints.Count > 0){// continue browse operation.clientSession.BrowseNext(null,true,continuationPoints,out results,out diagnosticInfos);ClientBase.ValidateResponse(results, continuationPoints);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, continuationPoints);for (int j = 0; j < continuationPoints.Count; j++){if (StatusCode.IsBad(results[j].StatusCode)){continue;}if (results[j].References.Count == 0){continue;}referenceDescriptions.AddRange(results[j].References);if (results[j].ContinuationPoint != null){revisedContiuationPoints.Add(results[j].ContinuationPoint);}}revisedContiuationPoints = continuationPoints;}browseDescriptions = unprocessedOperations;}return referenceDescriptions;}catch (Exception exception){return null;}}#endregion#region Node Write/Read Support/// <summary>/// Read a value node from server/// </summary>/// <param name="nodeId">node id</param>/// <returns>DataValue</returns>public DataValue ReadNode(NodeId nodeId){ReadValueIdCollection nodesToRead = new ReadValueIdCollection{new ReadValueId( ){NodeId = nodeId,AttributeId = Attributes.Value}};// read the current valueclientSession.Read(null,0,TimestampsToReturn.Neither,nodesToRead,out DataValueCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, nodesToRead);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);return results[0];}/// <summary>/// 是否可读写节点/// </summary>/// <param name="nodeId"></param>/// <returns></returns>public bool IsWriteableNode(NodeId nodeId){ReadValueIdCollection nodesToRead = new ReadValueIdCollection{new ReadValueId( ){NodeId = nodeId,AttributeId = Attributes.AccessLevel}};// read the current valueclientSession.Read(null,0,TimestampsToReturn.Neither,nodesToRead,out DataValueCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, nodesToRead);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);DataValue value= results[0];if (value.WrappedValue == Variant.Null){return true;}return !((byte)value.WrappedValue.Value == 1);}/// <summary>/// Read a value node from server/// </summary>/// <typeparam name="T">type of value</typeparam>/// <param name="tag">node id</param>/// <returns>实际值</returns>public T ReadNode<T>(string tag){DataValue dataValue = ReadNode(new NodeId(tag));return (T)dataValue.Value;}/// <summary>/// Read a tag asynchronously/// </summary>/// <typeparam name="T">The type of tag to read</typeparam>/// <param name="tag">tag值</param>/// <returns>The value retrieved from the OPC</returns>public Task<T> ReadNodeAsync<T>(string tag){ReadValueIdCollection nodesToRead = new ReadValueIdCollection{new ReadValueId(){NodeId = new NodeId(tag),AttributeId = Attributes.Value}};// Wrap the ReadAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:var taskCompletionSource = new TaskCompletionSource<T>();clientSession.BeginRead(requestHeader: null,maxAge: 0,timestampsToReturn: TimestampsToReturn.Neither,nodesToRead: nodesToRead,callback: ar =>{DataValueCollection results;DiagnosticInfoCollection diag;var response = clientSession.EndRead(result: ar,results: out results,diagnosticInfos: out diag);try{if (!StatusCode.IsGood(response.ServiceResult))throw new Exception(string.Format("Invalid response from the server. (Response Status: {0})", response.ServiceResult));if (!StatusCode.IsGood(results[0].StatusCode))throw new Exception(string.Format("Invalid response from the server. (Response Status: {0})", results[0].StatusCode));var val = results[0];taskCompletionSource.TrySetResult((T)val.Value);}catch (Exception ex){taskCompletionSource.TrySetException(ex);}},asyncState: null);return taskCompletionSource.Task;}/// <summary>/// read several value nodes from server/// </summary>/// <param name="nodeIds">all NodeIds</param>/// <returns>all values</returns>public List<DataValue> ReadNodes(NodeId[] nodeIds){ReadValueIdCollection nodesToRead = new ReadValueIdCollection();for (int i = 0; i < nodeIds.Length; i++){nodesToRead.Add(new ReadValueId(){NodeId = nodeIds[i],AttributeId = Attributes.Value});}// 读取当前的值clientSession.Read(null,0,TimestampsToReturn.Neither,nodesToRead,out DataValueCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, nodesToRead);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);return results.ToList();}/// <summary>/// read several value nodes from server/// </summary>/// <param name="nodeIds">all NodeIds</param>/// <returns>all values</returns>public Task<List<DataValue>> ReadNodesAsync(NodeId[] nodeIds){ReadValueIdCollection nodesToRead = new ReadValueIdCollection();for (int i = 0; i < nodeIds.Length; i++){nodesToRead.Add(new ReadValueId(){NodeId = nodeIds[i],AttributeId = Attributes.Value});}var taskCompletionSource = new TaskCompletionSource<List<DataValue>>();// 读取当前的值clientSession.BeginRead(null,0,TimestampsToReturn.Neither,nodesToRead,callback: ar =>{DataValueCollection results;DiagnosticInfoCollection diag;var response = clientSession.EndRead(result: ar,results: out results,diagnosticInfos: out diag);try{CheckReturnValue(response.ServiceResult);taskCompletionSource.TrySetResult(results.ToList());}catch (Exception ex){taskCompletionSource.TrySetException(ex);}},asyncState: null);return taskCompletionSource.Task;}/// <summary>/// read several value nodes from server/// </summary>/// <param name="tags">所以的节点数组信息</param>/// <returns>all values</returns>public List<T> ReadNodes<T>(string[] tags){List<T> result = new List<T>();ReadValueIdCollection nodesToRead = new ReadValueIdCollection();for (int i = 0; i < tags.Length; i++){nodesToRead.Add(new ReadValueId(){NodeId = new NodeId(tags[i]),AttributeId = Attributes.Value});}// 读取当前的值clientSession.Read(null,0,TimestampsToReturn.Neither,nodesToRead,out DataValueCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, nodesToRead);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);foreach (var item in results){result.Add((T)item.Value);}return result;}/// <summary>/// read several value nodes from server/// </summary>/// <param name="tags">all NodeIds</param>/// <returns>all values</returns>public Task<List<T>> ReadNodesAsync<T>(string[] tags){ReadValueIdCollection nodesToRead = new ReadValueIdCollection();for (int i = 0; i < tags.Length; i++){nodesToRead.Add(new ReadValueId(){NodeId = new NodeId(tags[i]),AttributeId = Attributes.Value});}var taskCompletionSource = new TaskCompletionSource<List<T>>();// 读取当前的值clientSession.BeginRead(null,0,TimestampsToReturn.Neither,nodesToRead,callback: ar =>{DataValueCollection results;DiagnosticInfoCollection diag;var response = clientSession.EndRead(result: ar,results: out results,diagnosticInfos: out diag);try{CheckReturnValue(response.ServiceResult);List<T> result = new List<T>();foreach (var item in results){result.Add((T)item.Value);}taskCompletionSource.TrySetResult(result);}catch (Exception ex){taskCompletionSource.TrySetException(ex);}},asyncState: null);return taskCompletionSource.Task;}/// <summary>/// 0:NodeClass 1:Value 2:AccessLevel 3:DisplayName 4:Description/// </summary>/// <param name="nodeIds"></param>/// <returns></returns>public DataValue[] ReadNodeAttributes(List<NodeId> nodeIds){ReadValueIdCollection nodesToRead = new ReadValueIdCollection();foreach (var nodeId in nodeIds){NodeId sourceId = nodeId;nodesToRead.Add(new ReadValueId(){NodeId = sourceId,AttributeId = Attributes.NodeClass});nodesToRead.Add(new ReadValueId(){NodeId = sourceId,AttributeId = Attributes.Value});nodesToRead.Add(new ReadValueId(){NodeId = sourceId,AttributeId = Attributes.AccessLevel});nodesToRead.Add(new ReadValueId(){NodeId = sourceId,AttributeId = Attributes.DisplayName});nodesToRead.Add(new ReadValueId(){NodeId = sourceId,AttributeId = Attributes.Description});}// read all values.clientSession.Read(null,0,TimestampsToReturn.Neither,nodesToRead,out DataValueCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, nodesToRead);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);return results.ToArray();}/// <summary>/// write a note to server(you should use try catch)/// </summary>/// <typeparam name="T">The type of tag to write on</typeparam>/// <param name="tag">节点名称</param>/// <param name="value">值</param>/// <returns>if success True,otherwise False</returns>public bool WriteNode<T>(string tag, T value){WriteValue valueToWrite = new WriteValue(){NodeId = new NodeId(tag),AttributeId = Attributes.Value};valueToWrite.Value.Value = value;valueToWrite.Value.StatusCode = StatusCodes.Good;valueToWrite.Value.ServerTimestamp = DateTime.MinValue;valueToWrite.Value.SourceTimestamp = DateTime.MinValue;WriteValueCollection valuesToWrite = new WriteValueCollection{valueToWrite};// 写入当前的值clientSession.Write(null,valuesToWrite,out StatusCodeCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, valuesToWrite);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, valuesToWrite);if (StatusCode.IsBad(results[0])){throw new ServiceResultException(results[0]);}return !StatusCode.IsBad(results[0]);}/// <summary>/// Write a value on the specified opc tag asynchronously/// </summary>/// <typeparam name="T">The type of tag to write on</typeparam>/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name. E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`</param>/// <param name="value">The value for the item to write</param>public Task<bool> WriteNodeAsync<T>(string tag, T value){WriteValue valueToWrite = new WriteValue(){NodeId = new NodeId(tag),AttributeId = Attributes.Value,};valueToWrite.Value.Value = value;valueToWrite.Value.StatusCode = StatusCodes.Good;valueToWrite.Value.ServerTimestamp = DateTime.MinValue;valueToWrite.Value.SourceTimestamp = DateTime.MinValue;WriteValueCollection valuesToWrite = new WriteValueCollection{valueToWrite};// Wrap the WriteAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:var taskCompletionSource = new TaskCompletionSource<bool>();clientSession.BeginWrite(requestHeader: null,nodesToWrite: valuesToWrite,callback: ar =>{var response = clientSession.EndWrite(result: ar,results: out StatusCodeCollection results,diagnosticInfos: out DiagnosticInfoCollection diag);try{ClientBase.ValidateResponse(results, valuesToWrite);ClientBase.ValidateDiagnosticInfos(diag, valuesToWrite);taskCompletionSource.SetResult(StatusCode.IsGood(results[0]));}catch (Exception ex){taskCompletionSource.TrySetException(ex);}},asyncState: null);return taskCompletionSource.Task;}/// <summary>/// 所有的节点都写入成功,返回<c>True</c>,否则返回<c>False</c>/// </summary>/// <param name="tags">节点名称数组</param>/// <param name="values">节点的值数据</param>/// <returns>所有的是否都写入成功</returns>public bool WriteNodes(string[] tags, object[] values){WriteValueCollection valuesToWrite = new WriteValueCollection();for (int i = 0; i < tags.Length; i++){if (i < values.Length){WriteValue valueToWrite = new WriteValue(){NodeId = new NodeId(tags[i]),AttributeId = Attributes.Value};valueToWrite.Value.Value = values[i];valueToWrite.Value.StatusCode = StatusCodes.Good;valueToWrite.Value.ServerTimestamp = DateTime.MinValue;valueToWrite.Value.SourceTimestamp = DateTime.MinValue;valuesToWrite.Add(valueToWrite);}}// 写入当前的值clientSession.Write(null,valuesToWrite,out StatusCodeCollection results,out DiagnosticInfoCollection diagnosticInfos);ClientBase.ValidateResponse(results, valuesToWrite);ClientBase.ValidateDiagnosticInfos(diagnosticInfos, valuesToWrite);bool result = true;foreach (var r in results){if (StatusCode.IsBad(r)){result = false;break;}}return result;}private void CheckReturnValue(StatusCode status){if (!StatusCode.IsGood(status))throw new Exception(string.Format("Invalid response from the server. (Response Status: {0})", status));}#endregion Node Write/Read Support#region 订阅/// <summary>/// 新增一批订阅,需要指定订阅的关键字,订阅的tag名数组,以及回调方法/// </summary>/// <param name="key">关键字</param>/// <param name="tags">节点名称数组</param>/// <param name="callback">回调方法</param>public void AddSubscription(string key, string[] tags, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback){Subscription m_subscription = new Subscription(clientSession.DefaultSubscription);m_subscription.PublishingEnabled = true;m_subscription.PublishingInterval = 0;m_subscription.KeepAliveCount = uint.MaxValue;m_subscription.LifetimeCount = uint.MaxValue;m_subscription.MaxNotificationsPerPublish = uint.MaxValue;m_subscription.Priority = 100;m_subscription.DisplayName = key;for (int i = 0; i < tags.Length; i++){var item = new MonitoredItem{StartNodeId = new NodeId(tags[i]),AttributeId = Attributes.Value,DisplayName = tags[i],SamplingInterval = 100,};item.Notification += (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args) =>{callback?.Invoke(key, monitoredItem, args);};m_subscription.AddItem(item);}clientSession.AddSubscription(m_subscription);m_subscription.Create();lock (SubscriptionNodes){if (SubscriptionNodes.ContainsKey(key)){// removeSubscriptionNodes[key].Delete(true);clientSession.RemoveSubscription(SubscriptionNodes[key]);SubscriptionNodes[key].Dispose();SubscriptionNodes[key] = m_subscription;}else{SubscriptionNodes.Add(key, m_subscription);}}}/// <summary>/// 新增订阅,需要指定订阅的关键字,订阅的tag名数组,以及回调方法/// </summary>/// <param name="key">关键字</param>/// <param name="tags">节点名称数组</param>/// <param name="callback">回调方法</param>public void AddSubscription(string key, string tag, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback){AddSubscription(key, new string[] { tag }, callback);}/// <summary>/// 移除订阅消息,如果该订阅消息是批量的,也直接移除/// </summary>/// <param name="key">订阅关键值</param>public void RemoveSubscription(string key){lock (SubscriptionNodes){if (SubscriptionNodes.ContainsKey(key)){// removeSubscriptionNodes[key].Delete(true);clientSession.RemoveSubscription(SubscriptionNodes[key]);SubscriptionNodes[key].Dispose();SubscriptionNodes.Remove(key);}}}/// <summary>/// 移除所有的订阅消息/// </summary>public void RemoveAllSubscription(){lock (SubscriptionNodes){foreach (var item in SubscriptionNodes){item.Value.Delete(true);clientSession.RemoveSubscription(item.Value);item.Value.Dispose();}SubscriptionNodes.Clear();}}#endregion}
}
利用上面的OPCUAClient开发的winform程序:
源码下载:https://download.csdn.net/download/elie_yang/20814696
C# OPC UA 客户端开发相关推荐
- OPC UA客户端工具Softing OPC Client使用_推荐使用
OPC UA客户端工具Softing OPC Client使用_推荐使用 Softing OPC Client工具介绍 Softing OPC Client工具是德国Softing公司出品的标准OPC ...
- OPC UA客户端工具UaExpert使用
OPC UA客户端工具UaExpert使用 官方下载地址: https://www.unified-automation.com/downloads.html UaExpert 是一个全功能的 OPC ...
- OPC UA JAVA开发笔记(四):数据写入
这一节我们来将如何实现Client端的数据持续写入OPC UA.一下程序均在Spring Boot环境中,请先添加相应的依赖 首先,我们准备一个RestController用于提供JSON数据. @R ...
- OPC:客户端开发——应用WTclient.dll使用手册部分中文版
WinTECH软件快速客户端开发DLL(WTclient),提供了一种应用API方便地将定制应用和任何OPC Server相结合的方式.所有COM和OPC的细节均由DLL来处理,这使得一个应用可以轻松 ...
- opcua客户端实现断线重连_PLCopen amp; OPC UA信息模型
▌介绍 PLCopen和OPC基金会之间的合作不仅仅体现在规范制定活动中,合作的结果也将在实际应用中得到体现.它们之间的合作融合了两项技术: PLCopen 技术是基于IEC 61131-3 编程标准 ...
- opcua客户端实现断线重连_虹科教您|实现OPC UA C/S快速部署及数据采集
想获取更多IIOT干货 请点击蓝字,关注我们 虹科HMI解决方案 高性能 高防护 宽温 ★★★★★ 简介 边缘HMI需要实现的主要功能包括连接到该边缘HMI的边缘设备的数据采集,实现边缘计算.提供人机 ...
- opcua客户端实现断线重连_干货:通过OPC UA协议访问西门子1500数据
轻松通过OPC UA协议访问西门子1500数据 如何实现西门子1500的OPC UA通信? 1. 功能实现 S7-1500从V2.0开始支持作为OPC UA服务器的功能,本例使用KepServer 作 ...
- 【OPC UA】C# 通过OpcUaHelper建立OPC客户端访问KEPServerEx6 OPC服务器数据
OpcUaHelper 一个通用的opc ua客户端类库,基于.net 4.6.1创建,基于官方opc ua基金会跨平台库创建,封装了节点读写,批量节点读写,引用读取,特性读取,历史数据读取,方法调用 ...
- sdk怎么用_PLC不支持OPC UA怎么办?别问了看完你就懂了
经常有人会烦恼这个问题:我的上位机软件什么功能都有,也支持OPC UA了,现在项目上准备用OPC UA的方式来获取我的PLC的数据,但是我的PLC却不支持OPC UA,怎么办呢?有的人碰到这个问题后就 ...
最新文章
- 第六课.模型评估与模型选择
- access工资明细表_《ACCESS》工资管理完整(整理).doc
- 理论基础 —— 排序 —— 归并排序
- Pythonic:递归、回溯等5种方法生成不重复数字整数
- FunTester框架Redis性能测试之list操作
- window 10及window7电脑前面耳机插孔没有声音,后面有声音
- 抓取豆瓣电影时遇到的问题记录
- 大厂面试 | 阿里巴巴大数据工程师面试题汇总
- 2021年游戏项目的十大编程语言
- 解决opencv打开视频上下翻转问题
- Access key id should not be null or empty.
- 数据库面试题(选择题)
- BenchmarkSQL配置参数介绍
- PHP目前市场怎么样,前景怎么样?
- 动网7.1 SP1得到后台密码得到WebShell
- 项目管理的SDCA环、PDCA环
- 免费下载Windows 7(申请序列号)
- doxygen无法使用dot简单解决方法
- 二手苹果8多少钱_苹果8p电池容量多少,苹果8p换电池多少钱
- c语言编译 mk文件,Application.mk文件使用说明