OPC UA 

一 、OPC UA简介

OPC UA(OPC Unified Architecture)是下一代OPC统一体系架构,是一种基于服务的、跨越平台的解决方案。

OPC UA具有如下特点:

1)    扩展了OPC的应用平台。兼容Windows、Linux和Unix平台,不受平台限制,不需要进行DCOM安全设置(DA需要)。这使得基于OPC UA的标准产品可以更好地实现工厂级的数据采集和管理;

2)    OPC UA定义了统一数据和服务模型,使数据组织更为灵活,可以实现报警与事件、数据存取、历史数据存取、控制命令、复杂数据的交互通信;

3)    OPC UA比OPC DA更安全。OPC UA传递的数据是可以加密的,并对通信连接和数据本身都可以实现安全控制。新的安全模型保证了数据从原始设备到系统,从本地到远程的各级自动化和信息化系统的可靠传递;

4)    OPC UA的Internet 通讯,可以不用设置防火墙。

想要了解更多,https://www.cnblogs.com/thammer/p/12882468.html

 

前期准备

 准备好开发的IDE,首选Visual Studio2019版本,新建项目,或是在你原有的项目上进行扩展。注意:项目的..Net Core 2.1和NET Framework版本最新为4.6.2

打开NuGet管理器,输入指令:

Install-Package UA-.NETStandard

或者

 

、OPC UA配置管理器

 1.OPC UA 

 其他家OPC配置界面

 

下面已基金会发布的SDK为基础,开发适合自己的OPC UA。也有基于open62541开发的。

 

OPCFoundation/UA-.NETStandard

 

 此OPC UA参考实现以.NET Standard规范为目标。

.NET Standard允许开发可在当今可用的所有常见平台上运行的应用程序,包括Linux,iOS,Android(通过Xamarin)和Windows 7/8 / 8.1 / 10(包括嵌入式/ IoT版本),而无需特定于平台的修改。

此项目中的参考实施之一已通过OPC Foundation认证测试实验室的认证,以证明其高质量。自从使用合规性测试工具(CTT)V1.04对认证过程进行了测试并验证了合规性以来的修复和增强功能。

此外,还支持云应用程序和服务(例如ASP.NET,DNX,Azure网站,Azure Webjobs,Azure Nano Server和Azure Service Fabric)。

1)Authentication设置

下图是配置设置界面

  1.  证书验证,OPC Foundation指定路径或者存储在“受信用的根证书颁发机构”

 

 

  2.用户名验证

  

  3.Server可支持匿名Anonymous

 

 三 、OPC UA  数据模型

 数据里最重要的能够存储信息,还要好查找易维护。看到唯一性,好多方法都是围绕唯一性展开,UA-.NETStandard里NodeId地址标识符,下面的注释,封装在***State类里面

    /// <summary>
    /// Stores an identifier for a node in a server's address space.
    /// </summary>
    /// <remarks>
    /// <para>
    /// <b>Please refer to OPC Specifications</b>:
    /// <list type="bullet">
    /// <item><b>Address Space Model</b> setion <b>7.2</b></item>
    /// <item><b>Address Space Model</b> setion <b>5.2.2</b></item>
    /// </list>
    /// </para>
    /// <para>
    /// Stores the id of a Node, which resides within the server's address space.
    /// <br/></para>
    /// <para>
    /// The NodeId can be either:
    /// <list type="bullet">
    /// <item><see cref="uint"/></item>
    /// <item><see cref="Guid"/></item>
    /// <item><see cref="string"/></item>
    /// <item><see cref="byte"/>[]</item>
    /// </list>
    /// <br/></para>
    /// <note>
    /// <b>Important:</b> Keep in mind that the actual ID's of nodes should be unique such that no two
    /// nodes within an address-space share the same ID's.
    /// </note>
    /// <para>
    /// The NodeId can be assigned to a particular namespace index. This index is merely just a number and does
    /// not represent some index within a collection that this node has any knowledge of. The assumption is
    /// that the host of this object will manage that directly.
    /// <br/></para>
    /// </remarks>
    [DataContract(Namespace = Namespaces.OpcUaXsd)]
    public class NodeId : IComparable, IFormattable

 

A.    初始化自己的节点

 

 OPC UA 里比较重要的是FolderStateNodeState和BaseDataVariableState,里面具体属性,可以自己去了解,这里不说了

    /// <summary> 
    /// A typed base class for all data variable nodes.
    /// </summary>
    public class BaseDataVariableState : BaseVariableState
    /// <summary> 
    /// The base class for all folder nodes.
    /// </summary>
    public class FolderState : BaseObjectState
    /// <summary>
    /// The base class for custom nodes.
    /// </summary>
    public abstract class NodeState : IDisposable, IFormattable

 

 

a)      比较重要的是CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)创建自己地址空间,下图也是提供了很样例,一个根目录字符串路径就可以了,其他数据源数据类型可以不提供。一般地,(Key, Value)值对。

 

类似二叉树数据结构

b)  二叉树结构体,左为支(Folder)(可再次向下遍历),右为叶(Variable),存储变量信息,如变量完整Full路径,“***.变量组0.变量”,下图是用第三方测试结果

 

 

 

c)      完整代码

 

 1         public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
 2         {
 3             lock (Lock)
 4             {
 5                 IList<IReference> references = null;
 6 
 7                 if (!externalReferences.TryGetValue(ObjectIds.ObjectsFolder, out references))
 8                 {
 9                     externalReferences[ObjectIds.ObjectsFolder] = references = new List<IReference>();
10                 }
11                 // 第三方数据源,来自API
12                 if ((OpcuaDataPrivade == null || OpcuaDataPrivade.VariableNode == null) || String.IsNullOrEmpty(OpcuaDataPrivade.VariableNode.name))
13                 {
14                     return;
15                 }
16 
17                 FolderState root = CreateFolder(null, OpcuaDataPrivade.VariableNode.name, OpcuaDataPrivade.VariableNode.name);
18                 root.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder);
19                 references.Add(new NodeStateReference(ReferenceTypes.Organizes, false, root.NodeId));
20                 root.EventNotifier = EventNotifiers.SubscribeToEvents;
21                 AddRootNotifier(root);
22 
23                 List<BaseDataVariableState> variables = new List<BaseDataVariableState>();
24 
25                 try
26                 {
27                     #region Device_Simulation
28 
29                     BrowseGroup(root, OpcuaDataPrivade.VariableNode);
30 
31                     m_dynamicNodes_temp = MemCopyList(m_dynamicNodes);
32                     #endregion
33 
34                 }
35                 catch (Exception e)
36                 {
37                     Utils.Trace(e, "Error creating the address space.");
38                 }
39 
40                 AddPredefinedNode(SystemContext, root);
41                 
42                 m_simulationTimer = new Timer(DoSimulation, cts, 1000, 1000);
43 
44                 System.Threading.Tasks.Task.Factory.StartNew(() =>
45                 {
46                     WriteProcHandle(cts);
47                 });
48             }
49         }
        private void BrowseGroup(FolderState folder, InterfaceSample.Model.NodeDef node, string folderFullPath = "")
        {
            if (node == null)
            {
                return;
            }

            foreach (InterfaceSample.Model.NodeDef childGroup in node.LeftFolder)
            {
                string str;
                if (!string.IsNullOrEmpty(folderFullPath))
                {
                    str = string.Format("{0}_{1}", folderFullPath, childGroup.name);
                }
                else
                {
                    str = string.Format("{0}_{1}", childGroup.ParentName, childGroup.name);
                }

                FolderState folderStae = CreateFolder(folder, str, childGroup.name);
                BrowseGroup(folderStae, childGroup, str);
            }

            foreach (InterfaceSample.Model.NodeVariable nv in node.RightVariable)
            {
                string Device_scalarSimulation = string.Format("{0}_{1}", folder.BrowseName.Name, nv.name);
                if(!XXXXToBaseDataVariableDic.ContainsKey(nv.nameFullPath))  XXXXToBaseDataVariableDic.Add(nv.nameFullPath, Device_scalarSimulation);
                if (!baseDataVariableToXXXXDic.ContainsKey(Device_scalarSimulation)) baseDataVariableToXXXXDic.Add(Device_scalarSimulation, nv.nameFullPath);
                CreateDynamicVariable(folder, Device_scalarSimulation, nv.name, BuiltInType.Variant, ValueRanks.Scalar);
            }
        }
 1     public class NodeDef
 2     {
 3         public string name = String.Empty;
 4         public string ParentName = String.Empty;
 5         public List<NodeDef> LeftFolder = new List<NodeDef>();//String.Empty;
 6         public List<NodeVariable> RightVariable = new List<NodeVariable>();//String.Empty;
 7         public string nameAbsolutePath = string.Empty;
 8     }
 9 
10     public class NodeVariable
11     {
12         public string name = string.Empty;
13 
14         public string nameFullPath = string.Empty;
15     }

 

OPC UA 统一架构 (二),讲讲如何读取和修改值。码杰-博客园

 

 

 

posted on 2021-01-29 21:35  码杰  阅读(4011)  评论(1编辑  收藏  举报