You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Opc.Ua库创建OPC服务器:服务器无法被发现问题排查

OPC UA服务器创建失败排查(仅能看到localhost:4840连接)

我尝试使用OPC基金会的库创建OPC UA服务器,实现代码如下,但服务器未成功创建,仅能看到localhost:4840的连接,请问哪里操作有误?

实现代码

public class OPCServer
{
    public void StartServer()
    {
        ApplicationInstance application = new ApplicationInstance();
        application.ApplicationName = "MyServer";
        application.ApplicationType = ApplicationType.Server;

        // 加载应用配置
        ApplicationConfiguration config = new ApplicationConfiguration()
        {
            ApplicationName = "MyServer",
            ApplicationType = ApplicationType.Server,
            ApplicationUri = "urn:localhost:MyServer",
            ServerConfiguration = new ServerConfiguration
            {
                BaseAddresses = new StringCollection { "opc.tcp://localhost:4840/MyServer" },
                SecurityPolicies =
                {
                    new ServerSecurityPolicy()
                    {
                        SecurityMode = MessageSecurityMode.None,
                        SecurityPolicyUri = SecurityPolicies.None,
                    }
                },
            },
            SecurityConfiguration = new SecurityConfiguration
            {
                ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = "MyServer" },
                TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications" },
                RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates" },
                AutoAcceptUntrustedCertificates = true,
                AddAppCertToTrustedStore = true
            },
            TransportConfigurations = new TransportConfigurationCollection(),
            TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
            ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
            TraceConfiguration = new TraceConfiguration()
        };

        config.Validate(ApplicationType.Server).GetAwaiter().GetResult();
        if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
        {
            config.CertificateValidator.CertificateValidation += (s, e) =>
            {
                e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted);
            };
        }
        
        application.ApplicationConfiguration = config;

        // 检查应用证书
        bool haveAppCertificate = application.CheckApplicationInstanceCertificate(false, 0).Result;

        if (!haveAppCertificate)
        {
            throw new Exception("应用实例证书无效!");
        }

        // 启动服务器
        application.Start(new MySampleServer()).Wait();
        Console.ReadLine();
    }
}

public class MySampleServer : StandardServer
{
    private BaseDataVariableState m_myVariable;

    protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
    {
        List<INodeManager> nodeManagers = new List<INodeManager>
        {
            new MyNodeManager(server, configuration)
        };

        return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
    }

    protected override ServerProperties LoadServerProperties()
    {
        ServerProperties properties = new ServerProperties
        {
            ManufacturerName = "My Manufacturer",
            ProductName = "My Sample Server",
            ProductUri = "http://my.sample.server.com/",
            SoftwareVersion = Utils.GetAssemblySoftwareVersion(),
            BuildNumber = Utils.GetAssemblyBuildNumber(),
            BuildDate = Utils.GetAssemblyTimestamp()
        };

        return properties;
    }
}

public class MyNodeManager : CustomNodeManager2
{
    private FolderState myFolder;
    private BaseDataVariableState myVariable;

    public MyNodeManager(IServerInternal server, ApplicationConfiguration config)
        : base(server, config, Namespaces.OpcUa)
    {
        SystemContext.NodeIdFactory = this;

        myFolder = new FolderState(null)
        {
            SymbolicName = "MyFolder",
            NodeId = new NodeId("MyFolder", NamespaceIndex),
            BrowseName = new QualifiedName("MyFolder", NamespaceIndex),
            DisplayName = new LocalizedText("MyFolder"),
            TypeDefinitionId = ObjectTypeIds.FolderType,
            EventNotifier = EventNotifiers.None
        };

        // 创建变量
        myVariable = new BaseDataVariableState(myFolder)
        {
            SymbolicName = "MyVariable",
            NodeId = new NodeId("MyVariable", NamespaceIndex),
            BrowseName = new QualifiedName("MyVariable", NamespaceIndex),
            DisplayName = new LocalizedText("MyVariable"),
            DataType = DataTypeIds.Int32,
            ValueRank = ValueRanks.Scalar,
            AccessLevel = AccessLevels.CurrentReadOrWrite,
            UserAccessLevel = AccessLevels.CurrentReadOrWrite,
            Historizing = false,
            Value = 123,
            TypeDefinitionId = VariableTypeIds.BaseDataVariableType
        };

        myFolder.AddChild(myVariable);
    }

    public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
    {
        base.CreateAddressSpace(externalReferences);
        AddPredefinedNode(SystemContext, myFolder);
    }
}

可能的错误点及修复方案

1. 自定义节点未挂载到根对象文件夹

当前代码仅将myFolder添加到地址空间,但没有将其挂载到OPC UA服务器的根ObjectsFolder,导致客户端无法浏览到自定义节点。

修复:在MyNodeManager的CreateAddressSpace方法中添加根节点引用:

public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
{
    base.CreateAddressSpace(externalReferences);
    AddPredefinedNode(SystemContext, myFolder);

    // 获取根ObjectsFolder并添加引用
    var objectsFolder = FindPredefinedNode(ObjectIds.ObjectsFolder, typeof(FolderState)) as FolderState;
    if (objectsFolder != null)
    {
        // 建立从根文件夹到自定义文件夹的组织引用
        objectsFolder.AddReference(ReferenceTypes.Organizes, true, myFolder.NodeId);
        // 建立反向引用
        myFolder.AddReference(ReferenceTypes.Organizes, false, ObjectIds.ObjectsFolder);
    }
}

2. 命名空间配置问题

MyNodeManager构造函数中使用Namespaces.OpcUa作为命名空间索引,这是OPC UA的标准命名空间。如果你的自定义节点属于自定义命名空间,需要先注册命名空间URI。

修复:在MySampleServer的CreateMasterNodeManager中添加自定义命名空间:

protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
{
    // 注册自定义命名空间URI
    server.NamespaceUris.Add("http://your-company.com/MySampleServer");
    int customNamespaceIndex = server.NamespaceUris.Count - 1;

    List<INodeManager> nodeManagers = new List<INodeManager>
    {
        new MyNodeManager(server, configuration, customNamespaceIndex) // 修改构造函数传入自定义索引
    };

    return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
}

同时修改MyNodeManager的构造函数:

public MyNodeManager(IServerInternal server, ApplicationConfiguration config, int namespaceIndex)
    : base(server, config, namespaceIndex)
{
    // ... 原有代码 ...
}

3. 异步调用同步阻塞导致死锁

代码中使用.GetAwaiter().GetResult()和.Wait()同步阻塞异步方法,可能导致线程死锁,尤其是在有同步上下文的环境中(如WPF/WinForms)。

修复:将StartServer改为异步方法,使用await替代同步阻塞:

public async Task StartServer()
{
    ApplicationInstance application = new ApplicationInstance();
    application.ApplicationName = "MyServer";
    application.ApplicationType = ApplicationType.Server;

    // ... 原有配置代码 ...

    // 异步验证配置
    await config.Validate(ApplicationType.Server);
    if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
    {
        config.CertificateValidator.CertificateValidation += (s, e) =>
        {
            e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted);
        };
    }
    
    application.ApplicationConfiguration = config;

    // 异步检查证书
    bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
    if (!haveAppCertificate)
    {
        throw new Exception("应用实例证书无效!");
    }

    // 异步启动服务器
    await application.Start(new MySampleServer());
    Console.ReadLine();
}

4. 证书生成/加载异常

虽然设置了自动接受证书,但可能证书未正确生成或存储路径解析失败(%CommonApplicationData%在某些环境下可能无法自动解析)。

修复:

  • 手动替换证书路径为绝对路径,比如@"C:\ProgramData\OPC Foundation\CertificateStores\MachineDefault"
  • 强制生成证书,将CheckApplicationInstanceCertificate的第一个参数改为true:
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(true, 2048);

5. 端口占用或地址配置问题

确保端口4840未被其他程序占用,或者尝试更换BaseAddresses中的端口,比如opc.tcp://localhost:4841/MyServer。

内容的提问来源于stack exchange,提问作者Josef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 12:48:11