经过多个月的开发、现场测试和反馈修改,自己开发的OPC客户端终于真正能在现场使用了。由于工业现场的服务器很古老,只支持OPCDA架构,我们也只能按照DA的标准设计客户端。由于有着WTopcclient这个相对好用的客户端开发工具,在开发方面花费的精力还好,可是却苦了现场的同学,在DCOM配置上花费了不少功夫╮(╯-╰)╭,在这里向他们表示最高的敬意。
言归正传,WTopcclient在使用中,我几乎找遍了整个百度,真正有用的除了人家在.h里函数声明处加的注释,就只剩下一个英文说明书了,除此之外几乎没有任何有价值的资料,为此开发OPC客户端中也遇到不少坑,下面把自己的一些经验总结一下,方便以后的开发(当然,如果以后OPCUA普及的话,就再也不用这么费劲的整一个上个世纪的玩意了)。下面对OPCclient的头文件和官方英文说明书中的内容进行润色翻译,并结合自己的备注额外补充一些说明,防止以后使用再踩这些坑。
创作不易,转载请您注明出处,感谢您的支持。
备注:全文中如有“应用程序”字样,全部指的是“由开发者依据这个DLL开发的OPC客户端应用程序”
译文:
WTclient DLL 使用手册
WinTECH软件快速客户端开发DLL(WTclient)提供了一套很简单就可以使用的API,用于将用户开发的OPC客户端应用程序与来自任何OPC服务器的数据建立连接。COM和OPC的所有细节由DLL自己处理,所以开发应用程序时很简单地就可以从服务器获取数据点,而不必关心底层接口的实际实现。在OPC通讯中所有必需的OPC接口,本DLL都支持OPC1.0和OPC 2.0数据访问标准以及浏览接口,并支持连接到简单的报警和事件服务器。 该使用手册应配合OPC数据访问和OPC警报和事件规范文件(规范文件是OPC基金会官方定义的有关OPC的标准,可从基金会官网http://www.opcfoundation.org中获得)。
制作自己的OPC客户端程序
使用 WTclient.DLL 关联WTclient.lib 到项目中
WTclient.lib包含了DLL中 API函数的导出定义。将此文件包含在自己开发的OPC客户端应用程序的项目文件中,并将WTclientAPI.h包含在将要调用DLL的模块中。
备注:在这个头文件中有lib中函数的声明,可以看到函数调用和返回的参数,以及对函数的注释
译文:
初始化DCOM
从WTClient.DLL版本2.0开始,现在由客户端应用程序负责正确初始化DCOM。在版本2.0之前,WTClient.dll在DLLMain函数中执行DCOM和安全初始化,并在卸载dll时调用CoUninitialize()。由于各种Windows dll的加载顺序不同,这样会出现一些问题,所以现在这些初始化调用被移到一个新的导出函数WTclientCoInit( )中,客户端应用程序可以在主线程启动期间调用该函数。WTclientCoInit()将DCOM初始化为多线程,并使用默认安全性。您所开发的OPC客户端应用程序也可以选择自己进行DCOM初始化,而不使用函数WTclientCoInit( )。无论是否调用API函数进行DCOM初始化,应用程序在终止时都必须调用CoUninitialize()。
备注:可以看到,如果想简单通过MFC或Qt开发一个OPC客户端,在程序开始时必须WTclientCoInit()初始化才可以正常使用DCOM,程序结束时必须调用CoUninitialize()。需要注意一下,在WTclientAPI.h中还声明了一个WTclientCoUninit()函数,且注释里说这个函数用来取消DCOM初始化,但说明书里并没有提到这个函数- -。CoUninitialize()这个是windows提供的头文件中取消初始化的函数,与COM和OLE有关系,OPC是过程控制中使用的OLE嘛,所以OPC基础还是COM和OLE没毛病。
译文:
获取 OPC 服务器列表
WTclient.dll 提供了两个函数来使客户端应用程序可以获取可用的OPC Servers列表.
int NumberOfOPCServers(BOOL UseOPCENUM, LPCSTR MachineName);
OPCENUM是OPC基金会提供的一个组件,它允许客户端应用程序获取本地或远程计算机上可用服务器的列表(注:OPCENUM顾名思义,会枚举指定计算机拥有OPC服务器的数量)。很遗憾在编写组件时,OPCENUM的操作没有很好的跨平台一致性。所以WTclient.dll允许应用程序使用OPCENUM组件获取服务器列表,或通过扫描Windows注册表的本地副本绕过OPCENUM并获取服务器列表。如果可以使用OPCENUM的话,你可以传递一个远程计算机的名称(其实指的是它的IP地址)来获取远程OPC服务器列表。
头文件注释翻译:
NumberOfOPCServers(…)
返回可用OPC服务器的数量,如果UseOPCENUM为FALSE,服务器列表从本地Windows注册表获取,忽略MachineName;如果UseOPCENUM为真,服务器列表是从OPCENUM组件获取的,由OPC基金会和MachineName提供,从远程计算机获取服务器列表。
备注: 实际上之所以OPC服务器端初始化有个步骤叫“注册服务器”,就是因为它是在计算机注册表里注册了OPC服务器的信息
功能:获取服务器数量
参数:BOOL UseOPCENUM 是否使用OPCENUM来枚举服务器
LPCSTR MachineName 计算机名,本地服务器为空,远程服务器为IP地址
返回值:int 返回查询到的服务器数量
连接远程服务器,一定要注意DCOM的配置,这是非常麻烦的一件事,本地服务器相对配置就非常简单(几乎不需要配置)。
译文:
从WTclient.dll返回服务器数后,应用程序可以通过调用下面函数获取服务器名:
BOOL GetServerName(int index, LPSTR Buffer, int BufSize);
由客户端程序开发者提供一个提前定义好的字符串Buffer,调用该函数获取的服务器名字会放在该字符串中,也可以直接使用该函数读取指定第几个服务器名字。BufSize标识用户提供的字符缓冲区的长度(注:也就是说限制了获取的服务器名字最多BufSize这么长),可以防止dll载入过长的名字。如果Buffer中返回了一个有效的服务器名称,GetServerName函数返回TRUE,否则返回FALSE。
头文件注释翻译:
用于遍历NumberOfServers()获得的服务器列表
在服务器列表索引处填充服务器名称,放到Buffer指向的用户缓冲区中。
返回的值为FALSE表示索引无效。
备注:这个函数用来获取服务器名字。一个由用户提供的字符缓冲区Buffer负责存放传递过来的index标识的服务器列表中某个服务器名字。也就是说,比如调用NumberOfOPCServers()得到本地有两个服务器,建立一个for循环就可以逐个读取服务器名字,索引从0开始,每次执行该函数得到的服务器名字会放在Buffer中,可以使用别的变量保存每个服务器名字。
译文:
建立与 OPC 服务器的连接
HANDLE ConnectOPC(LPCSTR MachineName, LPCSTR ServerName, BOOL EnableDLLBuffering);
HANDLE ConnectOPC1(LPCSTR MachineName, LPCSTR ServerName, BOOL EnableDLLBuffering);
HANDLE ConnectOPC_AE(LPCSTR MachineName, LPCSTR ServerName);
如果可以通过MachineName(OPC服务器所在计算机名,本地则为空字符串,远程则为IP地址)和ServerName(OPC服务器名字,在之前的步骤中得到)建立到指定OPC服务器的连接,那么这个函数返回一个有效的句柄HANDLE 。(这个句柄是服务器的句柄,对指定服务器的内容进行操作,都需要提供这个句柄,一定要好好保存!)客户端程序可以同时连接多个服务器,每个服务器连接成功后都会返回这个服务器的句柄。WTclient将尝试使用OPC 2.0连接点接口进行连接。如果不可用,它将恢复OPC 1.0 DataObject接口。
EnableDLLBuffering如果为TRUE,DLL会自己维护客户端应用程序创建的所有项的列表并接收服务器更新,当服务器数据改变,dll维护的这个列表会更新,应用程序要用ReadOPCItem()来读取项的数据(本身是为了读取OPC服务器项对应标签的数据,实际是读取了DLL中的列表中的对应数据,对于我们使用者来说这些细节无关紧要)。
如果EnableDLLBuffering为FALSE,DLL不会保留项数据的本地副本和对的后续调用(DLL不会自己维护一个项列表),ReadOPCItem()将失败。用户应用程序必须定义正确的回调函数,(EnableOPCNotification()),获取项更新,对于那些使用不支持回调函数的工具设计的应用程序(如Visual Basic 5.0),可以将WTclient配置为维护由应用程序创建的所有OPC项(标记)的列表(即上面的参数设为TRUE)。当来自服务器的数据更改时,dll列表中相应的标记值将被更新。控制应用程序可以随时读取标记的值。
ConnectOPC1 会一直尝试一个 OPC 1.0 标准对OPC服务器的连接。
ConnectOPC_AE 将会建立一个到 OPC 报警 & 事件服务器的连接。
头文件注释翻译:
ConnectOPC (…)
与指定服务器建立OPC连接,如果无法建立连接,则返回INVALID_HANDLE_VALUE (-1),如果EnableDLLBuffering为真,DLL将维护应用程序创建的所有项的列表,并由服务器更新。应用程序可以根据需要使用ReadOPCItem()获取新的项数据。如果EnableDLLBuffering为FALSE,DLL不会保留项数据和后续调用的本地副本,ReadOPCItem()将失败。用户应用程序必须定义正确的回调函数(EnableOPCNotification())获取项更新。
ConnectOPC1(…)
如果OPC服务器支持IConnectionPtContainer接口,那么ConnectOPC函数总会尝试一个OPC2.0版的连接,而这个函数可以强制客户端使用IAdvise接口来使得客户端与服务器建立OPC1.0a标准连接。
ConnectOPC_AE (…)
与指定的A&E服务器建立OPC连接,如果无法连接返回INVALID_HANDLE_VALUE (-1)。
备注: 一般来说都是使用最普通的ConnectOPC(…)函数进行OPC连接的。第一个参数在远程连接服务器时为服务器所在IP地址,本地时传入空字符即可,第二个参数就是连接到的OPC服务器名字,最后一个参数一般都是用TRUE,这样就不用再费力气去研究什么回调函数了,直接使用开发工具提供的读写函数就可以操作OPC项了。
译文:
void DisconnectOPC(HANDLE hConnect);
void DisconnectOPC_AE(HANDLE hConnect);
当应用程序终止时,它负责与连接的服务器断开连接。DisconnectOPC(), (或者 Disconnect_OPCAE()), 函数会将hConnect(这个句柄就是在连接到指定服务器的时候返回的句柄,断开时也要用到,后面很多操作都需要,十分重要)定义的连接的完全关闭。
头文件注释翻译:
DisconnectOPC(…)
用于关闭OPC连接。
备注:用来在结束对服务器读写后关闭到服务器连接的,其他没什么好说的,一定要注意这个参数填的是连接到服务器时获取的句柄。再次强调一遍,在OPC连接中所有的句柄都非!常!重!要!千万要好好保存。
译文:
获取 OPC Server 标签名列表
int NumberOfOPCItems(HANDLE hConnect);
BOOL GetOPCItemName(HANDLE hConnect, int index, char *pBuf, int BufSize);
NumberOfOPCItems() 返回由句柄hConnect指向的 OPC Server所拥有的标签总数. 建立一个for循环, GetOPCItemName将会遍历所有可用的标签名,来让用户选择要建立连接项的标签,或者生成一个标签名列表. NumberOfOPCItems 将从服务器浏览项目名称的完整列表,并以“FLAT”格式显示该列表。
头文件注释翻译:
NumberOfOPCItems(…)
从指定服务器连接的浏览接口返回OPC项目数,如果服务器不支持浏览,则返回值为xero。此函数用于填充itemnames的内部数组,这个数组通过GetOPCItemName()访问。此函数相当于从根位置使用OPC_FLAT调用BrowseItems,NumberOfOPCItems()使用BROWSE_DOWN和BROWSE_UP遍历树。
GetOPCItemName(…)
允许用户从NumberOfOPCItems()迭代来获取每个项的名字。
备注:这两个函数使用就像之前获取服务器名时一样,先获取服务器拥有的标签item数目,通过NumberOfOPCItems返回值,然后逐个读取标签的名字。
BOOL GetNameSpace(HANDLE hConnect, WORD *pNameSpace);
BOOL BrowseTo(HANDLE hConnect, LPCSTR NodeName);
char SetWTclientQualifier(char qualifier);
int BrowseItems(HANDLE hConnect, WORD Filter);
为了更好地访问具有分层名称空间的服务器,WTClient dll提供了一些函数,允许应用程序直接从服务器浏览项名称。 SetWTclientQualifier 允许应用程序更改用于分析分层命名空间节点的定界字符。客户端dll所需的默认分隔符是“.”,但是,某些服务器使用“.”字符作为节点名的一部分。GetNameSpace允许应用程序确定服务器的命名空间。BrowseTo将当前浏览位置移动到指定的名称,BrowseItems使用服务器中的节点名填充内部名称列表。BrowseTo可用于读取OPC_BRANCH下面的OPC_LEAF(叶子的名字),首先要用GetOPCItemName获取OPC_BRANCH树枝名,然后使用BrowseTo移动浏览位置到该树枝名位置,然后继续使用GetOPCItemName就可以读取当前树枝下的叶子名。WtClient.DLL为NumberOfOPCItems()函数和BrowseItems()函数维护一个单一的名称列表,因此应用程序在浏览服务器命名空间的时候必须很小心,避免覆盖前一次调用的值。在各个等级的树的相互作用过程中,调用BrowseItems之后,所有的项目名称必须立刻拷贝到本地存储中。
使用OPC_FLAT从根位置调用BrowseItems的行为与NumberOfOPCItems()相同。
头文件注释翻译:
GetNameSpace(…)
从指定的服务器连接中返回OPC_NS_FLAT (2)或者OPC_NS_HIERARCHIAL (1)。
BrowseTo(…)
将服务器的当前浏览位置更改为指定的节点,使用NULL或“Root”浏览到树的顶部,NodeName应该是从GetItemName返回的完整的指定节点名。
SetWTclientQualifier(…)
允许应用程序提供用于在分层命名空间中分隔标记名,分隔字符默认为"."。
BrowseItemNames(…)
返回当前浏览位置的项数并通过GetItemName()填充具有可访问节点名的内部项名称数组。
Filter参数指定:
OPC_BRANCH:返回所有有孩子的项
OPC_LEAF:返回所有没有孩子的项
OPC_FLAT:返回所有在当前位置下的OPC项名字,(LEAFS ONLY),(包括所有孩子的孩子)。
备注:一个一个函数说明,首先是GetNameSpace(…),它可以查看服务器是平面结构还是分层结构的,如果是平面,那么服务器下面直接就能列出所有的项,相当于你打开C盘,所有的txt文件都存在了C盘下面,如果是分层那么后面获取项就稍微麻烦了,相当于你在C盘下建了很多文件夹,文件夹里可能又建了文件夹。。。服务器可能套了很多层目录,然后你一层层展开才能找到真正的项。
BrowseTo(…)这个函数是切换目录的函数,当然是很重要了!相当于linux系统下面的一次cd操作,每次遍历完一个目录都要用这个函数切换目录,需要注意,如果是多层目录,切换的时候一定要输入目录的全名!相当于绝对路径(这一点上我就遇到了大坑,在OPC服务器目录到两层以上的时候,怎么切换目录都切不过去,后来发现,目录必须是全名!)
SetWTclientQualifier(…)这个函数用来更改解析服务器目录的标识符,举例说明,服务器里有个标签叫“1.2.3”,那么默认用“.”解析,会认为这个标签首先在树枝1下面,然后树枝1下面有个小树枝2,小树枝2下面有个树叶3,(使用BrowseTo(…)切换到小树枝2的时候,一定参数要填“1.2”而不是“2”!),可能有的服务器也有一个这样结构的标签,但比如它用“_”来分界,那么标签全名就成了“1_2_3”,这时候就需要用到这个函数更改解析服务器层级结构的标识符了(当然,目前见到过的几个大公司,OPC服务器都是用这个小数点,暂时没遇到那种奇葩的标识符不用小数点的- -)。
BrowseItem(…)这个函数用来在BrowseTo(…)切换位置后,规定程序怎么看待当前目录的,规定了是把这个目录下面的东西当树枝(目录)看还是当叶子(真正的标签)看,同时它的返回值返回了按照这种方式看这个目录,下面有多少个指定的玩意。比如说,现在的目录下面都是真正的标签了,你还用树枝的方式看这个目录,那么返回的就只有0个了。所以说,其实我们开发OPC客户端时还是用了一定先验知识的,知道现在这层目录下面是树枝还是叶子,如果提前不知道,那么只能一层一层的试才能解析出来目录结构是几层- -。
译文:
BOOL SetBrowseFilters(HANDLE hConnect, LPCSTR UserString, VARTYPE DataType, DWORD AccessType);
应用程序可以设置浏览过滤器,以影响从服务器返回的OPC项目数。
应用程序可以选择仅检索具有特定数据类型或访问权限的那些项。
UserString:只能浏览到包含指定字符的标签
DataType:只能浏览到指定数据类型的标签
AccessType:只能浏览到包含指定读写标志的标签(可读 可写 既可读又可写)
头文件注释翻译:
SetBrowseFilters(…)
允许应用程序指定在浏览操作期间使用的筛选器。可以根据用户定义的字符串、数据类型或读/写访问权限。
备注:囧,这个开发工具真的是越用越方便,刚开始用的时候各种不顺手,筛选点名都是用CString的Find来做的- -,我也是现在翻译文档才发现还有个专门的筛选函数QAQ,居然还可以筛权限和数据类型。
译文:
创建 OPC 组 and 项
HANDLE AddOPCGroup(HANDLE hConnect, LPCSTR Name, DWORD *pRate, float *pDeadBand);
void RemoveOPCGroup(HANDLE hConnect, HANDLE hGroup);
WTclient.dll有两个允许您创建和删除OPC组的函数。函数的作用是:创建一个组后返回一个组句柄(对组内内容的操作都要通过组句柄),必要时必须提供给其他Wtclient函数。作为参数传递给AddOPCGroup的值由控制应用程序确定,并且特定于每个组。LPCSTR Name名称是任意的,不能使用服务器名。DWORD *pRate是指向所需刷新率(以毫秒为单位)的指针,服务器使用该刷新率向客户端提供更新,就是说每隔刷新率的时间,服务器向这个组内内容更新数据。
头文件注释翻译:
AddOPCGroup(…)
为定义的连接创建新的OPC组,需要参数列表中指定的请求名称、数据速率和死区,速率和死区的实际值从服务器返回到相应的指针位置,组在创建时候总是激活(Active)状态的。
RemoveOPCGroup(…)
删除并清除为定义的组分配的资源。
备注:数据速率指的是在数据订阅模式下客户端这个组里所有的项数据多久刷新一次,死区是当数据变化超过百分之多少的波动范围时服务器会向客户端这个组发出通知。组的句柄是在添加组的时候返回的,也要注意保存好。在移除组的时候,如果自己定义了相关的类或者结构体,记得把相关的变量自己清除。
译文:
BOOL ChangeOPCGroupState(HANDLE hConnect, HANDLE hGroup, BOOL Active));
使用此功能,可以激活(Active = TRUE)或停用(Active = FALSE)定义的OPC组。
头文件注释翻译:
允许应用激活和停用一个OPC组。
备注:这个功能没用过,也不理解为什么要不激活- -。
译文:
HANDLE AddOPCItem(HANDLE hConnect, HANDLE hGroup, LPCSTR ItemName);
HANDLE AddOPCItemWithPath(HANDLE hConnect, HANDLE hGroup, LPCSTR PathName, LPCSTR ItemName);
void RemoveOPCItem(HANDLE hConnect, HANDLE hGroup, HANDLE hItem);
要将服务器标签添加到定义的OPC组,只需使用组的句柄和所需添加项的服务器标签名和/或路径名调用AddOPCItem函数。这个函数将返回唯一的项句柄(如果请求的项不存在,比如服务器没有这个名字的标签,则返回无效的句柄值)。由于项都属于某个组,关闭组时,需要调用RemoveOPCItem清除对每个项所分配的内存。
头文件注释翻译:
AddOPCItem(…)
请求连接的OPC服务器向指定组添加项目,返回值标识了访问的项,用来在未来用户应用程序通过这个返回值操作项,服务器没有叫这个名字的项那么返回值为INVALID_HANDLE_VALUE。
AddOPCItemWithPath(…)
与AddOPCItem相同,但允许用户指定路径。
RemoveOPCItem(…)
移除指定的OPC项并且清空资源。
备注:同样记得保存添加项时返回的项句柄。
译文:
BOOL ChangeOPCItemState(HANDLE hConnect, HANDLE hGroup, HANDLE hItem,
BOOL Active);
使用此功能,可以激活(Active = TRUE)或停用(Active = FALSE)定义的OPC项。
头文件注释翻译:
允许应用激活和停用一个OPC项。
备注:同样没用过,可能有时候对某个项的数据不感兴趣需要停用吧,不是很理解其用途。
译文:
BOOL GetOPCItemNameFromHandle(HANDLE hConnect, HANDLE hGroup, HANDLE hItem,char *pBuf, int BufSize);
此函数允许客户端应用程序通过传递从AddOPCItem返回的句柄来检索OPC项名称,获取到的标签名会保存在char *pBuf,int BufSize是客户端提供的标签名最大长度。
头文件注释翻译:
允许用户迭代从NumberOfOPCItems()获取的项名称列表。
备注:通过得到的项句柄来反向获取服务器标签的名字,有时候把每个项的句柄存了忘了存名字,用这个反向获取名字,项的名字对于OPC客户端来说在建立连接后其实就无所谓了,从之前的函数也可以看出来,项句柄才是最关键的,名字主要是各种界面显示,需要给人看的。
译文:
BOOL GetOPCItemType(HANDLE hConnect, HANDLE hGroup, LPCSTR ItemName,VARTYPE *pType, DWORD *pAccessRights);
这个函数返回了指定标签的VARTYPE *pType变量类型以及DWORD *pAccessRights读写权限。
头文件注释翻译:
允许应用程序获取规范数据类型,以及给定项的读/写访问属性。返回值FALSE,表示请求的项名称没有存在于连接的服务器中。
备注:这个函数特殊一点,确实需要传入项的名字,前三个参数分别是服务器句柄,组句柄和项名字,后面两个数据类型和读写权限的参数只需要初始化即可,函数调用成功后会将获取的数据类型和读写权限返回给这最后两个参数的指针。
译文:
int NumberOfItemProperties(HANDLE hConnect, LPCSTR ItemName);
这个函数返回与指定标签相关联的 OPC 项属性编号。
头文件注释翻译:
返回指定项的项属性编号。使用GetItemPropertyDescription函数填充可访问的属性描述的内部数组。***小心***这是由dll为每个服务器维护的一个项属性数组。应用程序必须读取给定项的所有属性描述,并在为新项调用NumberOfItemProperties之前保存PropertyID。
备注:在这儿特别区分一下,一般项和标签都是混着说,但是准确的说,标签指的是服务器中的一个数据点,一个项指的是客户端到服务器一个具体标签的连接,是的,项是一个抽象的东西,准确定义,它是一个“连接”。在DLL看来,服务器中每个标签是按一个固定顺序排列的,比如名字叫“123”的标签在DLL看来就是服务器中第3个标签,通过这个函数给定服务器中标签名,可以查看当前标签是服务器中第几个标签,这样省去了用前面的函数一层一层遍历来找指定标签了。
译文:
BOOL GetItemPropertyDescription(HANDLE hConnect, int PropertyIndex, DWORD *pPropertyID,VARTYPE *pVT, BYTE *pDescr, int BufSize);
可以通过遍历NumberOfOPCItemProperties创建的列表来获得每个项属性的BYTE *pDescr描述。
头文件注释翻译:
返回由NumberOfItemProperties()指定的最后一个项的属性值说明,应用程序可以使用返回的PropertyID,并使用ReadPropertyValue()从服务器读取属性值。
备注:按照说明,这个函数用来获取一个项属性的描述,可能是对这个标签的注释吧,解释这个标签代表什么物理量,实际中暂时没见过。
BOOL ReadPropertyValue(HANDLE hConnect, LPCSTR Itemname, DWORD PropertyID, VARIANT *pValue);
ReadItemProperty返回项属性的当前值。
头文件注释翻译:
ReadItemProperty返回项属性的当前值。
备注:这里是把指定项的属性值获取到了,注意只是属性值!比如这个项是什么数据类型,并不是项的值是多少!
译文:
从OPC服务器获取更新的标签值(读标签值)
BOOL EnableOPCNotification(HANDLE hConnect, NOTIFYPROC lpCallback);
如果应用程序支持回调,当连接的服务器发出了标签数据值改变的通知时,WTclient将发出回调。回调的原型定义如下:
void CALLBACK EXPORT OPCUpdateCallback(HANDLE hGroup, HANDLE hItem, VARIANT *pVar, FILETIME timestamp, DWORD quality)
一个标签的当前值、时间戳和OPC质量标志将通过回调提供给应用程序。标签由组句柄和项句柄联系。
译文:
BOOL ReadOPCItem(HANDLE hConnect, HANDLE hGroup, HANDLE hItem, VARIANT *pVar, FILETIME *pTimeStamp, DWORD *pQuality);
如果已将WTclient配置为DLLBuffering=TRUE,由DLL维护项列表,则应用程序可通过ReadOPCItem函数读取标签的当前值。如果请求的项不可用(或DLLBuffering=FALSE),函数将返回FALSE
可由应用程序用于读取项的当前值。
头文件注释翻译:
可由应用程序用于读取项的当前值,仅当EnableDLLBuffering设置为TRUE时且已与服务器建立连接才有效。
备注:建立项后用来读取对应标签的值,包括其数值,数据类型,时间戳,点质量。
译文:
HRESULT ReadOPCItemFromDevice(HANDLE hConnect, HANDLE hGroup, HANDLE hItem, VARIANT *pVar, FILETIME *pTimeStamp, DWORD *pQuality);
HRESULT ReadOPCItemFromCache(HANDLE hConnect, HANDLE hGroup, HANDLE hItem, VARIANT *pVar, FILETIME *pTimeStamp, DWORD *pQuality);
这些函数从连接的服务器对指定项执行同步的读取。读取数据可以从服务器的缓存请求,也可以直接从设备请求。
头文件注释翻译:
ReadOPCItemFromDevice(…)
使用SyncIO接口直接从服务器读取一个OPC项。
ReadOPCItemFromCache(…)
使用SyncIO接口直接从服务器缓存读取一个OPC项。
备注:这两个函数使用的是OPC同步IO接口,实现了同步读,实际中用ReadOPCItem()一般就足够了。
译文:
向服务器写标签值
DWORD WriteOPCItem(HANDLE hConnect, HANDLE hGroup, HANDLE hItem, VARIANT *pVar, BOOL DoAsync);
向连接的服务器写入新数据是通过WriteOPCItem函数完成的。必须指定组和项的句柄以及要写入的数据。DoAsync参数为TRUE时dll对服务器执行异步写入。为FALSE则同步写入,同步写入时返回值表示从服务器返回的hResult,非零值表示错误。对于异步写入,返回值是定义异步回调句柄的事务ID,零值表示错误。
头文件注释翻译:
允许控制应用程序写入定义的OPC项,对于同步写入,返回值为从服务器返回的hResult。对于异步写入,返回值是一个非零的TransactionID,用于标识WriteCompletion回调。如果TransactionID返回零,则表示异步写入请求失败。
备注:同步写类似一种阻塞方式,客户端知道服务器全写完了才能再写,异步写只需要发出写的请求给服务器就可以了,服务器那边写完给不给客户端通知完全不管,一般来说想追求效率肯定是异步写较好。
译文:
BOOL EnableAsyncWriteNotification(HANDLE hConnect, NOTIFYPROC lpCallback);
对于异步写入请求,可以将dll配置为在写入完成时向应用程序发出回调。回调的原型定义如下:
void CALLBACK EXPORT AsyncWriteCallback (HANDLE hGroup, DWORD TransactionID, DWORD hResult)
The TransactionID 定义特定于此回调的写入请求,也就是说,之前的WriteOPCItem函数在异步写时,如果返回的TransactionID与这里的回调中的TransactionID相等,那么进入这个回调。 hResult定义写入状态.,这个变量为S_OK,代表写入成功,否则写入失败。
译文:
一些杂项函数和回调函数
BOOL GetSvrStatus(HANDLE hConnect, OPCSERVERSTATUS *pSvrStatus,int VendorInforBufSize);
在提供的缓冲区OPCSERVERSTATUS *pSvrStatus中返回标识的服务器的当前状态。
头文件注释翻译:
允许控制应用程序询问正在运行的连接的服务器的状态。pSvrStatus指向包含指向要接收VendorInfo字符串(WSTR)的缓冲区的指针。VendorInfoBufSize定义此缓冲区的长度,以防止dll溢出。可以使用pSvrStatus=NULL调用GetSvrStatus,在这种情况下返回值TRUE表示服务器处理了接口调用,但是未返回任何数据。
备注:这个函数用来获取服务器状态。
译文:
BOOL SetClientName(HANDLE hConnect, LPCSTR Name);
此函数允许客户端定义一个名称来指定与服务器的连接。
BOOL EnableErrorNotification(ERRORPROCAPI lpCallback);
WTClient dll可以通知客户端应用程序在处理对OPC服务器的数据接口调用期间发生的错误。如果在处理用户请求期间出现意外情况,则dll可能会生成错误消息。dll的默认操作是会弹出一个对话框提示用户错误信息。在大多数情况下,应用程序本身更适合处理错误消息,而不是由dll生成对话框。如果启用了EnableErrorNotification,则控件将通过以下回调传递给应用程序:
void CALLBACK EXPORT ErrorMsgCallback(DWORD hResult, char *pMsg)
pMsg 中包含错误的文本描述以及由此产生的 hResult.
BOOL EnableClientEventMsgs(EVENTMSGPROC lpCallback);
WTClient dll还可以在与服务器连接的操作过程中通知客户端应用程序各种事件。这些事件基本上用于调试,并表示与处理单个接口相关的正常活动(例如进入和退出特定函数)。通常,客户端应用程序不需要知道这些事件,但是,如果它希望提供连接如何工作的低级描述,则可以通过启用事件msg在这些调试语句发生时接收它们。
void CALLBACK EXPORT EventMsgCallback(char *pMsg)
pMsg包含事件的文本描述。
BOOL EnableShutdownNotification(HANDLE hConnect, SHUTDOWNPROCAPI lpCallback);
如果连接的OPC服务器希望关闭,它可以请求任何或所有客户端断开连接。客户端应用程序应在WTClient dll中启用关闭通知EnableShutdownNotification来处理此请求并终止连接。
void CALLBACK EXPORT ShutdownCallback(HANDLE hConnect)
hConnect参数标识请求断开连接的服务器连接。