OPC服务器是如何被找到的?

上篇谈了OPC和COM的渊源,现在继续谈下如何找到服务器。

今天要说的是在每个服务器上必装的一个来自OPC基金会的应用,OpcEnum.exe。当一个客户端程序要和服务器沟通时第一步就是要问,“嘿,你那有我要的东西吗?有装OPC服务器吗?”。这个程序就负责回答这个问题。

看一下OPCEnum.exe中IDL定义的唯一一个类OpcServerList。它执行了opccomn.idl中的两个接口,IOpcServerList2和IOpcServerList。 

[
	uuid(13486D43-4821-11D2-A494-3CB306C10000),
	version(1.1),
	helpstring("OpcEnum 1.1 Type Library")
]
library OpcEnumLib
{
	importlib("stdole32.tlb");
	importlib("stdole2.tlb");

	[
		uuid(13486D51-4821-11D2-A494-3CB306C10000),
		helpstring("OpcEnum Class")
	]
	coclass OpcServerList
	{
		[default] interface IOpcServerList2;
		          interface IOpcServerList;
	};
};

在这个类的具体执行中,这二个接口都被照顾到了,见OpcSeLst.cpp如下(相关非全部),

class ATL_NO_VTABLE COpcServerList : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<COpcServerList, &CLSID_OpcServerList>,
	public IOPCServerList,
	public IOPCServerList2
{
// IOpcServerList
public:
	STDMETHOD(EnumClassesOfCategories)(
			/*[in]*/ ULONG cImplemented,
			/*[in,size_is(cImplemented)]*/ CATID rgcatidImpl[],
			/*[in]*/ ULONG cRequired,
			/*[in,size_is(cRequired)]*/ CATID rgcatidReq[],
			/*[out]*/ IEnumCLSID** ppenumClsid);
// IOpcServerList2
public:
	STDMETHOD(EnumClassesOfCategories)(
			/*[in]*/ ULONG cImplemented,
			/*[in,size_is(cImplemented)]*/ CATID rgcatidImpl[],
			/*[in]*/ ULONG cRequired,
			/*[in,size_is(cRequired)]*/ CATID rgcatidReq[],
			/*[out]*/ IOPCEnumGUID** ppenumClsid);

至此便明白了,只要能获得OpcServerList的实例,必然有IOpcServerList2和IOpcServerList的接口,下面只要根据需要调用其中一个EnumClassesOfCategories就行了。那么话又说回来,EnumClassesOfCategories是如何帮我找到OPC服务器的呢?继续看,相关非全部的程序,

STDMETHODIMP COpcServerList::EnumClassesOfCategories2(
			/*[in]*/ ULONG cImplemented,
			/*[in,size_is(cImplemented)]*/ CATID rgcatidImpl[],
			/*[in]*/ ULONG cRequired,
			/*[in,size_is(cRequired)]*/ CATID rgcatidReq[],
			/*[out]*/ IEnumCLSID** ppenumClsid)
{
	ICatInformationPtr pCatInfo;
	HRESULT hr = pCatInfo.CreateInstance(CLSID_StdComponentCategoriesMgr);
	
	return pCatInfo->EnumClassesOfCategories(cImplemented, rgcatidImpl, 
		cRequired, rgcatidReq, ppenumClsid);
}

看到这里就明白了,先获得个微软COM提供的关于CLSID_StdComponentCategoriesMgr的实例,然后根据输入的CATID返回一个枚举实例,你所要的OPC服务器实例尽在该枚举中了。注意CATID可以是多个,如基金会定义的CATID_OPCDAServer10/20/30(相应的uuid:{63D5F430-CFE4-11D1-B2C8-0060083BA1FB}, {63D5F432-CFE4-11D1-B2C8-0060083BA1FB} 和{CC603642-66D7-48F1-B69A-B625E73652D7})。

下面看下Matrikon的实例,很明显地,上述三个CATID都出现了,也就是说该服务器注册时注册了三个CATID,也说是表明本程序执行了基金会定义的DA规范1.0到3.0的接口函数。


这里还发现了另外二个CATID,{7DE5B060-E089-11D2-A5E6-000086339399}和{58E13251-AC87-11D1-84D5-00608CB8A7E9}。这是谁的CATID?查一下IDL文件,在opc_ae.idl中找到如下的,

//==============================================================================
// Category ID declaration (defined as an interface to ensure they show up in the typelib).

[uuid(58E13251-AC87-11d1-84D5-00608CB8A7E9)] interface OPCEventServerCATID : IUnknown {}

cpp_quote("#define CATID_OPCAEServer10 IID_OPCEventServerCATID")

在opchda.idl中找到了,

//==============================================================================
// Category ID declarations (defined as interfaces to ensure they show up in the typelib).

[uuid(7DE5B060-E089-11d2-A5E6-000086339399)] interface CATID_OPCHDAServer10 : IUnknown {}

cpp_quote("#define CATID_OPCHDAServer10 IID_CATID_OPCHDAServer10")

至此五个CATID都清楚了,也就是Matrikon实现了DA,HDA和AE的规范,如下图所见和我们的分析一样,遗憾的是它没有执行security方面的IDL,是要你出银子的。这方面可以找我进一步咨询也行。


最后来点干货,看看是如何用到我刚才的分析,

HRESULT enumServerList(wchar_t hostname[], MULTI_QI MultiQI[], DWORD count)
{
	// initialize server security info here if required.
	COSERVERINFO ServerInfo = { NULL };
	ServerInfo.pwszName = hostname;

	HRESULT hr = CoCreateInstanceEx(CLSID_OpcServerList, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, &ServerInfo, count, MultiQI); 

	if (FAILED(hr))
	{
		OLECHAR *bstrGuid;
		StringFromCLSID(CLSID_OpcServerList, &bstrGuid);
		_com_error err(hr);
		LPCTSTR errMsg = err.ErrorMessage();

		_tprintf(_T("enumServerList failed: %s for interface %S\n"), errMsg, bstrGuid);
		CoTaskMemFree(bstrGuid);
	}

	return hr;
}

这段主要就是获得一个在服务器端的关于CLSID_OpcServerList的实例,CLSID_OpcServerList是定义在OpcEnum_i.c中的,

MIDL_DEFINE_GUID(CLSID, CLSID_OpcServerList,0x13486D51,0x4821,0x11D2,0xA4,0x94,0x3C,0xB3,0x06,0xC1,0x00,0x00);

数值和OpcEnum.idl中一模一样,说白了就是要获得一个OpcServerList的实例,从OpcEnum.exe中。

        [
		uuid(13486D51-4821-11D2-A494-3CB306C10000),
		helpstring("OpcEnum Class")
	]
	coclass OpcServerList

下篇给出个自产的用C和COM写的获取OPC服务器的示例,望有所收获!

在MATLAB R2016b版本中,OPC Toolbox为用户提供了简洁的接口来进行工业自动化中OPC服务器的数据交互。要连接并读取OPC服务器的数据,首先需要安装并确认OPC Toolbox已正确安装。接下来,可以通过以下步骤操作: 参考资源链接:[OPC Toolbox用户指南:MATLAB版](https://wenku.youkuaiyun.com/doc/3ghfvw1t3s?spm=1055.2569.3001.10343) 1. 创建OPC连接:使用`opcserverinfo`函数检查并创建到指定OPC服务器的连接。例如: ```matlab [server, status] = opcserverinfo('localhost', 'OPC.A&E.Simulation.1'); ``` 这将返回与指定服务器的连接对象或状态信息。 2. 连接到OPC服务器:使用`opcconnect`函数连接到服务器,确保已有的服务器对象: ```matlab if status == 0 server = opcconnect(server); end ``` 3. 浏览OPC服务器项:使用`browse`方法来浏览服务器上的数据项和组: ```matlab items = browse(server, 'Items'); ``` 4. 读取OPC数据项:选择感兴趣的项并使用`read`方法读取数据值: ```matlab if ~isempty(items) itemID = items{1}.ItemID; data = read(server, itemID); end ``` 这里的`data`变量将包含读取的数据值。 5. 断开连接:操作完成后,使用`opcdisconnect`函数来断开与OPC服务器的连接: ```matlab opcdisconnect(server); ``` 以上步骤展示了如何在MATLAB中使用OPC Toolbox连接和读取OPC服务器的数据。建议在进行以上操作前,先阅读《OPC Toolbox用户指南:MATLAB版》,该手册不仅提供了详细的操作说明,还包含了大量的使用示例和最佳实践,可帮助用户更好地理解和使用OPC Toolbox。 为了深入理解OPC标准和MATLAB中OPC Toolbox的更多功能,用户可以在MathWorks的官方网站上找到相关的技术文档、教程和社区支持。在实际应用中,用户可能需要根据实际OPC服务器和数据的具体情况调整代码,以及处理可能出现的通信错误和异常情况。 参考资源链接:[OPC Toolbox用户指南:MATLAB版](https://wenku.youkuaiyun.com/doc/3ghfvw1t3s?spm=1055.2569.3001.10343)
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值