OPC通讯-UA

OPC UA Nuget包:OPCFoundation.NetStandard.Opc.Ua

OPCUA登录

匿名方式登录

private static async Task<Session> AnonymousConnect()
{
    //ApplicationConfiguration configuration,
    //ConfiguredEndpoint endpoint :服务器地址,opc.tcp://10.127.64.99:49320
    //bool updateBeforeConnect:给个true就可以
    //string sessionName, 自定义Session名称
    //uint sessionTimeout,
    //IUserIdentity identity,
    //IList<string> preferredLocales, CancellationToken ct = default(CancellationToken)
    #region OPCUA匿名访问kepware
    //对于匿名访问的情况下,不需要configuration,只要有一个地址就行
    Session session = await Session.Create(
       new ApplicationConfiguration()
       {
           ClientConfiguration = new ClientConfiguration()
       },
       new ConfiguredEndpoint(
           null,
           new EndpointDescription("opc.tcp://10.127.64.99:49320")
           ),
      updateBeforeConnect: true,
      sessionName: "opc-test",
      sessionTimeout: 5000,
       identity: new UserIdentity(),
       preferredLocales: new List<string> { }
       );
    #endregion
    return session;
}

用户名登录

配置完重新初始化一下

//<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua" Version="1.5.374.176" />的使用方式
private static Task<Session> UserNameConnect(string endpoint, string username, string password)
{
    ApplicationConfiguration configuration = new ApplicationConfiguration();
    configuration.ClientConfiguration = new ClientConfiguration();

    CertificateValidator validator = new CertificateValidator();
    // 处理异常:Certificate is not trusted
    validator.CertificateValidation += (se, ev) =>
    {
        if (ev.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted)
            ev.Accept = true;
    };
    // 处理异常:Opc.Ua.ServiceResultException:“SHA1 signed certificates are not trusted.”
    validator.Update(new SecurityConfiguration
    {
        RejectSHA1SignedCertificates = false
    });
    configuration.CertificateValidator = validator;

    return Session.Create(
       configuration,
        new ConfiguredEndpoint(null, new EndpointDescription(endpoint)),
        true,
        "opc-test",
        5000,
        new UserIdentity(username, password),
        new List<string> { }
        );
}

带证书登录

private static Task<Session> CertificateConnect(string endpoint, string username, string password)
{
    ApplicationConfiguration configuration = new ApplicationConfiguration();
    configuration.ApplicationName = "InspurOpc";
    configuration.ClientConfiguration = new ClientConfiguration();

    CertificateValidator validator = new CertificateValidator();
    // 处理异常:Certificate is not trusted
    validator.CertificateValidation += (se, ev) =>
    {
        if (ev.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted)
            ev.Accept = true;
    };
    configuration.CertificateValidator = validator;

    //证书的生成:CommonApplicationData : C:\\ProgramData
    configuration.SecurityConfiguration = new SecurityConfiguration()
    {
        RejectSHA1SignedCertificates = false,
        ApplicationCertificate = new CertificateIdentifier()
        {
            StoreType = "Directory",
            StorePath = @"%CommonApplicationData%/InspurOpc/CertificateStores/MachineDefault",
            SubjectName = $"CN=InspurOpc,DC={Utils.GetHostName()}"
        },
        TrustedPeerCertificates = new CertificateTrustList(),
        TrustedIssuerCertificates = new CertificateTrustList(),
        RejectedCertificateStore = new CertificateTrustList(),
    };
    configuration.Validate(ApplicationType.Client).Wait();
    ApplicationInstance instance = new ApplicationInstance()
    {
        ApplicationConfiguration = configuration,
    };
    instance.CheckApplicationInstanceCertificate(false, 1024).ConfigureAwait(false);


    return Session.Create(
    configuration,
        new ConfiguredEndpoint(null, new EndpointDescription(endpoint)),
        true,
        "opc-test",
        5000,
        new UserIdentity(username, password),
        new List<string> { }
        );
}

执行完成后:存在安全证书,如果没有可以手动导入证书.der

生产环境:证书登录配合安全策略使用

安全策略

如果OPC UA中配置了安全策略,连接Session中需要指定签名和加密方式

return Session.Create(
	configuration,
	new ConfiguredEndpoint(null, new EndpointDescription(endpoint)
	{
		SecurityMode = MessageSecurityMode.SignAndEncrypt,
		//SecurityPolicyUri = "http://opcfoundation.org/UA/SecurityPolicy#Basic256",
		SecurityPolicyUri = "http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15",
	}),
	true,
	"opc-test",
	5000,
	new UserIdentity(username, password),
	new List<string> { }
	);

标签浏览节点

static void GetNodes(Browser browser, NodeId nodeId)
{
	var collections = browser.Browse(nodeId);
	foreach (var node in collections)
	{
		Console.WriteLine(node.DisplayName);
	}
}

同步读取

#region 同步读取
/**
 * RequestHeader requestHeader, 
 * double maxAge:设置读取的时候缓存数据还是实时数据,0:读取当前数据
 * TimestampsToReturn timestampsToReturn:时间戳
 * ReadValueIdCollection nodesToRead:需要读取的标签节点
 * out DataValueCollection results:读取到的标签数据
 * out DiagnosticInfoCollection diagnosticInfos:诊断信息
 * **/
var readValueIds = new ReadValueIdCollection();
readValueIds.Add(new ReadValueId()
{
    NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short1",
    AttributeId = Attributes.Value
});
readValueIds.Add(new ReadValueId()
{
    NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short2",
    AttributeId = Attributes.Value
});
readValueIds.Add(new ReadValueId()
{
    NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.ShortArray",
    AttributeId = Attributes.Value
});
session.Read(
    requestHeader: new RequestHeader(),
    maxAge: 0,
    timestampsToReturn: TimestampsToReturn.Both,
    nodesToRead: readValueIds,
    results: out DataValueCollection results,
    diagnosticInfos: out DiagnosticInfoCollection diagnosticInfos
    );
foreach (var item in results)
{
    //返回的节点数据是值类型
    if (item.WrappedValue.TypeInfo.ValueRank == -1)
    {
        Console.WriteLine(item.WrappedValue.Value);
    }
    //返回的节点数据是数组类型
    if (item.WrappedValue.TypeInfo.ValueRank == 1)
    {
        foreach (var v in (short[])item.Value)
        {
            Console.WriteLine(v);
        }
    }
}
#endregion

异步读取与订阅

异步读取

private static async void AsyncRead(Session session)
{
    var readValueIds = new ReadValueIdCollection();
    readValueIds.Add(new ReadValueId()
    {
        NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short1",
        AttributeId = Attributes.Value
    });
    readValueIds.Add(new ReadValueId()
    {
        NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short2",
        AttributeId = Attributes.Value
    });
    readValueIds.Add(new ReadValueId()
    {
        NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.ShortArray",
        AttributeId = Attributes.Value
    });
    /**
     * CancellationToken ct:
     * **/
    CancellationToken ct = new CancellationToken();
    //异步方式1
    ReadResponse response = await session.ReadAsync(
                requestHeader: new RequestHeader(),
                maxAge: 0,
                timestampsToReturn: TimestampsToReturn.Both,
                nodesToRead: readValueIds,
                ct: ct
                );
    DataValueCollection dataValues = response.Results;
    foreach (var item in dataValues)
    {
        //返回的节点数据是值类型
        if (item.WrappedValue.TypeInfo.ValueRank == -1)
        {
            Console.WriteLine(item.WrappedValue.Value);
        }
        //返回的节点数据是数组类型
        if (item.WrappedValue.TypeInfo.ValueRank == 1)
        {
            foreach (var v in (short[])item.Value)
            {
                Console.WriteLine(v);
            }
        }
    }


    //异步方式2:使用回调函数
    session.BeginRead(new RequestHeader(), 0, TimestampsToReturn.Both, readValueIds, new AsyncCallback(ReadCompleted), session);

    static void ReadCompleted(IAsyncResult asyncResult)
    {
        var session = asyncResult.AsyncState as Session;
        session!.EndRead(asyncResult, out DataValueCollection results, out         DiagnosticInfoCollection diagnosticInfos);

    }
}

订阅

private static void Subscription(Session session)
{
	var sub = session.DefaultSubscription;
	MonitoredItem mi = new MonitoredItem();
	mi.StartNodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short1";
	mi.Notification += Mi_Notification;
	sub.AddItem(mi);
	session.AddSubscription(sub);
    //监听订阅状态
    sub.PublishStatusChanged += (se, er) =>
    {
        if (sub.PublishingStopped)
        {
            //TODO:订阅已断开
        }
    };

	sub.Create();
}

private static void Mi_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
	var item = e.NotificationValue as MonitoredItemNotification;
	Console.WriteLine($"{monitoredItem.StartNodeId}:{item.Value}");
}

同步写

private static void SyncWrite(Session session)
{
    WriteValueCollection writeValues = new WriteValueCollection();
    writeValues.Add(new WriteValue()
    {
        NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short1",
        AttributeId = Attributes.Value,
        Value = new DataValue() { Value = (short)888 }//这个地方的数据类型要和实际地址的值严格一致
    });
    session.Write(new RequestHeader(), writeValues, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos);
    foreach (var item in results)
    {
        Console.WriteLine(StatusCode.IsGood(item));
    }
}

异步写

private static async void AsyncWrite(Session session)
{
    WriteValueCollection writeValues = new WriteValueCollection();
    writeValues.Add(new WriteValue()
    {
        NodeId = "ns=2;s=数据类型示例.16 位设备.K 寄存器.Short1",
        AttributeId = Attributes.Value,
        Value = new DataValue() { Value = (short)8888 }//这个地方的数据类型要和实际地址的值严格一致
    });
    CancellationToken cancellationToken = new CancellationToken();
    var writeResponse = await session.WriteAsync(new RequestHeader(), writeValues, cancellationToken);
    var results = writeResponse.Results;
    foreach (var item in results)
    {
        Console.WriteLine(StatusCode.IsGood(item));
    }
}

//第二种方式异步写:回调的方式
session.BeginWrite(new RequestHeader(), writeValues, new AsyncCallback(WriteCompleted), session);

static void WriteCompleted(IAsyncResult asyncResult)
{
    var session = asyncResult.AsyncState as Session;
    session!.EndWrite(asyncResult, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos);
    foreach (var item in results)
    {
        Console.WriteLine(StatusCode.IsGood(item));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值