基于opc ua客户端上位机编写
使用自动化接口进行opcda的客户端编写,但是opcda是基于com/dcom的技术,如果服务器与客户端不在同一台电脑上配置较为复杂;基于com/dcom的技术有着不可根除的缺点,因此随着技术的进步,以及数据交换各方面需求的提高,opc基金会在2008年发布了新的规范:opc ua。opc ua规范不再是基于com/dcom技术,因此opc ua不仅能在windows平台上实现,更可以在linux,以及其他的嵌入式平台中实现。
与传统opc规范相同,opc ua同样有着相同的设计目标:
1.功能等价:所有的基于com的opc规范中的功能,都映射到了opc ua中。
2. 多平台支持:支持从嵌入式的微控制器到基于云的分散式控制架构。
3.安全:信息加密,互访认证以及安全监听功能。
4.扩展性:不影响现有应用程序的情况下,就可以添加新的功能。
5.丰富的信息建模:可定义复杂的信息,而不再是单一的数据。
opcua服务器配置
本次opcua服务器依然使用kepserverex 6,配置如下:
1、在托盘找到kepserverex 6图标,右键弹出菜单,点击opcua配置
2、在弹出的配置界面进行如下图的配置:
3、打开kepserverex 6主界面,右键点击项目,选择属性,进行配置如下,即完成opcua服务器配置;
03
软件编写
1、软件实现节点浏览,添加订阅,移除订阅,清空订阅的功能,演示界面如下:
2、本次代码实现主要基于opc ua的最新官方库二次封装的一个类库:opcuahelper.dll,开源地址:https://github.com/dathlin/opcuahelper;准备好开发的ide,新建项目。注意:项目的.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>节点浏览
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>();
// 找到所有组件的节点
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;
// 找到所有节点组织的节点.
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);
// 从服务器获取引用
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.得到的源节点
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);
}
}