如何一步步编写opc client程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何一步步编写opc client程序相关的知识,希望对你有一定的参考价值。

一.准备工作:
下载opc基金会的OPC Proxy DLL然后按照说明进行安装。这些动态库是opc程序运行所必须的。
1. 将下列文件拷贝至要运行OPC服务器和OPC客户端的机器上的SYSTEM32目录下
copy opcproxy.dll C:\WINDOWS\system32
copy opccomn_ps.dll C:\WINDOWS\system32
copy opc_aeps.dll C:\WINDOWS\system32
copy opchda_ps.dll C:\WINDOWS\system32
copy aprxdist.exe C:\WINDOWS\system32
copy opcenum.exe C:\WINDOWS\system32
2. 注册这些 dll 文件
REGSVR32 opcproxy.dll
REGSVR32 opccomn_ps.dll
REGSVR32 opc_aeps.dll
REGSVR32 opchda_ps.dll
3. 如果在windows 系统(\WINDOWS \system32)目下不存在actxprxy.dll,运行aprxdist.exe
4. 安装 opcenum.exe
opcenum /regserver

二.opcserver开发
1. 新建vc工程。比如建一个控制台工程。在工程中包含如下头文件
#include "opcda.h"
#include "opc_ae.h"
#include "WTOPCsvrAPI.h"
#include "WtOPCsvrEXTapi.h"
以上头文件在例子程序中都能找到,拷贝到自己的工程下。
WTOPCsvrAPI.h是开发包动态库提供的导出函数文件。里面有每个函数的具体说明,调用的时候看一下说明。
2. 在.cpp中定义一个GUID这个guid是用来标识opcserver的唯一id可以通过操作系统的工具生成,也可以编一个。格式如下。
const GUID
CLSID_OPCSimSvr = 0x99b8f472, 0xc037, 0x11d2, 0x80, 0xb8, 0x0, 0x60, 0x97, 0x58, 0x58, 0xbe;

3. 在工程中加入对WtOPCSvr动态库的连接。
WTOPCsvr动态库的有关文件都在之前的例子包里面能找到。
WTOPCsvr.lib WTOPCsvr.dll是对应lib和dll

4. 初始化
(1)调用UpdateRegistry()函数完成注册。下面是一个调用参考例子代码

BOOL COPCSimSvrApp::InitInstance()

TCHAR szTokens[] = _T("-/ ");
CString HelpPath;
CString SvrName, SvrDescrip;
int i;

HelpPath = AfxGetApp()->m_pszHelpFilePath;
i = HelpPath.ReverseFind('\\');
HelpPath = HelpPath.Left(i+1);
HelpPath += "OPCSIMSVR.EXE";
//
// Self-Registration code
// (look for cmdline options to register & unregister server)
//

SvrName = "WinTECH.OPCServer";
SvrDescrip = "WinTECH Software OPC Server Simulator";

CString tempCmdLine(m_lpCmdLine);
LPTSTR lpszToken = _tcstok(tempCmdLine.GetBuffer(1), szTokens);
while (lpszToken != NULL)

if (_tcsicmp(lpszToken, _T("UnregServer"))==0)

UnregisterServer ((BYTE *)&CLSID_OPCSimSvr, SvrName);
return (FALSE);

else if (_tcsicmp(lpszToken, _T("RegServer"))==0)

UpdateRegistry ((BYTE *)&CLSID_OPCSimSvr,
SvrName,
SvrDescrip,
HelpPath);
return (FALSE);

lpszToken = _tcstok(NULL, szTokens);


(2)调用InitWTOPCsvr完成开发包dll初始化。
以上两个步骤不可颠倒。
5. 创建item
这里的item是服务器所有的item,这些item通过opc库户端能浏览到。创建item的函数为CreateTag().
Opc有两种地址空间形式:扁平和多层结构的。
比如调用创建的点为item1、item2、item3.这类结构就是扁平的。各个点类似与文件系统中的文件。
比如调用创建的点为test.item1、test.item2、test.item3这种点名,开发包会自动形成多层的点结构。Test类似与文件系统中的文件夹,item类似与文件系统中的文件夹下的文件。扁平式和多层结构在客户端浏览点名时体现。
多层结构的点名之间默认是用“.”分隔。为了简单可以将我们的opcserver设计为扁平结构。
创建一个点时会返回一个handle用来标识这个点。我们程序需要自己建立这个handle和数据库中保存设备实时值的对应关系。这部分可以参考例子代码。
6. 建立一个定时器采集设备数据
定时从数据库中取得各个handle对应的item的值。然后比较各个item的值是否和上一次读取的值有变化,如果有变化调用UpdateTagToList放入对应的队列。开发包会自动将变化的值送到客户端。

大致流程为
(1) 从数据库中采集一遍所有点的实时值。
(2) 调用StartUpdateTags()
(3) 循环读取每个item在数据库中的数据,和上一次读取到的进行比较。如果有变化调用UpdateTagToList()
(4) 最用调用EndUpdateTags()完成所有item的更新。
7. 客户端控制
客户端写tag的值的时候,在opcserver是通过一个回调函数来响应的。
在服务器端必须调用EnableWriteNotification()来指定写值的回调函数。回调函数的格式为:
typedef VOID (CALLBACK* WRITENOTIFYPROC)(HANDLE, VARIANT*, DWORD*);
然后在回调函数内部实现从HANDLE指定的tag写到具体的控制设备对应的变量中。
8. 其它常用函数
RequestDisconnect()一般在opcserver在关闭时调用,用来通知客户端opcserver自己要关闭。
NumbrClientConnections()用来计算当前有多少个客户端连接到了opcserver
UninitWTOPCsvr()程序退出时清理
SetVendorInfo()设置厂商信息
三.opc客户端测试
下面以本机opcquickclient.exe为例说明服务器和客户端之间的应用关系。
1. 运行opcquickclient.exe
2. 点击edit->new server connection 菜单。弹出server properties对话框。从中展开opc data access server version 2.0.会浏览到本机安装的所有opcserver。选中我们自己开发的opcserver,点击确定。完成与服务器的连接
3. 点击edit->new group 用默认值添加组。
4. 点击edit->new item 弹出如下对话框。(下图是一个多层结构地址空间的例子,单层的更简单)选中左侧的某个父节点,右侧会显示出各个子tag。选中要向opcserver查询的tag,点击add leaves。然后点击ok
5. 在客户端主界面上能看到刚才要查询的tag。它会自动跟随opcserver端数据的变化而变换。
6. 右键要控制的tag,在菜单中选择同步写或者异步写(一般建议用异步,避免阻塞客户端的正常运行)。键入要写入的值。查看设备上是否动作。从而检验opcserver是否编写的正确。
参考技术A 用Custom接口,出此之外还有Automation接口。以下是Custome接口开发时涉及到的三个关键对象:OpcServer、OpcGroup、OpcItem

C# opc-client .net api

【中文标题】C# opc-client .net api【英文标题】: 【发布时间】:2011-04-13 05:49:05 【问题描述】:

我正在编写 opc 客户端,使用来自 opc 基金会的 .NET API。

在我只看到的示例中,项目的名称是硬编码的,例如:

items[0] = new Opc.Da.Item();
items[0].ItemName = "blahblahblah";

我想要的不是亲手写下所有物品的名称。 例如,我想将服务器中的所有项目加载到树中。 我该怎么做?

【问题讨论】:

在我看来您正在查看错误的样本。那将是服务器代码,在 OPC 中更为常见。 【参考方案1】:

好吧,我不熟悉您的 opc 客户端库,但您应该能够浏览服务器项目。这是许多独立 OPC 客户端使用的通用功能。

【讨论】:

【参考方案2】:

您可以使用以下结构浏览服务器:

using Opc.Da;
using Server=Opc.Da.Server;
using Factory=OpcCom.Factory;

string urlstring = string.Format("opcda://0/1/2", _hostName, _serverName, serverid);
Server s = new Server(new Factory(), new URL(urlstring));
s.Connect();
ItemIdentifier itemId = null;
BrowsePosition position;
BrowseFilters filters = new BrowseFilters() BrowseFilter = browseFilter.item;
BrowseElement[] elements = s.Browse(itemId, filters, out position);

标签在元素[i].Name中。

【讨论】:

是的。我使用了相同的代码。但。我在服务器上有文件夹......你的代码没有显示它们 更改 BrowseFilter = browseFilter.item;到 BrowseFilter = browseFilter.all;【参考方案3】:

您从 XML 文件加载项目。您可以使用任意数量的项目创建一次 XML 文件,然后仅在需要时对其进行编辑。例如,要解析它,您可以使用 MSXML DOM。 VB.NET 中有一个解析器Parsing XML file in VB.NET using DOM。但是您可以在 C# 中搜索实现。

【讨论】:

【参考方案4】:
Public Function Browse(ByRef node As TreeNode, Optional id As Opc.ItemIdentifier = Nothing) As Integer
    Try
        Dim clone As Opc.Da.Server = your_connected_server
        Dim filters As New Opc.Da.BrowseFilters
        filters.BrowseFilter = Opc.Da.browseFilter.all
        Dim pos As Opc.Da.BrowsePosition = Nothing
        Dim elements() As Opc.Da.BrowseElement = clone.Browse(id, filters, pos)
        If (elements IsNot Nothing) Then
            For Each element As Opc.Da.BrowseElement In elements
                Console.WriteLine(element.ItemName)
                AddBrowseElement(node, element)
                If (element.HasChildren = True) Then
                    id = New Opc.ItemIdentifier(element.ItemPath, element.ItemName)
                    Browse(node.Nodes.Item(node.Nodes.Count - 1), id)
                End If
            Next
        End If
        Return 0
    Catch ex As Exception
        RaiseEvent OnException(GetCurrentMethod, ex)
        Return -1
    End Try
End Function

Private Sub AddBrowseElement(ByRef parent As TreeNode, element As Opc.Da.BrowseElement)
    Dim node As TreeNode = New TreeNode(element.Name)
    node.Text = element.Name
    node.Tag = element

    ' add properties
    If (element.Properties IsNot Nothing) Then
        For Each [property] As Opc.Da.ItemProperty In element.Properties
            AddItemProperty(node, [property])
        Next
    End If
    ' add to parent.
    parent.Nodes.Add(node)
End Sub

【讨论】:

您使用 TreeNode 对象调用 Browse 函数: Dim node As New TreeNode("Root") Browse(node) 然后 Browse 函数自递归调用它。完成后,您将拥有节点控件中的所有服务器标签。【参考方案5】:

使用 XML 或 NLOG 或 log4NET 文件加载标签项。

使用 opcdaauto.dll。它是供 OPC 基金会成员使用的免费 dll

【讨论】:

以上是关于如何一步步编写opc client程序的主要内容,如果未能解决你的问题,请参考以下文章

如何通过OPC自定义接口来实现客户端数据的读取?

如何通过OPC自定义接口来实现客户端数据的读取

wincc如何通过opc与外部设备连接

如何使用 open62541 一次使用 OPC-UA 编写多个节点?

用 Lazarus 开发 OPC Client 1 (关于字符集)

如何获取 .Net C# 上的 OPC UA 服务器列表?