OPC Server开发的几大境界 联系客服

发布时间 : 星期二 文章OPC Server开发的几大境界更新完毕开始阅读912be06a561252d380eb6ec7

OPC server的开发相对OPC client 更加困难,OPC server 的开发主要应用COM技术,主要应用书籍为潘爱民写的《COM入门和应用》,大量的技术有很大的可重用性,在开发完一个程序后,基本上所有的东东都可以被重用,并且重新发布。 开发方式有三种:

1。简单的:就是应用已经有的如DDE to OPC 或串口 to OPC,modbus to OPC等软件把您的设备变成相关的OPC服务器。方法简单实用,费用合理快速,让你马上搭上OPC的快车。主要缺陷:你的设备必需是标准设备或更标准设备相容。这种方式比较适合产品多的厂家快速的转型,或提供OPC相关产品。

2.。一般的:应用第三方的开发包开发,这种方式相对来说减少了开发的难度,而且第三方有相应的技术支持,开发商专业做这种产品,产品有一定的稳定性。这种方式对开发包有依赖性,并且OPC服务器的稳定性依赖第三方的开发水平。本方法适于老板要求你马上开发出OPC产品是^_^,或者入门者使用。国内相关的开发包有华富慧通的和拓林的开发包。 国外的可参考:http://www.opcconnect.com/source.php上面的还可以,不过现在发现也有死链接,不过他的提示确实不错。

3。有难度的:应用COM开发。这种开发方式是最可以学习到东西的。但需要有一定的基础,而且需要对英文有一定的认识度。主要涉及的知识有一下几点。 有一定的C/C++编程经验----代码是一定要写的 能应用VC开发环境------没有屠龙刀怎么能行了 掌握一定的COM知识-----进阶的东东

阅读相关OPC开发文档-----葵花宝典

开发-------------自己杀的猪肉才好吃

Hash表相关内容,主要用于大量节点时查询-----杀人于无形 基本就按照这个学习思路,对于前两项有太多的介绍了,在此不说了。

COM技术学习可参考VChelp http://www.vckbase.com/的COM内容,其他网站都是随便说说而已 。书籍可以看潘爱民的《COM原理和应用》,《COM本质论》等书籍。 OPC文档可以

Data Access Custom Interface 和 OPC Common Definitions and Interfaces。

源码可以参考lightopc 和 GE的源码,如果没有后面那个可以给我发email,好像网上不提供下载了。

GE的程序写得很好,结构清晰,但就是太老了,好在我们尊老爱幼。

lightOPC的程序相对太多了,感觉结构不是太好,有点乱,也可能是我没有深入把。

hash的东东我还没接触,不过数据结构都学过,快速定位。现在的内存这么大,实现大一点hash表,就可以了,当然一个不错的hash函数是不可少的。这是开发过程中要考虑的。 至于开发是不是一定要用MFC和ATL就看你自己了,不用也可以,用也可以。感觉差别不是太大,如果不会就不用在学了,因为这两项并非那么容易掌握的。但COM必需要了解的。

对于这三种方法没有优劣之分,只有看你的需求如何。你付出的时间越多,相应的付出的Money越少,越灵活,学到的内容越多,这就是社会规律。在工控领域就是选择最适合的。[/nobr]

OPC主要适用于过程控制和制造自动化等应用领域。 OPC是以OLE/COM机制作为应用程序的通讯标准。OLE/COM是一种客户/服务器模式,具有语言无关性、代码重用性、易于集成性等优点。OPC规范了接口函数,不管现场设备以何种形式存在,客户都以统一的方式去访问,从而保证软件对客户的透明性,使得用户完全从低层的开发中脱离出来 然后我们再来看看OPC Server的组成

一个设备的OPC Server主要有两部组成,一是OPC标准接口的实现;二是与硬件设备的通信模块。

实现OPC 标准接口

[图1]

在这些接口中,IOPCServer 是OPC Server的主接口,通过它实现OPC Server在操作系统中的安装和注册。此接口是必须要实现的,其所有方法也必须实现。其它的接口都是可选的我们就不做介绍了,下面主要来介绍如何实现IOPCServer接口。 在IOPCServer接口中共有六个法: 1、 IOPCServer::AddGroup

HRESULT AddGroup( [in, string] LPCWSTR szName, [in] BOOL bActive,

[in] DWORD dwRequestedUpdateRate, [in] OPCHANDLE hClientGroup, [unique, in] LONG *pTimeBias, [in] FLOAT * pPercentDeadband, [in] DWORD dwLCID,

[out] OPCHANDLE * phServerGroup, [out] DWORD *pRevisedUpdateRate, [in] REFIID riid,

[out, iid_is(riid)] LPUNKNOWN * ppUnk );

此方法是在OPC Server上建立一个组。下在我们来实现这个方法: …. ….

首先要对组名(szName)进行检查,看是否有效或是否已经有这个组。 if (szName != NULL) {

RequestedName = szName; if (RequestedName == \

RequestedName = pSvrObject->DefaultGroupName(); }

else

RequestedName = pSvrObject->DefaultGroupName();

for (i=0; iNumbrGroups(); i++) {

pGroup = pSvrObject->GetGroup(i); if (RequestedName == pGroup->Name) return (OPC_E_DUPLICATENAME); }

这需要在内存中维护OPC Group(组)的列表(还要有OPC 项的列表)。

如果szName(组名)正确并且没有建立过该组,就开始根据传过来的参数进行组的建立,建立好后将该组加到自己的组列表中以备后用。

if ((dwRequestedUpdateRate == 0) || (dwRequestedUpdateRate < pApp->ServerTickRate))

ActualRate = pApp->ServerTickRate; else {

ActualRate = dwRequestedUpdateRate; MinRate = pApp->ServerTickRate; ActualRate += (MinRate/2); ActualRate /= MinRate; ActualRate *= MinRate; }

if (pRevisedUpdateRate)

*pRevisedUpdateRate = ActualRate;

pGroup = new (COPCGroup); if(pGroup == NULL)

return (E_OUTOFMEMORY);

pGroup->Name = RequestedName; pGroup->pSvrObject = pSvrObject; pGroup->MarkedForDeletion = FALSE;

pGroup->ClientGroupHandle = hClientGroup;

pGroup->UpdateRate = ActualRate;

pGroup->IsActive = bActive; if (pPercentDeadband)

pGroup->Deadband = *pPercentDeadband; else

pGroup->Deadband = 0.0; pGroup->LCID = dwLCID; if (pTimeBias)

pGroup->TimeBias = *pTimeBias; else

{

_ftime( &timebuffer );

pGroup->TimeBias = timebuffer.timezone; // pGroup->TimeBias = 300L; }

r1 = pGroup->QueryInterface(riid, (LPVOID*) ppUnk); if(FAILED(r1)) {

// If error - delete group and return

delete (pGroup);

return r1; }

pSvrObject->AddNewGroup(pGroup);

最后将新建组的接口指针返回给客户端。

*phServerGroup = pGroup->ServerGroupHandle;

2、IOPCServer::GetErrorString

HRESULT GetErrorString( [in] HRESULT dwError, [in] LCID dwLocale,

[out, string] LPWSTR *ppString );

为Server的错误代码返回相应的错误字符串。 char buf[128];

BOOL bFound = FALSE;

for( int i = 0; i < nOpcErrors; ) {

OpcError* e = &OpcErrors[i++];

if( (bFound = (hr == e->hrErr)) != FALSE ) { strcpy( buf, e->ErrText ); break;

} }

if( !bFound ) {

DWORD dwStatus = FormatMessage(

FORMAT_MESSAGE_FROM_SYSTEM

| FORMAT_MESSAGE_ARGUMENT_ARRAY, // Arguments is not a va_list NULL, // LPCVOID pointer to message source

hr, // DWORD requested message identifier

LANG_NEUTRAL, // DWORD language identifier for message buf, // LPTSTR pointer to message buffer

127, // DWORD maximum size of message buffer

NULL ); // va_list *Arguments address of array of message inserts

if( !dwStatus ) { _snprintf( buf, 127, \ } }

*ppString = pApp->WSTRFromCString( buf, TRUE);