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));
}
}