ToB企服应用市场:ToB评测及商务社交产业平台

标题: 使用C#开发OPC UA服务器 [打印本页]

作者: 汕尾海湾    时间: 2024-6-19 22:58
标题: 使用C#开发OPC UA服务器
OPC基金会提供了OPC UA .NET标准库以及示例步伐,但官方文档过于简单,光看官方文档和示例步伐很难弄懂OPC UA .NET标准库怎么用,花了不少时间探索才略微弄懂如何使用,以下记录如何从一个控制台步伐开发一个OPC UA服务器。
安装Nuget包

安装OPCFoundation.NetStandard.Opc.Ua

主步伐

修改Program.cs代码如下:
  1. using Opc.Ua;
  2. using Opc.Ua.Configuration;
  3. using Opc.Ua.Server;
  4. namespace SampleOpcUaServer
  5. {
  6.     internal class Program
  7.     {
  8.         static void Main(string[] args)
  9.         {
  10.             // 启动OPC UA服务器
  11.             ApplicationInstance application = new ApplicationInstance();
  12.             application.ConfigSectionName = "OpcUaServer";
  13.             application.LoadApplicationConfiguration(false).Wait();
  14.             application.CheckApplicationInstanceCertificate(false, 0).Wait();
  15.             var server = new StandardServer();
  16.             var nodeManagerFactory = new NodeManagerFactory();
  17.             server.AddNodeManager(nodeManagerFactory);
  18.             application.Start(server).Wait();
  19.             // 模拟数据
  20.             var nodeManager = nodeManagerFactory.NodeManager;
  21.             var simulationTimer = new System.Timers.Timer(1000);
  22.             var random = new Random();
  23.             simulationTimer.Elapsed += (sender, EventArgs) =>
  24.             {
  25.                 nodeManager?.UpdateValue("ns=2;s=Root_Test", random.NextInt64());
  26.             };
  27.             simulationTimer.Start();
  28.             // 输出OPC UA Endpoint
  29.             Console.WriteLine("Endpoints:");
  30.             foreach (var endpoint in server.GetEndpoints().DistinctBy(x => x.EndpointUrl))
  31.             {
  32.                 Console.WriteLine(endpoint.EndpointUrl);
  33.             }
  34.             Console.WriteLine("按Enter添加新变量");
  35.             Console.ReadLine();
  36.             // 添加新变量
  37.             nodeManager?.AddVariable("ns=2;s=Root", "Test2", (int)BuiltInType.Int16, ValueRanks.Scalar);
  38.             Console.WriteLine("已添加变量");
  39.             Console.ReadLine();
  40.         }
  41.     }
  42. }
复制代码
上述代码中:
OPC UA配置文件

新建OpcUaServer.Config.xml文件。

在属性中设为“始终赋值”。

内容如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <ApplicationConfiguration
  3.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.   xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
  5.   xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
  6. >
  7.         <ApplicationName>Sample OPC UA Server</ApplicationName>
  8.         <ApplicationUri>urn:localhost:UA:OpcUaServer</ApplicationUri>
  9.         <ProductUri>uri:opcfoundation.org:OpcUaServer</ProductUri>
  10.         <ApplicationType>Server_0</ApplicationType>
  11.         <SecurityConfiguration>
  12.                
  13.                 <ApplicationCertificate>
  14.                         <StoreType>Directory</StoreType>
  15.                         <StorePath>%CommonApplicationData%\OPC Foundation\pki\own</StorePath>
  16.                         <SubjectName>CN=Sample Opc Ua Server, C=US, S=Arizona, O=SomeCompany, DC=localhost</SubjectName>
  17.                 </ApplicationCertificate>
  18.                
  19.                 <TrustedIssuerCertificates>
  20.                         <StoreType>Directory</StoreType>
  21.                         <StorePath>%CommonApplicationData%\OPC Foundation\pki\issuer</StorePath>
  22.                 </TrustedIssuerCertificates>
  23.                
  24.                 <TrustedPeerCertificates>
  25.                         <StoreType>Directory</StoreType>
  26.                         <StorePath>%CommonApplicationData%\OPC Foundation\pki\trusted</StorePath>
  27.                 </TrustedPeerCertificates>
  28.                
  29.                 <RejectedCertificateStore>
  30.                         <StoreType>Directory</StoreType>
  31.                         <StorePath>%CommonApplicationData%\OPC Foundation\pki\rejected</StorePath>
  32.                 </RejectedCertificateStore>
  33.         </SecurityConfiguration>
  34.         <TransportConfigurations></TransportConfigurations>
  35.         <TransportQuotas>
  36.                 <OperationTimeout>600000</OperationTimeout>
  37.                 <MaxStringLength>1048576</MaxStringLength>
  38.                 <MaxByteStringLength>1048576</MaxByteStringLength>
  39.                 <MaxArrayLength>65535</MaxArrayLength>
  40.                 <MaxMessageSize>4194304</MaxMessageSize>
  41.                 <MaxBufferSize>65535</MaxBufferSize>
  42.                 <ChannelLifetime>300000</ChannelLifetime>
  43.                 <SecurityTokenLifetime>3600000</SecurityTokenLifetime>
  44.         </TransportQuotas>
  45.         <ServerConfiguration>
  46.                 <BaseAddresses>
  47.                         <ua:String>https://localhost:62545/OpcUaServer/</ua:String>
  48.                         <ua:String>opc.tcp://localhost:62546/OpcUaServer</ua:String>
  49.                 </BaseAddresses>
  50.                 <SecurityPolicies>
  51.                         <ServerSecurityPolicy>
  52.                                 <SecurityMode>SignAndEncrypt_3</SecurityMode>
  53.                                 <SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</SecurityPolicyUri>
  54.                         </ServerSecurityPolicy>
  55.                         <ServerSecurityPolicy>
  56.                                 <SecurityMode>None_1</SecurityMode>
  57.                                 <SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#None</SecurityPolicyUri>
  58.                         </ServerSecurityPolicy>
  59.                         <ServerSecurityPolicy>
  60.                                 <SecurityMode>Sign_2</SecurityMode>
  61.                                 <SecurityPolicyUri></SecurityPolicyUri>
  62.                         </ServerSecurityPolicy>
  63.                         <ServerSecurityPolicy>
  64.                                 <SecurityMode>SignAndEncrypt_3</SecurityMode>
  65.                                 <SecurityPolicyUri></SecurityPolicyUri>
  66.                         </ServerSecurityPolicy>
  67.                 </SecurityPolicies>
  68.                 <UserTokenPolicies>
  69.                         <ua:UserTokenPolicy>
  70.                                 <ua:TokenType>Anonymous_0</ua:TokenType>
  71.                         </ua:UserTokenPolicy>
  72.                         <ua:UserTokenPolicy>
  73.                                 <ua:TokenType>UserName_1</ua:TokenType>
  74.                         </ua:UserTokenPolicy>
  75.                         <ua:UserTokenPolicy>
  76.                                 <ua:TokenType>Certificate_2</ua:TokenType>
  77.                         </ua:UserTokenPolicy>
  78.                        
  79.                 </UserTokenPolicies>
  80.                 <DiagnosticsEnabled>false</DiagnosticsEnabled>
  81.                 <MaxSessionCount>100</MaxSessionCount>
  82.                 <MinSessionTimeout>10000</MinSessionTimeout>
  83.                 <MaxSessionTimeout>3600000</MaxSessionTimeout>
  84.                 <MaxBrowseContinuationPoints>10</MaxBrowseContinuationPoints>
  85.                 <MaxQueryContinuationPoints>10</MaxQueryContinuationPoints>
  86.                 <MaxHistoryContinuationPoints>100</MaxHistoryContinuationPoints>
  87.                 <MaxRequestAge>600000</MaxRequestAge>
  88.                 <MinPublishingInterval>100</MinPublishingInterval>
  89.                 <MaxPublishingInterval>3600000</MaxPublishingInterval>
  90.                 <PublishingResolution>50</PublishingResolution>
  91.                 <MaxSubscriptionLifetime>3600000</MaxSubscriptionLifetime>
  92.                 <MaxMessageQueueSize>10</MaxMessageQueueSize>
  93.                 <MaxNotificationQueueSize>100</MaxNotificationQueueSize>
  94.                 <MaxNotificationsPerPublish>1000</MaxNotificationsPerPublish>
  95.                 <MinMetadataSamplingInterval>1000</MinMetadataSamplingInterval>
  96.                 <AvailableSamplingRates>
  97.                         <SamplingRateGroup>
  98.                                 <Start>5</Start>
  99.                                 <Increment>5</Increment>
  100.                                 <Count>20</Count>
  101.                         </SamplingRateGroup>
  102.                         <SamplingRateGroup>
  103.                                 <Start>100</Start>
  104.                                 <Increment>100</Increment>
  105.                                 <Count>4</Count>
  106.                         </SamplingRateGroup>
  107.                         <SamplingRateGroup>
  108.                                 <Start>500</Start>
  109.                                 <Increment>250</Increment>
  110.                                 <Count>2</Count>
  111.                         </SamplingRateGroup>
  112.                         <SamplingRateGroup>
  113.                                 <Start>1000</Start>
  114.                                 <Increment>500</Increment>
  115.                                 <Count>20</Count>
  116.                         </SamplingRateGroup>
  117.                 </AvailableSamplingRates>
  118.                 <MaxRegistrationInterval>30000</MaxRegistrationInterval>
  119.                 <NodeManagerSaveFile>OpcUaServer.nodes.xml</NodeManagerSaveFile>
  120.         </ServerConfiguration>
  121.         <TraceConfiguration>
  122.                 <OutputFilePath>Logs\SampleOpcUaServer.log</OutputFilePath>
  123.                 <DeleteOnLoad>true</DeleteOnLoad>
  124.                
  125.                
  126.                
  127.                
  128.                
  129.                 <TraceMasks>515</TraceMasks>
  130.                
  131.                
  132.                
  133.                
  134.                
  135.                
  136.         </TraceConfiguration>
  137. </ApplicationConfiguration>
复制代码
需要关注的内容有:
NodeManagerFactory

新建NodeManagerFactory类,OPC UA server将调用该类的Create方法创建INodeManager实现类,而INodeManager实现类用于管理OPC UA地点空间。内容如下:
  1. using Opc.Ua;
  2. using Opc.Ua.Server;
  3. namespace SampleOpcUaServer
  4. {
  5.     internal class NodeManagerFactory : INodeManagerFactory
  6.     {
  7.         public NodeManager? NodeManager { get; private set; }
  8.         public StringCollection NamespacesUris => new StringCollection() { "http://opcfoundation.org/OpcUaServer" };
  9.         public INodeManager Create(IServerInternal server, ApplicationConfiguration configuration)
  10.         {
  11.             if (NodeManager != null)
  12.                 return NodeManager;
  13.             NodeManager = new NodeManager(server, configuration, NamespacesUris.ToArray());
  14.             return NodeManager;
  15.         }
  16.     }
  17. }
复制代码
NodeManager

新建NodeManager类:
  1. using Opc.Ua;
  2. using Opc.Ua.Server;
  3. namespace SampleOpcUaServer
  4. {
  5.     internal class NodeManager : CustomNodeManager2
  6.     {
  7.         public NodeManager(IServerInternal server, params string[] namespaceUris)
  8.             : base(server, namespaceUris)
  9.         {
  10.         }
  11.         public NodeManager(IServerInternal server, ApplicationConfiguration configuration, params string[] namespaceUris)
  12.             : base(server, configuration, namespaceUris)
  13.         {
  14.         }
  15.         protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
  16.         {
  17.             FolderState root = CreateFolder(null, "Root");
  18.             root.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder); // 将节点添加到服务器根节点
  19.             root.EventNotifier = EventNotifiers.SubscribeToEvents;
  20.             AddRootNotifier(root);
  21.             CreateVariable(root, "Test", BuiltInType.Int64, ValueRanks.Scalar);
  22.             return new NodeStateCollection(new List<NodeState> { root });
  23.         }
  24.         protected virtual FolderState CreateFolder(NodeState? parent, string name)
  25.         {
  26.             string path = parent?.NodeId.Identifier is string id ? id + "_" + name : name;
  27.             FolderState folder = new FolderState(parent);
  28.             folder.SymbolicName = name;
  29.             folder.ReferenceTypeId = ReferenceTypes.Organizes;
  30.             folder.TypeDefinitionId = ObjectTypeIds.FolderType;
  31.             folder.NodeId = new NodeId(path, NamespaceIndex);
  32.             folder.BrowseName = new QualifiedName(path, NamespaceIndex);
  33.             folder.DisplayName = new LocalizedText("en", name);
  34.             folder.WriteMask = AttributeWriteMask.None;
  35.             folder.UserWriteMask = AttributeWriteMask.None;
  36.             folder.EventNotifier = EventNotifiers.None;
  37.             if (parent != null)
  38.             {
  39.                 parent.AddChild(folder);
  40.             }
  41.             return folder;
  42.         }
  43.         protected virtual BaseDataVariableState CreateVariable(NodeState? parent, string name, BuiltInType dataType, int valueRank)
  44.         {
  45.             return CreateVariable(parent, name, (uint)dataType, valueRank);
  46.         }
  47.         protected virtual BaseDataVariableState CreateVariable(NodeState? parent, string name, NodeId dataType, int valueRank)
  48.         {
  49.             string path = parent?.NodeId.Identifier is string id ? id + "_" + name : name;
  50.             BaseDataVariableState variable = new BaseDataVariableState(parent);
  51.             variable.SymbolicName = name;
  52.             variable.ReferenceTypeId = ReferenceTypes.Organizes;
  53.             variable.TypeDefinitionId = VariableTypeIds.BaseDataVariableType;
  54.             variable.NodeId = new NodeId(path, NamespaceIndex);
  55.             variable.BrowseName = new QualifiedName(path, NamespaceIndex);
  56.             variable.DisplayName = new LocalizedText("en", name);
  57.             variable.WriteMask = AttributeWriteMask.None;
  58.             variable.UserWriteMask = AttributeWriteMask.None;
  59.             variable.DataType = dataType;
  60.             variable.ValueRank = valueRank;
  61.             variable.AccessLevel = AccessLevels.CurrentReadOrWrite;
  62.             variable.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
  63.             variable.Historizing = false;
  64.             variable.Value = Opc.Ua.TypeInfo.GetDefaultValue(dataType, valueRank, Server.TypeTree);
  65.             variable.StatusCode = StatusCodes.Good;
  66.             variable.Timestamp = DateTime.UtcNow;
  67.             if (valueRank == ValueRanks.OneDimension)
  68.             {
  69.                 variable.ArrayDimensions = new ReadOnlyList<uint>(new List<uint> { 0 });
  70.             }
  71.             else if (valueRank == ValueRanks.TwoDimensions)
  72.             {
  73.                 variable.ArrayDimensions = new ReadOnlyList<uint>(new List<uint> { 0, 0 });
  74.             }
  75.             if (parent != null)
  76.             {
  77.                 parent.AddChild(variable);
  78.             }
  79.             return variable;
  80.         }
  81.         public void UpdateValue(NodeId nodeId, object value)
  82.         {
  83.             var variable = (BaseDataVariableState)FindPredefinedNode(nodeId, typeof(BaseDataVariableState));
  84.             if (variable != null)
  85.             {
  86.                 variable.Value = value;
  87.                 variable.Timestamp = DateTime.UtcNow;
  88.                 variable.ClearChangeMasks(SystemContext, false);
  89.             }
  90.         }
  91.                 public void AddFolder(NodeId parentId, string name)
  92.                 {
  93.                     var node = Find(parentId);
  94.                     if (node != null)
  95.                     {
  96.                         CreateFolder(node, name);
  97.                         AddPredefinedNode(SystemContext, node);
  98.                     }
  99.                 }
  100.                 public void AddVariable(NodeId parentId, string name, BuiltInType dataType, int valueRank)
  101.                 {
  102.                     AddVariable(parentId, name, (uint)dataType, valueRank);
  103.                 }
  104.                 public void AddVariable(NodeId parentId, string name, NodeId dataType, int valueRank)
  105.                 {
  106.                     var node = Find(parentId);
  107.                     if (node != null)
  108.                     {
  109.                         CreateVariable(node, name, dataType, valueRank);
  110.                         AddPredefinedNode(SystemContext, node);
  111.                     }
  112.                 }
  113.     }
  114. }
复制代码
上述代码中:
测试服务器

比较好用的测试工具有:
以下用OpcExpert测试。
浏览本地盘算机可发现OPC UA服务器,可看到添加的Root节点和Test变量,Test变量的值会每秒更新。

源码地点:https://github.com/Yada-Yang/SampleOpcUaServer

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4