📄 server.cpp
字号:
// **************************************************************************
// server.cpp
//
// Description:
// Implements the CKServer class. An object of this class is associated with
// with each OPC Server we may connect to.
//
// DISCLAIMER:
// This programming example is provided "AS IS". As such Kepware, Inc.
// makes no claims to the worthiness of the code and does not warranty
// the code to be error free. It is provided freely and can be used in
// your own projects. If you do find this code useful, place a little
// marketing plug for Kepware in your code. While we would love to help
// every one who is trying to write a great OPC client application, the
// uniqueness of every project and the limited number of hours in a day
// simply prevents us from doing so. If you really find yourself in a
// bind, please contact Kepware's technical support. We will not be able
// to assist you with server related problems unless you are using KepServer
// or KepServerEx.
// **************************************************************************
#include "stdafx.h"
#include "opctestclient.h"
#include "server.h"
#include "group.h"
#include "item.h"
#include "shutdownsink.h"
#include "mainwnd.h"
// Versions for serializing:
#define VERSION_1 1 // serialized verions
#define CURRENT_VERSION VERSION_1 // current version for saving
// m_arrMultiQI indices:
#define MQI_IOPCSERVER 0
#define MQI_IOPCCOMMON 1
#define MQI_IOPCCONNPT 2
#define MQI_IOPCITEMPROP 3
#define MQI_IOPCBROWSE 4
#define MQI_IOPCPUBLIC 5
#define MQI_IOPCPERSIST 6
// Satisfy OPCENUM requirements
static const CLSID CLSID_OPCServerList =
{ 0x13486D51, 0x4821, 0x11D2, { 0xA4, 0x94, 0x3C, 0xB3, 0x06, 0xC1, 0x00, 0x00 } };
/////////////////////////////////////////////////////////////////////////////
// CKServer construction/destruction
/////////////////////////////////////////////////////////////////////////////
// **************************************************************************
// CKServer ()
//
// Description:
// Default constructor.
//
// Parameters:
// none
//
// Returns:
// none
// **************************************************************************
CKServer::CKServer ()
{
// Initialize members:
m_strProgID = cApp.GetDefConnectProgID ();
m_strRemoteMachine.Empty ();
ZeroMemory (&m_bfFlags, sizeof (m_bfFlags));
m_pPrev = NULL;
m_pNext = NULL;
m_hTreeItem = NULL;
m_pGroupHead = NULL;
m_cdwGroups = 0;
// Fill our multiple query interface structure with all of the
// interfaces we can possible request at the server level:
for (int i = 0; i < sizeof (m_arrMultiQI) / sizeof (MULTI_QI); i++)
{
m_arrMultiQI [i].pItf = NULL;
m_arrMultiQI [i].hr = 0;
}
// Interface pointers we will query and maintain:
m_pIServer = NULL;
m_pICommon = NULL;
m_pIConnPtContainer = NULL;
m_pIItemProps = NULL;
m_pIBrowse = NULL;
m_pIPublicGroups = NULL;
m_pIPersistFile = NULL;
m_pIShutdownSink = NULL;
m_dwCookieShutdownSink = 0;
m_bConnected = false;
}
// **************************************************************************
// CKServer
//
// Description:
// Constructor for when OPC Server's prog ID and the machine name it will be
// running on are known.
//
// Parameters:
// CString &strProgID Prog ID of server.
// CString &strRemoteMachine Name of remote machine.
//
// Returns:
// none
// **************************************************************************
CKServer::CKServer (CString &strProgID, CString &strRemoteMachine)
{
ASSERT (!strProgID.IsEmpty ());
// Initialize members:
m_strProgID = strProgID;
m_strRemoteMachine = strRemoteMachine;
ZeroMemory (&m_bfFlags, sizeof (m_bfFlags));
m_pPrev = NULL;
m_pNext = NULL;
m_hTreeItem = NULL;
m_pGroupHead = NULL;
m_cdwGroups = 0;
// Fill our multiple query interface structure with all of the
// interfaces we can possible request at the server level:
for (int i = 0; i < sizeof (m_arrMultiQI) / sizeof (MULTI_QI); i++)
{
m_arrMultiQI [i].pItf = NULL;
m_arrMultiQI [i].hr = 0;
}
// Interface pointer we will query and maintain:
m_pIServer = NULL;
m_pICommon = NULL;
m_pIConnPtContainer = NULL;
m_pIItemProps = NULL;
m_pIBrowse = NULL;
m_pIPublicGroups = NULL;
m_pIPersistFile = NULL;
m_pIShutdownSink = NULL;
m_dwCookieShutdownSink = 0;
m_bConnected = false;
}
// **************************************************************************
// ~CKServer ()
//
// Description:
// Destructor.
//
// Parameters:
// none
//
// Returns:
// none
// **************************************************************************
CKServer::~CKServer ()
{
// Remove all groups:
RemoveAllGroups ();
// Disconnect from the OPC Sserver we are connected to:
Disconnect ();
// Assert that all interfaces and groups have been properly released:
ASSERT (m_cdwGroups == 0);
ASSERT (m_pIServer == NULL);
ASSERT (m_pICommon == NULL);
ASSERT (m_pIConnPtContainer == NULL);
ASSERT (m_pIItemProps == NULL);
ASSERT (m_pIBrowse == NULL);
ASSERT (m_pIPublicGroups == NULL);
ASSERT (m_pIPersistFile == NULL);
ASSERT (m_pIShutdownSink == NULL);
ASSERT (m_dwCookieShutdownSink == 0);
}
/////////////////////////////////////////////////////////////////////////////
// CKServer group management
/////////////////////////////////////////////////////////////////////////////
// **************************************************************************
// AddGroup ()
//
// Description:
// Called to add an OPC Group. A CKGroup will be added to this object if
// needed and a request to add a corresponding OPC Group to the OPC Server
// will be made.
//
// Parameters:
// CKGroup *pGroup Pointer to group object to add.
// bool bLoadingProject Set to true if call is made during project
// load so group can be added to this object's
// group list. Otherwise we will assume group
// already exists in list, and only needs to
// added to OPC Server.
//
// Returns:
// void
// **************************************************************************
void CKServer::AddGroup (CKGroup *pGroup, bool bLoadingProject /*= false */)
{
ASSERT (pGroup != NULL);
// If the group is new add it to our list:
if (!bLoadingProject)
{
// New groups are added to the head of the linked list.
// That means new the groups's "next" group is old head of list,
pGroup->SetNext (m_pGroupHead);
// and the new group is the old head of list's "previous" group,
if (m_pGroupHead)
m_pGroupHead->SetPrev (pGroup);
// and that new group is now the new "head" of list.
m_pGroupHead = pGroup;
// Don't forget to bump up the group count:
++m_cdwGroups;
}
// If we are connected to OPC Server, issue a request to add this group:
if (m_bConnected)
{
// Initialize arguments for add group request:
HRESULT hr = E_FAIL;
WCHAR *pszName = NULL;
long lBias = pGroup->GetBias ();
float fDeadband = pGroup->GetDeadband ();
LPCTSTR lpszName = pGroup->GetName ();
DWORD dwRevUpdateRate = 0;
OPCHANDLE hServer = NULL;
IUnknown *pUnknown = NULL;
// All strings transmitted by COM must be in wide character (Unicode) format.
// Declare a buffer to contain name string in wide character format.
WCHAR wchBuffer [DEFBUFFSIZE];
// Convert the string format:
if (lpszName != NULL)
{
#ifdef _UNICODE
// String is already in Unicode format, so simply copy into buffer:
lstrcpyn (wchBuffer, lpszName, sizeof (wchBuffer) / sizeof (WCHAR));
#else
// String is not in Unicode format, so convert and place result into buffer:
if (!MultiByteToWideChar (CP_ACP, 0, lpszName, -1, wchBuffer, DEFBUFFSIZE))
{
ASSERT (FALSE);
return;
}
#endif
// Reassign name pointer to buffer:
pszName = wchBuffer;
}
// Issue add OPC Group request using IOPCServer interface. Pointer to
// the IOPCServer interface, m_pIServer, should have been set in Connect().
hr = m_pIServer->AddGroup (
pszName, // [in] group name
pGroup->IsActive (), // [in] active state
pGroup->GetUpdateRate (), // [in] requested update rate
(OPCHANDLE) pGroup, // [in] our handle to this group
&lBias, // [in] time bias
&fDeadband, // [in] percent deadband
pGroup->GetLanguageID (), // [in] requested language ID
&hServer, // [out] server handle to this group
&dwRevUpdateRate, // [out] revised update rate
IID_IUnknown, // [in] request an IUknown pointer
&pUnknown);
// OPC Group was successfully added:
if (SUCCEEDED (hr))
{
// Log success:
LogMsg (IDS_GROUP_ADD_SUCCESS, pGroup->GetName (), GetProgID ());
// Since OPC Group was successfully added, we can go ahead an initialize
// the associated CKGroup object.
// We can now consider group valid:
pGroup->SetValid (TRUE);
// We should have gotten a valid pointer to the OPC Groups's IUnknown interface.
// Set some things that only make sence if we have a vaild IUnknown pointer:
if (pUnknown)
{
// Set the OPC Server's handle for this group:
pGroup->SetServerHandle (hServer);
// Reset update rate if OPC Server does not support requested rate:
if (pGroup->GetUpdateRate () != dwRevUpdateRate)
pGroup->SetUpdateRate (dwRevUpdateRate);
// Initialize the CKGroup object, which will include getting necessary
// interface pointers from IUnknown:
pGroup->Initialize (pUnknown);
// We can release the IUnknown pointer since initialized group
// should have gotten the interface pointers it needs from it.
pUnknown->Release ();
}
else
{
TRACE (_T("OTC: Warning added group %s to OPC server, but IUnknown is invalid.\r\n"),
pGroup->GetName ());
}
}
// OPC Group was not successfully added:
else
{
// Log message that says add OPC Group request failed:
LogMsg (IDS_GROUP_ADD_FAILURE, pGroup->GetName (), GetProgID (), hr);
}
}
// No connection:
else
{
// Log message that says we can't add the OPC Group because we are not
// connected to OPC Server:
LogMsg (IDS_SERVER_ADD_GROUP_FAILED_NOCONNECTION, pGroup->GetName (), GetProgID ());
}
}
// **************************************************************************
// AddClonedGroup ()
//
// Description:
// Add cloned group to list.
//
// This is called from CKGroup::Clone (), which takes care of instantiating
// the new CKGroup object and adding its associated OPC Group to the OPC
// Server. All that's left to do here is add the clone to the group list.
//
// Parameters:
// CKGroup *pClone Pointer to cloned group.
//
// Returns:
// void
// **************************************************************************
void CKServer::AddClonedGroup (CKGroup *pClone)
{
// New groups are added to the head of the linked list.
// That means the clone's "next" group is old head of list,
pClone->SetNext (m_pGroupHead);
// and the clone is the old head of list's "previous" group,
if (m_pGroupHead)
m_pGroupHead->SetPrev (pClone);
// and that clone is now the new "head" of list.
m_pGroupHead = pClone;
// Don't forget to bump up the group count:
++m_cdwGroups;
}
// **************************************************************************
// RemoveGroup ()
//
// Description:
// Remove an OPC Group from OPC server, and delete associated CKGroup object
// if asked.
//
// Parameters:
// CKGroup *pGroup Pointer to CKGroup associated with OPC Group
// to remove.
// bool bDelete Delete CKGroup object after removing
// associated OPC Group. true by default.
//
// Returns:
// void
// **************************************************************************
void CKServer::RemoveGroup (CKGroup *pGroup, bool bDelete /* = true */)
{
ASSERT (pGroup != NULL);
HRESULT hr;
// Uninitialize group with OPC server. This means all interfaces it
// is using are released. It is good practice to do this BEFORE
// issuing a remove group request. Pass the bDelete value so the
// group knows to delete its items if any.
pGroup->Uninitialize (bDelete);
// remove the group from the OPC server
if (pGroup->IsValid ())
{
ASSERT (m_pIServer != NULL);
// Issue remove OPC Group request using IOPCServer interface. Pointer to
// the IOPCServer interface, m_pIServer, should have been set in Connect().
hr = m_pIServer->RemoveGroup (
pGroup->GetServerHandle (), // server handle for this group.
pGroup->IsForceDeletion ()); // force a delete if this client has not released the group properly.
// Log success or failure:
if (SUCCEEDED (hr))
LogMsg (IDS_GROUP_REMOVE_SUCCESS, pGroup->GetName (), GetProgID ());
else
LogMsg (IDS_GROUP_REMOVE_FAILURE, pGroup->GetName (), GetProgID (), hr);
}
// Delete the group if asked:
if (bDelete)
{
// We need to remove the group from the linked list before we delete it.
// To remove a link, we must first get pointers to the adjacent links:
CKGroup *pPrev = pGroup->GetPrev ();
CKGroup *pNext = pGroup->GetNext ();
// If there is a "previous" link, then its "next" is removed link's "next",
if (pPrev)
pPrev->SetNext (pNext);
// and if there is a "next" link, then its "previous" is removed link's "previous",
if (pNext)
pNext->SetPrev (pPrev);
// and if removed link was the "head", then the new head is removed link's "next".
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -