OpcUaHelper
一個(gè)通用的opc ua客戶端類庫,基于.net 4.6.1創(chuàng)建,基于官方opc ua基金會(huì)跨平臺(tái)庫創(chuàng)建,封裝了節(jié)點(diǎn)讀寫,批量節(jié)點(diǎn)讀寫,引用讀取,特性讀取,歷史數(shù)據(jù)讀取,方法調(diào)用,節(jié)點(diǎn)訂閱,批量訂閱等操作。還提供了一個(gè)節(jié)點(diǎn)瀏覽器工具。
KEPServerEX
第三方的OPC服務(wù)器,各不同廠家多種設(shè)備下位PLC與上位機(jī)之間通訊。KEPServerEX是行業(yè)領(lǐng)先的通信平臺(tái),用于向您的所有應(yīng)用程序提供單一來源的工業(yè)自動(dòng)化數(shù)據(jù)。該平臺(tái)的設(shè)計(jì)使用戶能夠通過一個(gè)直觀的用戶界面來連接、管理、監(jiān)視和控制不同的自動(dòng)化設(shè)備和軟件應(yīng)用程序。KEPServerEX 利用 OPC(自動(dòng)化行業(yè)的互操作性標(biāo)準(zhǔn))和以 IT 為中心的通信協(xié)議(如 SNMP、ODBC 和 Web 服務(wù))為用戶提供單一來源的工業(yè)數(shù)據(jù)。要開始使用,只需下載 KEPServerEX,然后從Kepware 的包含 150 多個(gè)設(shè)備驅(qū)動(dòng)程序、客戶端驅(qū)動(dòng)程序和高級(jí)選項(xiàng)的庫中進(jìn)行選擇,以滿足您獨(dú)特的工業(yè)控制系統(tǒng)需求。
以下基于開源的OpcUaHelper自帶的demo,修改了界面,測(cè)試了部分功能。
主界面
C# 通過OPC UA 訪問KEPServerEx6 演示視頻
部分代碼:
//同步讀取
private void button1_Click( object sender, EventArgs e )
{
if (cbo函數(shù)Nodeid.Text=="")
{
return;
}
DataValue dataValue = m_OpcUaClient.ReadNode( new NodeId(cbo函數(shù)Nodeid.Text ) );
if (dataValue.WrappedValue.Value != null)
{
txtvalue.Text = dataValue.WrappedValue.Value.ToString();
}
//test();
}
//單節(jié)點(diǎn)訂閱
private void button2_Click( object sender, EventArgs e )
{
// sub
m_OpcUaClient.AddSubscription( "A", textBox4.Text, SubCallback );
}
//取消訂閱 關(guān)鍵字A
private void button3_Click( object sender, EventArgs e )
{
// remove sub
m_OpcUaClient.RemoveSubscription( "A" );
}
//訂閱回調(diào)
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")
{
// 如果有多個(gè)的訂閱值都關(guān)聯(lián)了當(dāng)前的方法,可以通過key和monitoredItem來區(qū)分
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
if (notification != null)
{
textBox3.Text = notification.Value.WrappedValue.Value.ToString( );
}
}
else if(key == "B")
{
// 需要區(qū)分出來每個(gè)不同的節(jié)點(diǎn)信息
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( );
}
}
}
// 緩存的批量訂閱的節(jié)點(diǎn)
private string[] MonitorNodeTags = null;
//多節(jié)點(diǎn)訂閱
private void button5_Click( object sender, EventArgs e )
{
// 多個(gè)節(jié)點(diǎn)的訂閱
MonitorNodeTags = new string[]
{
textBox6.Text,
textBox7.Text,
textBox8.Text,
};
m_OpcUaClient.AddSubscription( "B", MonitorNodeTags, SubCallback );
}
//取消訂閱
private void button4_Click( object sender, EventArgs e )
{
// 取消多個(gè)節(jié)點(diǎn)的訂閱
m_OpcUaClient.RemoveSubscription( "B" );//關(guān)鍵字 B
}
//異步讀取節(jié)點(diǎn)
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;
}
// 一個(gè)數(shù)據(jù)的類型是不是數(shù)組由 value.WrappedValue.TypeInfo.ValueRank 來決定的
// -1 說明是一個(gè)數(shù)值
// 1 說明是一維數(shù)組,如果類型BuiltInType是Int32,那么實(shí)際是int[]
// 2 說明是二維數(shù)組,如果類型BuiltInType是Int32,那么實(shí)際是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; // 數(shù)組的情況參照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Float)
{
float temp = (float)value.WrappedValue.Value; // 數(shù)組的情況參照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.String)
{
string temp = (string)value.WrappedValue.Value; // 數(shù)組的情況參照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.DateTime)
{
DateTime temp = (DateTime)value.WrappedValue.Value; // 數(shù)組的情況參照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
short temp = (short)value.WrappedValue.Value; // 數(shù)組的情況參照上面的例子
txtvalue.Text = temp.ToString();
}
else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Double)
{
double temp = (double)value.WrappedValue.Value; // 數(shù)組的情況參照上面的例子
txtvalue.Text = temp.ToString();
????????????}
????????}
//瀏覽節(jié)點(diǎn)引用
????????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 = "語言學(xué)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:風(fēng)俗 風(fēng)俗 ns=2;s=Devices/分廠一/車間二/ModbusTcp客戶端/風(fēng)俗
// Variable 2:轉(zhuǎn)速 轉(zhuǎn)速 ns=2;s=Devices/分廠一/車間二/ModbusTcp客戶端/轉(zhuǎn)速
// Variable 2:機(jī)器人關(guān)節(jié) 機(jī)器人關(guān)節(jié) ns=2;s=Devices/分廠一/車間二/ModbusTcp客戶端/機(jī)器人關(guān)節(jié)
// Variable 2:cvsdf cvsdf ns=2;s=Devices/分廠一/車間二/ModbusTcp客戶端/cvsdf
// Variable 2:條碼 條碼 ns=2;s=Devices/分廠一/車間二/ModbusTcp客戶端/條碼
// Variable 2:開關(guān)量 開關(guān)量 ns=2;s=Devices/分廠一/車間二/ModbusTcp客戶端/開關(guān)量
}
catch (Exception ex)
{
ClientUtils.HandleException(this.Text, ex);
}
}
//啟用設(shè)備屬性-操作模式-模擬 可讀取節(jié)點(diǎn)屬性
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
{
// 此處演示寫入一個(gè)short,2個(gè)float類型的數(shù)據(jù)批量寫入操作。啟用設(shè)備模擬后寫入成功。但是很快就被模擬的0覆蓋
bool success = m_OpcUaClient.WriteNodes(new string[] {
"ns=2;s=Devices.分廠一.車間二.ModbusTcp客戶端.溫度",
"ns=2;s=Devices.分廠一.車間二.ModbusTcp客戶端.風(fēng)俗",
"ns=2;s=Devices.分廠一.車間二.ModbusTcp客戶端.轉(zhuǎn)速"},
new object[] {
(short)1234,
123.456f,
123f
});
//bool success = m_OpcUaClient.WriteNode("ns=2;s=Devices.分廠一.車間二.ModbusTcp客戶端.風(fēng)俗", 123.456f);
if (success)
{
MessageBox.Show("寫入成功!");
}
else
{
// 寫入失敗,一個(gè)失敗即為失敗
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文章來源:http://www.zghlxwxcb.cn/news/detail-410483.html
The End文章來源地址http://www.zghlxwxcb.cn/news/detail-410483.html
到了這里,關(guān)于【OPC UA】C# 通過OpcUaHelper建立OPC客戶端訪問KEPServerEx6 OPC服務(wù)器數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!