UA全称是unified architecture(统一架构)。为了应对标准化和跨平台的趋势,为了更好地推广OPC,OPC基金会近些年在之前OPC成功应用的基础上推出了一个新的OPC标准-OPC UA。OPC UA接口协议包含了之前的 A&E, DA,OPC XML DA or HDA,只使用一个地址空间就能访问之前所有的对象,而且不受WINDOWS平台限制,因为它是从传输层以上来定义的,导致了灵活性和安全性比之前的OPC都提升了。
opc ua支持c/s模式,同时也支持类似mqtt的发布订阅模式,通常各种设备作为opc ua的服务端提供各种服务。
信息模型(Information model)
OPC UA 使用了对象(objects)作为过程系统表示数据和活动的基础。对象包含了变量,事件和方法,它们通过引用(reference)来互相连接。
OPC UA 信息模型是节点的网络(Network of Node),或者称为结构化图(graph),由节点(node)和引用(References)组成,这种结构图称之为OPC UA 的地址空间。这种图形结构可以描述各种各样的结构化信息(对象)。
地址空间要点:
对象模型
节点模型
opc ua定义了8种类型的节点
对象(Object)
对象类型(Object Type)
变量(Variable)
变量类型(Variable Type)
方法(Method)
视图(View)
引用(Reference)
数据类型(Data Type)
节点模型要点
属性 | 数据类型 | 说明 |
---|---|---|
NodeId | NodeId | 在OPC UA服务器内唯一确定的一个节点,并且在OPC UA服务器中定位该节点 |
NodeClass | Int32 | 该节点的类型(上面列出的8种之一) |
BrowseName | QualifiedName | 浏览OPC UA服务器事定义的节点。它是非本地化的 |
DisplayName | LocalizedText | 包含节点的名字,用来在用户接口中显示名字,本地化 |
Description | LocalizedText | 本地化的描述(可选) |
WriteMask | Uint32 | 指示哪个节点属性是可写的,即可被OPC UA客户端修改(可选) |
UserWriteMask | Uint32 | 指示哪个节点属性可以被当前连接到服务器上的用户修改(可选 |
引用模型
节点(nodes) : 共计有8种节点(对象,对象类型,变量,变量类型,视图,方法,引用,数据类型)
服务可以看成是OPC UA服务器提供的API集合,OPC UA与定义了37个标准服务,常用的服务有:
可以获取和修改服务器指定节点指定属性的值
执行服务器上指定节点的方法
可以监控服务器数据的变化
右键OPC UA配制
输入管理员姓名与密码
主要配置以上信息
如果需要通过账号访问,需要设置中用户管理器下添加用户信息。
如果需要匿名登录,一定要配置以下信息
连接OPCUA Server
C#private async void btnConnect_Click(object sender, EventArgs e)
{
m_OpcUaClient = new OpcUaClient();
try
{
m_OpcUaClient.UserIdentity = new UserIdentity(new AnonymousIdentityToken());
//m_OpcUaClient.UserIdentity = new UserIdentity("opcuser", "123");
m_OpcUaClient.OpcStatusChange += M_OpcUaClient_OpcStatusChange;
await m_OpcUaClient.ConnectServer("opc.tcp://IDIO-002:49320");
this.Text = "连接成功!";
}
catch (Exception ex)
{
this.Text = "连接失败!";
}
}
bool isConnected = false;
/// <summary>
/// Opc状态事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void M_OpcUaClient_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
{
if (m_OpcUaClient != null)
{
if (e.Error)
{
isConnected = false;
return;
}
else
{
isConnected = true;
}
}
}
读取信息
C#private void btnRead_Click(object sender, EventArgs e)
{
DataValue dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.B1"));
uPanel1.Value = dataValue.Value.ToString();
uPanel1.Title ="B1";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.B2"));
uPanel2.Value = dataValue.Value.ToString();
uPanel2.Title = "B2";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.B3"));
uPanel3.Value = dataValue.Value.ToString();
uPanel3.Title = "B3";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.F1"));
uPanel4.Value = dataValue.Value.ToString();
uPanel4.Title = "F1";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.I1"));
uPanel5.Value = dataValue.Value.ToString();
uPanel5.Title = "I1";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.L1"));
uPanel6.Value = dataValue.Value.ToString();
uPanel6.Title = "L1";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.S1"));
uPanel7.Value = dataValue.Value.ToString();
uPanel7.Title = "S1";
dataValue = m_OpcUaClient.ReadNode(new NodeId("ns=2;s=Chanel1.Device1.S2"));
uPanel8.Value = dataValue.Value.ToString();
uPanel8.Title = "S2";
}
批量读取
C#private void btnBatchRead_Click(object sender, EventArgs e)
{
List<NodeId> nodeIds = new List<NodeId>();
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.B1"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.B2"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.B3"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.F1"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.I1"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.L1"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.S1"));
nodeIds.Add(new NodeId("ns=2;s=Chanel1.Device1.S2"));
// dataValues按顺序定义的值,每个值里面需要重新判断类型
List<DataValue> dataValues = m_OpcUaClient.ReadNodes(nodeIds.ToArray());
// 然后遍历你的数据信息
int idx = 0;
foreach (var dataValue in dataValues)
{
// 获取你的实际的数据
object value = dataValue.WrappedValue.Value == null ? "" : dataValue.WrappedValue.Value;
switch (idx)
{
case 0:
uPanel1.Value = value.ToString();
break;
case 1:
uPanel2.Value = value.ToString();
break;
case 2:
uPanel3.Value = value.ToString();
break;
case 3:
uPanel4.Value = value.ToString();
break;
case 4:
uPanel5.Value = value.ToString();
break;
case 5:
uPanel6.Value = value.ToString();
break;
case 6:
uPanel7.Value = value.ToString();
break;
case 7:
uPanel8.Value = value.ToString();
break;
default:
break;
}
idx++;
}
}
同类型数据读取
C#private void btnBatchSameRead_Click(object sender, EventArgs e)
{
// 如果你批量读取的值的类型都是一样的,比如bool,那么有简便的方式
List<string> tags = new List<string>();
tags.Add("ns=2;s=Chanel1.Device1.B1");
tags.Add("ns=2;s=Chanel1.Device1.B2");
tags.Add("ns=2;s=Chanel1.Device1.B3");
// 按照顺序定义的值
List<bool> values = m_OpcUaClient.ReadNodes<bool>(tags.ToArray());
uPanel1.Value = values[0].ToString();
uPanel2.Value = values[1].ToString();
uPanel3.Value = values[2].ToString();
}
异步读
C#private async void btnAsyncRead_Click(object sender, EventArgs e)
{
uPanel1.Value = (await m_OpcUaClient.ReadNodeAsync<bool>("ns=2;s=Chanel1.Device1.B1")).ToString();
uPanel2.Value = (await m_OpcUaClient.ReadNodeAsync<bool>("ns=2;s=Chanel1.Device1.B2")).ToString();
uPanel3.Value = (await m_OpcUaClient.ReadNodeAsync<bool>("ns=2;s=Chanel1.Device1.B3")).ToString();
uPanel4.Value = (await m_OpcUaClient.ReadNodeAsync<float>("ns=2;s=Chanel1.Device1.F1")).ToString();
uPanel5.Value = (await m_OpcUaClient.ReadNodeAsync<short>("ns=2;s=Chanel1.Device1.I1")).ToString();
uPanel6.Value = (await m_OpcUaClient.ReadNodeAsync<Int32>("ns=2;s=Chanel1.Device1.L1")).ToString();
uPanel7.Value = (await m_OpcUaClient.ReadNodeAsync<string>("ns=2;s=Chanel1.Device1.S1")).ToString();
uPanel8.Value = (await m_OpcUaClient.ReadNodeAsync<string>("ns=2;s=Chanel1.Device1.S2")).ToString();
}
异步写
C# m_OpcUaClient.WriteNode<string>("ns=2;s=Chanel1.Device1.S2", arg1);
批量写
C#private void btnBatchWrite_Click(object sender, EventArgs e)
{
bool issuccess = m_OpcUaClient.WriteNodes(new string[] {
"ns=2;s=Chanel1.Device1.S1",
"ns=2;s=Chanel1.Device1.S2"
},
new object[]
{
"A0001",
"A0002"
});
}
单个节点侦听
C#private void btnListen_Click(object sender, EventArgs e)
{
m_OpcUaClient.AddSubscription("KeyL1", "ns=2;s=Chanel1.Device1.L1", MonitorCallback);
}
private void MonitorCallback(string key, MonitoredItem arg2, MonitoredItemNotificationEventArgs arg3)
{
if (InvokeRequired)
{
Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(MonitorCallback), key, arg2, arg3);
return;
}
// 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
MonitoredItemNotification notification = arg3.NotificationValue as MonitoredItemNotification;
if (notification != null)
{
uPanel6.Value = notification.Value.WrappedValue.Value.ToString();
}
}
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!