本文接 本系列上一篇: 《OPC服务器开发之WtOPCSvr(2)》
在前面我们已经简单介绍了OPCSvr的开发环境的搭建,并简单聊了下关于WtOPcsvr的激活,以及各种回调事件的注册。本篇我们着重讲解Tag操作的几个高频函数。
一、标签读写和事件
1、OPC Item Functions 和 Callback Enabling Functions
/
//
// OPC Item Functions
//
/
//
// SetWtOPCsvrQualifier(...)
// Allows the application to supply the delimiting character used to
// seperate tag names in a hiearchial namespace.
// (The delimiting character by default is '.')
//
// CreateTag()
// Add an OPC Item to the WtOPCsvr.DLL local tag list. The dll takes care of
// all client references to the tag and provides a callback to the application
// if the tag is updated by a client. Once a tag gets created, it's name will
// automatically be presented in the browse list to any interested OPC Client.
//
// UpdateTag()
// UpdateTagWithTimeStamp()
// UpdateTagByName()
// Allows the Server Application to change the value, quality and timestamp of
// a tag. All updates are automatically provided to subscribing clients as defined
// by the particular connection.
//
// SetTagProperties()
// Tag Properties are values associated with an OPC Item other than its Value,
// Quality and TimeStamp. Any property value may be assigned by the server
// for a defined tag.
//
// ReadTag()
// ReadTagWithTimeStamp()
// Allows the Application to read each tag value from the WtOPCsvr.dll local Tag List.
//
// SuspendTagUpdates()
// When a Tag is created by the Server Application, it is automatically enabled for
// client subscription. The tag name will automatically be included in the server
// browse list and any client may connect and read it's current value. In certain
// applications, it is desirable to only create the tag whenever a client requests it.
// The UNKNOWNITEMPROC callback may be used for dynamic tag allocation, but until the
// tag is created, the tag name will not show up in the browse list. Creation of the
// tag followed by a call to SuspendTagUpdates() will allow the tag name to be browsed
// by a client, but not subscribed to. In this scenario, the WtOPCsvr.dll will issue
// the UNKNOWNITEMPROC callback to allow the Application to enable the tag and begin
// updating it's value only when being actively subscribed by an OPC Client.
//
// RemoveTag()
// Deletes a tag from the WtOPCsvr Tag List.
//
/
_declspec(dllexport) char WINAPI SetWtOPCsvrQualifier (char qualifier);
_declspec(dllexport) HANDLE WINAPI CreateTag (LPCSTR Name, VARIANT Value, WORD InitialQuality, BOOL IsWritable);
_declspec(dllexport) BOOL WINAPI UpdateTag (HANDLE TagHandle, VARIANT Value, WORD Quality);
_declspec(dllexport) BOOL WINAPI UpdateTagWithTimeStamp (HANDLE TagHandle, VARIANT Value, WORD Quality, FILETIME timestamp);
_declspec(dllexport) BOOL WINAPI UpdateTagByName (LPCSTR Name, VARIANT Value, WORD Quality);
_declspec(dllexport) BOOL WINAPI UpdateTagWithTimeStampByName (LPCSTR Name, VARIANT Value, FILETIME TimeStamp, WORD Quality);
_declspec(dllexport) BOOL WINAPI SetTagProperties (HANDLE TagHandle, DWORD PropertyID, LPCSTR Description, VARIANT Value);
_declspec(dllexport) BOOL WINAPI ReadTag (HANDLE TagHandle, VARIANT *pValue);
_declspec(dllexport) BOOL WINAPI ReadTagWithTimeStamp (HANDLE TagHandle, VARIANT *pValue, WORD *pQuality, FILETIME *pTimestamp);
_declspec(dllexport) BOOL WINAPI SuspendTagUpdates (HANDLE TagHandle, BOOL OnOff);
_declspec(dllexport) BOOL WINAPI RemoveTag (HANDLE TagHandle);
/
//
// Callback Enabling Functions
//
/
_declspec(dllexport) BOOL WINAPI EnableWriteNotification (WRITENOTIFYPROC lpCallback, BOOL ConvertToNativeType);
_declspec(dllexport) BOOL WINAPI EnableUnknownItemNotification (UNKNOWNITEMPROC lpCallback);
_declspec(dllexport) BOOL WINAPI EnableItemRemovalNotification (ITEMREMOVEDPROC lpCallback);
_declspec(dllexport) BOOL WINAPI EnableDisconnectNotification (DISCONNECTPROC lpCallback);
_declspec(dllexport) BOOL WINAPI EnableEventMsgs (EVENTMSGPROC lpCallback);
_declspec(dllexport) BOOL WINAPI EnableRateNotification (RATECHANGEPROC lpCallback);
_declspec(dllexport) BOOL WINAPI EnableDeviceRead (DEVICEREADPROC lpCallback);
_declspec(dllexport) BOOL WINAPI EnableErrorCallback (ERRORPROCAPI lpCallback);
/
1.1 SetWtOPCsvrQualifier
设置OPC服务器解析标签名称的分隔符,通常默认使用".“,什么意思呢?我们在前面也提到过标签组的概念,“.” 分隔符可以帮助我们指定标签属于哪个组,用于分层管理,譬如"温度控制.温度1”、“温度控制.温度2” … ,这样可知标签温度1
和温度2
都属于温度控制
的组,在创建OPCBrowser的时候,可以树形图分组展示和操作。
通常情况下,开发者都不必特别指定,使用默认即可。
1.2 创建标签CreateTag
_declspec(dllexport) HANDLE WINAPI CreateTag (LPCSTR Name, VARIANT Value, WORD InitialQuality, BOOL IsWritable);
创建标签,需要特别注意的是注意创建标签时,指定读写属性。另外注意返回值
HANDLE
句柄,在后续操作对应标签时,均可通过此句柄进行操作。在接下来的函数中我们可以看到。所以,这里给出一点点小小的编程建议,名获取句柄的方法,二次开发的朋友可以通过自定义一个map,管理Tag名称和
HANDLE
句柄之间的映射关系(有需要可以这样做,没有项目需要可直接跳过),当然DLL也提供了一些ByName的操作方法,例如UpdateTagByName
,通过标签名,修改对应的标签值对应的还有
_declspec(dllexport) BOOL WINAPI RemoveTag (HANDLE TagHandle);
方法用于移除指定标签
1.3 【写事件】 更新标签 UpdateTag
或 UpdateTagByName
等
当服务端进入前面我们注册的写事件回调函数时,函数原型如下:
_declspec(dllexport) BOOL WINAPI EnableWriteNotification (WRITENOTIFYPROC lpCallback, BOOL ConvertToNativeType);
在WRITENOTIFYPROC lpCallbac
事件回调函数中我们需要调用UpdateTag
或 UpdateTagByName
等接口更新服务端标签的值(这里的值仅仅是写入到服务端维护的TagList 缓存中,如果服务端有接入设备,获取提供远程读写点位的接口,可以在此编写相应的业务逻辑!!)
1.4 【读事件】 ReadTag
或 ReadTagWithTimeStamp
当服务端进入前面我们注册的读回调函数时,函数原型如下:
_declspec(dllexport) BOOL WINAPI EnableDeviceRead (DEVICEREADPROC lpCallback);
在回调函数中,我们可以通过ReadTag
或 ReadTagWithTimeStamp
方法先获取标签的值,然后将数据返回给请求端。
1.5 客户端连接和断开事件
这个比较简单,不过多说。但是结合
_declspec(dllexport) int WINAPI NumbrClientConnections ();
我们可以获取当前连接客户端的数量(在某写OPCSvr有界面展示需求的项目中,会有应用场景)
1.6 指定服务端数据发布频率的事件
_declspec(dllexport) BOOL WINAPI EnableRateNotification (RATECHANGEPROC lpCallback);
这个怎么理解?在客户端我们通常会指定数据的更新频率,当客户端改变Rate时,服务端则会进入此事件的回调。我们在服务端的回调函数中,需要通过
_declspec(dllexport) BOOL WINAPI ResetServerRate (UINT ServerRate);
来更新设置服务端发布数据的频率!
2 、 事件回调函数原型
typedef VOID (CALLBACK* WRITENOTIFYPROC)(HANDLE, VARIANT*, DWORD*);
typedef VOID (CALLBACK* UNKNOWNITEMPROC)(CHAR*,CHAR*);
typedef VOID (CALLBACK* ITEMREMOVEDPROC)(HANDLE,CHAR*,CHAR*);
typedef VOID (CALLBACK* DISCONNECTPROC)(DWORD);
typedef VOID (CALLBACK* EVENTMSGPROC)(CHAR*);
typedef VOID (CALLBACK* RATECHANGEPROC)(HANDLE, DWORD);
typedef VOID (CALLBACK* DEVICEREADPROC)(HANDLE, VARIANT*, WORD*, FILETIME*);
typedef VOID (CALLBACK* ERRORPROCAPI) (HANDLE, DWORD, CHAR*);
基本上,我们前面讲到的几个事件,回调函数都返回有 Tag
对应的句柄,VARIANT
类型的数据值。具体实施方法可以查看帮助文档获取更多细节。
二、示例项目(代码节选)
前面我们说到VARAINT类型,类型转换懒得写的朋友,下面的代码拿去改改,不谢哈~
void CWTSvrTestApp::FormatVariant(VARIANT *pValue, char *buf, int bufsize)
{
switch(pValue->vt)
{
case VT_I1:
case VT_UI1: // BYTE
sprintf (buf, "%d ", pValue->bVal );
break;
case VT_I2: // SHORT
sprintf (buf, "%d ", pValue->iVal );
break;
case VT_UI2: // UNSIGNED SHORT
sprintf (buf, "%d ", pValue->uiVal );
break;
case VT_I4: // LONG
sprintf (buf, "%ld ", pValue->lVal );
break;
case VT_UI4: // UNSIGNED LONG
sprintf (buf, "%ld ", pValue->ulVal );
break;
case VT_INT: // INTEGER
sprintf (buf, "%ld ", pValue->intVal );
break;
case VT_UINT: // UNSIGNED INTEGER
sprintf (buf, "%ld ", pValue->uintVal );
break;
case VT_R4: // FLOAT
sprintf (buf, "%5.2f ", pValue->fltVal );
break;
case VT_R8: // DOUBLE
sprintf (buf, "%9.4f ", pValue->dblVal );
break;
case VT_BSTR: //BSTR
sprintf (buf, "%ls ", pValue->bstrVal );
break;
case VT_BOOL:
if (pValue->boolVal)
strcpy (buf, "TRUE");
else
strcpy (buf, "FALSE");
break;
case VT_ARRAY | VT_I2:
FormatIntArray (pValue, buf, bufsize);
return;
case VT_ARRAY | VT_R4:
FormatFltArray (pValue, buf, bufsize);
return;
case VT_ARRAY | VT_BOOL:
FormatBoolArray (pValue, buf, bufsize);
return;
default:
sprintf (buf, " unknown type:%d ", pValue->vt );
break;
}
}
~ 未完,有时间再续! <<<<<<<<< 2025/3/19
路漫漫其修远兮,吾将上下而求索