基于opc ua客戶端上位機(jī)編寫
使用自動化接口進(jìn)行opcda的客戶端編寫,但是opcda是基于com/dcom的技術(shù),如果服務(wù)器與客戶端不在同一臺電腦上配置較為復(fù)雜;基于com/dcom的技術(shù)有著不可根除的缺點(diǎn),因此隨著技術(shù)的進(jìn)步,以及數(shù)據(jù)交換各方面需求的提高,opc基金會在2008年發(fā)布了新的規(guī)范:opc ua。opc ua規(guī)范不再是基于com/dcom技術(shù),因此opc ua不僅能在windows平臺上實(shí)現(xiàn),更可以在linux,以及其他的嵌入式平臺中實(shí)現(xiàn)。
與傳統(tǒng)opc規(guī)范相同,opc ua同樣有著相同的設(shè)計(jì)目標(biāo):
1.功能等價(jià):所有的基于com的opc規(guī)范中的功能,都映射到了opc ua中。
2. 多平臺支持:支持從嵌入式的微控制器到基于云的分散式控制架構(gòu)。
3.安全:信息加密,互訪認(rèn)證以及安全監(jiān)聽功能。
4.擴(kuò)展性:不影響現(xiàn)有應(yīng)用程序的情況下,就可以添加新的功能。
5.豐富的信息建模:可定義復(fù)雜的信息,而不再是單一的數(shù)據(jù)。
opcua服務(wù)器配置
本次opcua服務(wù)器依然使用kepserverex 6,配置如下:
1、在托盤找到kepserverex 6圖標(biāo),右鍵彈出菜單,點(diǎn)擊opcua配置
2、在彈出的配置界面進(jìn)行如下圖的配置:
3、打開kepserverex 6主界面,右鍵點(diǎn)擊項(xiàng)目,選擇屬性,進(jìn)行配置如下,即完成opcua服務(wù)器配置;
03
軟件編寫
1、軟件實(shí)現(xiàn)節(jié)點(diǎn)瀏覽,添加訂閱,移除訂閱,清空訂閱的功能,演示界面如下:
2、本次代碼實(shí)現(xiàn)主要基于opc ua的最新官方庫二次封裝的一個(gè)類庫:opcuahelper.dll,開源地址:https://github.com/dathlin/opcuahelper;準(zhǔn)備好開發(fā)的ide,新建項(xiàng)目。注意:項(xiàng)目的.net framework版本最低為4.6。打開nuget管理器,引用opcuahelper;
3、代碼編寫:
<1> 建立連接
private void button1_click(object sender, eventargs e)
{
opcuaclient.useridentity = new useridentity(new anonymousidentitytoken());
opcuaclient.connectserver(textbox1.text);
populatebranch(objectids.objectsfolder, treeview1.nodes);
}
<2>節(jié)點(diǎn)瀏覽
private async void populatebranch(nodeid sourceid, treenodecollection nodes)
{
odes.clear();
nodes.add(new treenode(browsering...));
// fetch references from the server.
treenode[] listnode = await task.run(() =>
{
referencedescriptioncollection references = getreferencedescriptioncollection(sourceid);
list<treenode> list = new list<treenode>();
if (references != null)
{
// process results.
for (int ii = 0; ii < references.count; ii++)
{
referencedescription target = references[ii];
treenode child = new treenode(utils.format({0}, target));
child.tag = target;
child.nodes.add(new treenode());
list.add(child);
}
}
return list.toarray();
});
//更新顯示的屬性
// displayattributes(sourceid);
nodes.clear();
nodes.addrange(listnode.toarray());
}
private referencedescriptioncollection getreferencedescriptioncollection(nodeid sourceid)
{
//taskcompletionsource<referencedescriptioncollection> task = new taskcompletionsource<referencedescriptioncollection>();
// 找到所有組件的節(jié)點(diǎn)
browsedescription nodetobrowse1 = new browsedescription();
nodetobrowse1.nodeid = sourceid;
nodetobrowse1.browsedirection = browsedirection.forward;
nodetobrowse1.referencetypeid = referencetypeids.aggregates;
nodetobrowse1.includesubtypes = true;
nodetobrowse1.nodeclassmask = (uint)(nodeclass.object | nodeclass.variable | nodeclass.method | nodeclass.referencetype | nodeclass.objecttype | nodeclass.view | nodeclass.variabletype | nodeclass.datatype);
nodetobrowse1.resultmask = (uint)browseresultmask.all;
// 找到所有節(jié)點(diǎn)組織的節(jié)點(diǎn).
browsedescription nodetobrowse2 = new browsedescription();
nodetobrowse2.nodeid = sourceid;
nodetobrowse2.browsedirection = browsedirection.forward;
nodetobrowse2.referencetypeid = referencetypeids.organizes;
nodetobrowse2.includesubtypes = true;
nodetobrowse2.nodeclassmask = (uint)(nodeclass.object | nodeclass.variable | nodeclass.method | nodeclass.view | nodeclass.referencetype | nodeclass.objecttype | nodeclass.variabletype | nodeclass.datatype);
nodetobrowse2.resultmask = (uint)browseresultmask.all;
browsedescriptioncollection nodestobrowse = new browsedescriptioncollection();
nodestobrowse.add(nodetobrowse1);
nodestobrowse.add(nodetobrowse2);
// 從服務(wù)器獲取引用
referencedescriptioncollection references = formutils.browse(opcuaclient.session, nodestobrowse, false);
return references;
}
private void treeview1_beforeexpand(object sender, treeviewcanceleventargs e)
{
try
{
// check if node has already been expanded once.
if (e.node.nodes.count != 1)
{
return;
}
if (e.node.nodes.count > 0)
{
if (e.node.nodes[0].text != string.empty)
{
return;
}
}
// get the source for the node.得到的源節(jié)點(diǎn)
referencedescription reference = e.node.tag as referencedescription;
if (reference == null || reference.nodeid.isabsolute)
{
e.cancel = true;
return;
}
// populate children.
populatebranch((nodeid)reference.nodeid, e.node.nodes);
}
catch (exception exception)
{
clientutils.handleexception(this.text, exception);
}
}