OpcUaHelper
一个通用的opc ua客户端类库,基于.net 4.6.1创建,基于官方opc ua基金会跨平台库创建,封装了节点读写,批量节点读写,引用读取,特性读取,历史数据读取,方法调用,节点订阅,批量订阅等操作。还提供了一个节点浏览器工具。
KEPServerEX
第三方的OPC服务器,各不同厂家多种设备下位PLC与上位机之间通讯。KEPServerEX是行业领先的通信平台,用于向您的所有应用程序提供单一来源的工业自动化数据。该平台的设计使用户能够通过一个直观的用户界面来连接、管理、监视和控制不同的自动化设备和软件应用程序。KEPServerEX 利用 OPC(自动化行业的互操作性标准)和以 IT 为中心的通信协议(如 SNMP、ODBC 和 Web 服务)为用户提供单一来源的工业数据。要开始使用,只需下载 KEPServerEX,然后从Kepware 的包含 150 多个设备驱动程序、客户端驱动程序和高级选项的库中进行选择,以满足您独特的工业控制系统需求。
以下基于开源的OpcUaHelper自带的demo,修改了界面,测试了部分功能。
主界面
C# 通过OPC UA 访问KEPServerEx6 演示视频
部分代码:
//同步读取
private void button1_Click( object sender, EventArgs e )
{
if (cbo函数Nodeid.Text=="")
{
return;
}
DataValue dataValue = m_OpcUaClient.ReadNode( new NodeId(cbo函数Nodeid.Text ) );
if (dataValue.WrappedValue.Value != null)
{
txtvalue.Text = dataValue.WrappedValue.Value.ToString();
}
//test();
}
//单节点订阅
private void button2_Click( object sender, EventArgs e )
{
// sub
m_OpcUaClient.AddSubscription( "A", textBox4.Text, SubCallback );
}
//取消订阅 关键字A
private void button3_Click( object sender, EventArgs e )
{
// remove sub
m_OpcUaClient.RemoveSubscription( "A" );
}
//订阅回调
private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args )
{
if (InvokeRequired)
{
Invoke( new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>( SubCallback ), key, monitoredItem, args );
return;
}
if (key == "A")
{
// 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
if (notification != null)
{
textBox3.Text = notification.Value.WrappedValue.Value.ToString( );
}
}
else if(key == "B")
{
// 需要区分出来每个不同的节点信息
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[0])
{
textBox5.Text = notification.Value.WrappedValue.Value.ToString( );
}
else if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[1])
{
textBox9.Text = notification.Value.WrappedValue.Value.ToString( );
}
else if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[2])
{
textBox10.Text = notification.Value.WrappedValue.Value.ToString( );
}
}
}
// 缓存的批量订阅的节点
private string[] MonitorNodeTags = null;
//多节点订阅
private void button5_Click( object sender, EventArgs e )
{
// 多个节点的订阅
MonitorNodeTags = new string[]
{
textBox6.Text,
textBox7.Text,
textBox8.Text,
};
m_OpcUaClient.AddSubscription( "B", MonitorNodeTags, SubCallback );
}
//取消订阅
private void button4_Click( object sender, EventArgs e )
{
// 取消多个节点的订阅
m_OpcUaClient.RemoveSubscription( "B" );//关键字 B
}
//异步读取节点
private async void buttonReadNodeAsync_Click(object sender, EventArgs e)
{
Opc.Ua.DataValue value = m_OpcUaClient.ReadNode(cboNodeid.Text);//SelectedItem.ToString()
if (value.Value == null)
{
return;
}
// 一个数据的类型是不是数组由 value.WrappedValue.TypeInfo.ValueRank 来决定的
// -1 说明是一个数值
// 1 说明是一维数组,如果类型BuiltInType是Int32,那么实际是int[]
// 2 说明是二维数组,如果类型BuiltInType是Int32,那么实际是int[,]
if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int32)
{
if (value.WrappedValue.TypeInfo.ValueRank == -1)
{
int temp = (int)value.WrappedValue.Value; // 最终值
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.ValueRank == 1)
{
int[] temp = (int[])value.WrappedValue.Value; // 最终值
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.ValueRank == 2)
{
int[,] temp = (int[,])value.WrappedValue.Value; // 最终值
txtvalue.Text = temp.ToString();
}
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.UInt32)
{
uint temp = (uint)value.WrappedValue.Value; // 数组的情况参照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Float)
{
float temp = (float)value.WrappedValue.Value; // 数组的情况参照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.String)
{
string temp = (string)value.WrappedValue.Value; // 数组的情况参照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.DateTime)
{
DateTime temp = (DateTime)value.WrappedValue.Value; // 数组的情况参照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
short temp = (short)value.WrappedValue.Value; // 数组的情况参照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Double)
{
double temp = (double)value.WrappedValue.Value; // 数组的情况参照上面的例子
txtvalue.Text = temp.ToString();
}
}
//浏览节点引用
private void buttonBrowseNodeReference_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
try
{
//ReferenceDescription[] references = m_OpcUaClient.BrowseNodeReference("ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端");
ReferenceDescription[] references = m_OpcUaClient.BrowseNodeReference(comboBox1.SelectedItem.ToString());
richTextBox1.AppendText(string.Format("{0,-30}", "NodeClass")+ string.Format("{0,-30}","BrowseName")+ string.Format("{0,-40}","DisplayName")+ string.Format("{0,-40}","NodeId")+"\r\n");
foreach (var item in references)
{
Console.Write(string.Format("{0,-30}", item.NodeClass));
Console.Write(string.Format("{0,-30}", item.BrowseName));
Console.Write(string.Format("{0,-20}", item.DisplayName));
Console.WriteLine(string.Format("{0,-20}", item.NodeId.ToString()));
/* string s1 = "中文";
string s2 = "语言学English";
Console.WriteLine("{0}{1}", padRightEx(s1, 20), "hello");
Console.WriteLine("{0}{1}", padRightEx(s2, 20), "hello");*/
//richTextBox1.AppendText(string.Format("{0,-30}", item.NodeClass));
//richTextBox1.AppendText(string.Format("{0,-30}", item.BrowseName));
//richTextBox1.AppendText(string.Format("{0,-20}", item.DisplayName));
//richTextBox1.AppendText(string.Format("{0,-20}", item.NodeId.ToString()) + "\r\n");
richTextBox1.AppendText(string.Format("{0}{1}{2}{3}\r\n", padRightEx(item.NodeClass.ToString().Trim(), 30), padRightEx(item.BrowseName.ToString().TrimStart(), 40), padRightEx(item.DisplayName.ToString().TrimStart(), 40), padRightEx(item.NodeId.ToString().TrimStart(), 100)));
}
;
// 输出如下
// NodeClass BrowseName DisplayName NodeId
// Variable 2:温度 温度 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/温度
// Variable 2:风俗 风俗 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/风俗
// Variable 2:转速 转速 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/转速
// Variable 2:机器人关节 机器人关节 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/机器人关节
// Variable 2:cvsdf cvsdf ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/cvsdf
// Variable 2:条码 条码 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/条码
// Variable 2:开关量 开关量 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/开关量
}
catch (Exception ex)
{
ClientUtils.HandleException(this.Text, ex);
}
}
//启用设备属性-操作模式-模拟 可读取节点属性
private void buttonReadNoteAttributes_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
try
{
//OpcNodeAttribute[] nodeAttributes = m_OpcUaClient.ReadNoteAttributes("ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.温度");
OpcNodeAttribute[] nodeAttributes = m_OpcUaClient.ReadNoteAttributes(cboNodeid.Text);
richTextBox1.AppendText(string.Format("{0,-30}", "Name") + string.Format("{0,-40}", "Type") + string.Format("{0,-40}", "StatusCode") + string.Format("{0,-40}", "Value") + "\r\n");
foreach (var item in nodeAttributes)
{
//Console.Write(string.Format("{0,-30}", item.Name));
//Console.Write(string.Format("{0,-20}", item.Type));
//Console.Write(string.Format("{0,-20}", item.StatusCode));
//Console.WriteLine(string.Format("{0,20}", item.Value));
richTextBox1.AppendText(string.Format("{0}{1}{2}{3}\r\n", padRightEx(item.Name.ToString().Trim(), 30), padRightEx(item.Type.ToString().Trim(), 40), padRightEx(item.StatusCode.ToString().Trim(), 40), padRightEx(item.Value.ToString().Trim(), 100)));
}
//写入操作
private void buttonWrite_Click(object sender, EventArgs e)
{
try
{
// 此处演示写入一个short,2个float类型的数据批量写入操作。启用设备模拟后写入成功。但是很快就被模拟的0覆盖
bool success = m_OpcUaClient.WriteNodes(new string[] {
"ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.温度",
"ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.风俗",
"ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.转速"},
new object[] {
(short)1234,
123.456f,
123f
});
//bool success = m_OpcUaClient.WriteNode("ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.风俗", 123.456f);
if (success)
{
MessageBox.Show("写入成功!");
}
else
{
// 写入失败,一个失败即为失败
MessageBox.Show("写入失败!");
}
}
catch (Exception ex)
{
ClientUtils.HandleException(this.Text, ex);
}
}
参考:
https://github.com/dathlin/OpcUaHelper
https://opcfoundation.cn/about/opc-technologies/opc-ua/
https://blog.csdn.net/xiaochenXIHUA/article/details/117070003
https://www.cnblogs.com/bgh408/p/10887580.html
https://blog.csdn.net/Vampire_1122/article/details/119025477
https://cloud.tencent.com/developer/article/1899065
https://blog.csdn.net/han_better/article/details/83034023
https://www.douban.com/note/837168906/?_i=61791832R3OOz3文章来源:https://www.toymoban.com/news/detail-410483.html
The End文章来源地址https://www.toymoban.com/news/detail-410483.html
到了这里,关于【OPC UA】C# 通过OpcUaHelper建立OPC客户端访问KEPServerEx6 OPC服务器数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!