www.pudn.com > Ge_opc_Server_v1.rar > GRP_DO.CPP


// Grp_do.cpp 
// 
//  This file contains the implementation of 
//  the LHEpipeview Group Object DataObject Async Logic 
//  for the OPC server. 
// 
// 
//  (c) COPYRIGHT 1997 INTELLUTION INC. 
//  ALL RIGHTS RESERVED 
// 
// Original Author: Al Chisholm 
// 
// Note - this logic supports independent subscriptions for 
// data with and without timestamp (although it is not likely 
// that this would ever be used) 
// 
// Note - this logic CANNOT be used at the same time as ConnectionPoints. 
// The client must use one or the other but not both. 
// 
// Modification Log: 
// Vers    Date   By    Notes 
// ----  -------- ---   ----- 
// 0.90  04/08/97 ACC   add async logic 
//       05/20/97 ACC   SendWriteStream should use GROUPHEADERWRITE, not GROUPHEADER 
//       09/11/97 ACC   checkOnDataChange should call SendStream, not SendStreamSithTime 
// 
 
#define WIN32_LEAN_AND_MEAN 
#define NOCOMM 
 
#include "OPCLHEpipeview.h" 
 
#include "OPC_PACK.h" 
 
 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::CheckDOOnDataTimeChange 
// 
// Handle IDataObject Callbacks for OnDataChange (WITH TimeStamp) 
// 
void		LHEpipeviewGroup::CheckDOOnDataTimeChange( void ) 
{ 
 
	// If group is active 
	// 
	if(!m_bActive) return; 
 
	// And if there is a Data object 
	// 
	if(!m_pILHEpipeviewDO) return; 
 
	// And if the DataObject has a callback 
	// 
	if(m_pILHEpipeviewDO->m_datatimeCallback) 
	{ 
		int i, Count, DataSize; 
 
		// Build a list of the things that changed 
		// 
		OPCHANDLE *ItemHandleList = new OPCHANDLE[ItemHandles()]; 
 
		Count = 0; 
		for(i=0, Count=0; iChanged(OPC_ODC_DOT))	// with time 
				{ 
					ItemHandleList[Count] = i; 
					Count++; 
				} 
			} 
		} 
 
		// And if anything changed 
		// 
		if(Count) 
		{ 
			// compute the size needed to send them 
			// 
			DataSize = ComputeSize(Count, ItemHandleList); 
 
			// Given the item list, build and send the callback 
			// 
			SendStreamWithTime(Count, ItemHandleList, DataSize, OPC_ODC_DOT, 0);	// with time 
		} 
 
		// Free the itemhandlelist 
		// 
		delete [] ItemHandleList; 
	} 
} 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::CheckDOOnDataChange 
// 
// Handle IDataObject Callbacks for OnDataChange (WITHOUT TimeStamp) 
// 
void		LHEpipeviewGroup::CheckDOOnDataChange( void ) 
{ 
 
	// If group is active 
	// 
	if(!m_bActive) return; 
 
	// And if there is a Data object 
	// 
	if(!m_pILHEpipeviewDO) return; 
 
	// And if the DataObject point has a callback 
	// 
	if(m_pILHEpipeviewDO->m_dataCallback) 
	{ 
		int i, Count, DataSize; 
 
		// Build a list of the things that changed 
		// 
		OPCHANDLE *ItemHandleList = new OPCHANDLE[ItemHandles()]; 
 
		Count = 0; 
		for(i=0, Count=0; iChanged(OPC_ODC_DO)) 
				{ 
					ItemHandleList[Count] = i; 
					Count++; 
				} 
			} 
		} 
 
		// And if anything changed 
		// 
		if(Count) 
		{ 
			// compute the size needed to send them 
			// 
			DataSize = ComputeSize(Count, ItemHandleList); 
 
			// Given the item list, build and send the callback (without time) 
			// 
			SendStream(Count, ItemHandleList, DataSize, OPC_ODC_DO, 0);	//without time 
		} 
 
		// Free the itemhandlelist 
		// 
		delete [] ItemHandleList; 
	} 
} 
 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::CheckDOAsyncRead 
// 
// If asyncread active 
//  If cancel requested then 
//   send cancel complete 
//   free handle list 
//   clear asyncread active 
//  else check for complete 
//  If all item reads complete then 
//   assemble read complete callback 
//   if callback ok then free the handle list 
//   clear asyncread active 
// 
void		LHEpipeviewGroup::CheckDOAsyncRead( void ) 
{ 
	BOOL complete; 
	int i, DataSize; 
 
	// If a read is in progress 
	// 
	if(m_AsyncReadActive) 
	{ 
		// then check to see if a cancel of this action was requested 
		// (TID will have been matched already) 
		// 
		if(m_AsyncReadCancel) 
		{ 
			// And if so then cancel the request 
			// (note there is no callback in this case) 
			// 
			pIMalloc->Free(m_AsyncReadList); 
			m_AsyncReadList = 0; 
			m_AsyncReadActive = 0; 
			m_AsyncReadCancel = 0; 
			return; 
		} 
 
		// Else (if no cancel) then check progress of read 
		// If read was from DEVICE then see if it's done 
		// 
		complete = 1; 
		if(m_AsyncReadSource == OPC_DS_DEVICE) 
		{ 
			for(i=0; iCheckDeviceRead(0)) 
					complete = 0; 
			} 
		} 
 
		// If Read is complete/data is ready 
		// 
		if(complete) 
		{ 
			if(m_pILHEpipeviewDO) 
			{ 
				// Compute data size for stream 
				// 
				DataSize = ComputeSize(m_AsyncReadActive, m_AsyncReadList); 
 
				// And if the DataObject has a callback 
				// 
				if(m_pILHEpipeviewDO->m_dataCallback) 
				{ 
					SendStream(m_AsyncReadActive, m_AsyncReadList, DataSize, 0, m_AsyncReadTID); 
				} 
				if(m_pILHEpipeviewDO->m_datatimeCallback) 
				{ 
					SendStreamWithTime(m_AsyncReadActive, m_AsyncReadList, DataSize, 0, m_AsyncReadTID); 
				} 
			} 
 
			// In any case the read is complete... 
			// Note that if readlist was not sent to the client 
			// then we must free it here. 
			// 
			if(m_AsyncReadList) pIMalloc->Free(m_AsyncReadList); 
			m_AsyncReadList = 0; 
			m_AsyncReadActive = 0; 
			m_AsyncReadCancel = 0; 
 
		}	// (else read not complete) 
	}		// (else read not active) 
} 
 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::CheckDOAsyncWrite 
// 
// If asyncwrite active 
//  If cancel requested 
//   send cancel complete 
//   free handle list ?? 
//   clear asyncwrite active 
//  else 
//   If all item writes complete 
//   then assemble write complete callback 
//   free handle list ?? 
//   clear asyncwrite active 
// 
void		LHEpipeviewGroup::CheckDOAsyncWrite( void ) 
{ 
	BOOL complete; 
	int i; 
 
	// If a write is in progress 
	// 
	if(m_AsyncWriteActive) 
	{ 
		// then check to see if a cancel of this action was requested 
		// (TID will have been matched already) 
		// 
		if(m_AsyncWriteCancel) 
		{ 
			// And if so then cancel the request 
			// (note there is no callback in this case) 
			// 
			pIMalloc->Free(m_AsyncWriteList); 
			m_AsyncWriteList = 0; 
			m_AsyncWriteActive = 0; 
			m_AsyncWriteCancel = 0; 
			return; 
		} 
 
		// Else (if no cancel) then check progress of Write 
		// 
		complete = 1; 
		for(i=0; iCheckDeviceWrite(0)) 
				complete = 0; 
		} 
 
		// If Write is complete 
		// 
		if(complete) 
		{ 
			if(m_pILHEpipeviewDO) 
			{ 
				// If the DataObject has a callback 
				// 
				if(m_pILHEpipeviewDO->m_writeCallback) 
				{ 
					SendWriteStream(m_AsyncWriteActive, m_AsyncWriteList, m_AsyncWriteTID); 
				} 
			} 
 
			// In any case the Write is complete... 
			// Note that if Writelist was not sent to the client 
			// then we must free it here. 
			// 
			if(m_AsyncWriteList) pIMalloc->Free(m_AsyncWriteList); 
			m_AsyncWriteList = 0; 
			m_AsyncWriteActive = 0; 
			m_AsyncWriteCancel = 0; 
 
		}	// (else Write not complete) 
	}		// (else Write not active) 
} 
 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::CheckDOFefresh 
// 
// This code is nearly identical to CheckDOAsyncRead except 
//  it uses a different set of state variables. 
//  there is a slight difference in 'active' logic 
//  and it invokes OnDataChange rather than OnReadComplete 
// 
// If refresh active 
//  If cancel requested then 
//   send cancel complete 
//   free handle list 
//   clear refresh active 
//  else check for complete 
//  If all item reads complete then 
//   assemble refresh complete callback 
//   if callback ok then free the handle list 
//   clear refresh active 
// 
void		LHEpipeviewGroup::CheckDORefresh( void ) 
{ 
	BOOL complete; 
	int i, DataSize; 
 
	// If a read is in progress 
	// 
	if(m_RefreshActive) 
	{ 
		// then check to see if a cancel was requested 
		// 
		if(m_RefreshCancel) 
		{ 
			// Cancel the refresh 
			// (note there is no callback in this case) 
			// 
			pIMalloc->Free(m_RefreshList); 
			m_RefreshList = 0; 
			m_RefreshActive = 0; 
			m_RefreshCancel = 0; 
			return; 
		} 
 
		// Else (if no cancel) then check progress of read 
		// If read was from DEVICE then see if it's done 
		// 
		complete = 1; 
		if(m_RefreshSource == OPC_DS_DEVICE) 
		{ 
			for(i=0; iCheckDeviceRead(0)) 
					complete = 0; 
			} 
		} 
 
		// If Read is complete/data is ready 
		// 
		if(complete) 
		{ 
			if(m_pILHEpipeviewDO) 
			{ 
				// Compute data size for stream 
				// 
				DataSize = ComputeSize(m_RefreshActive, m_RefreshList); 
 
				// generally there are only active items in list 
				// but if not then logic in the subroutine will handle it 
				// 
				if(m_pILHEpipeviewDO->m_dataCallback) 
				{ 
					SendStream(m_RefreshActive, m_RefreshList, DataSize, 0, m_RefreshTID); 
				} 
				if(m_pILHEpipeviewDO->m_datatimeCallback) 
				{ 
					SendStreamWithTime(m_RefreshActive, m_RefreshList, DataSize, 0, m_RefreshTID); 
				} 
			} // (else no callback) 
 
			// In any case the refresh is complete... 
			// Note that if readlist was not sent to the client 
			// then we must free it here. 
			// 
			if(m_RefreshList) pIMalloc->Free(m_RefreshList); 
			m_RefreshList = 0; 
			m_RefreshActive = 0; 
			m_RefreshCancel = 0; 
 
		}	// (else read not complete) 
	}		// (else read not active) 
} 
 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::SendStreamWithTime 
// 
// This function is used to build the stream for OnDataChange and also 
// for async operations such as Read and Refresh 
// The two cases are 'almost' the same except for 
// a small bit of 'mask' and quality logic as you can see below 
// 
// Create the Stream which consists of: 
//  GroupHeader 
//  ItemHeaders 
//  Variants and Related Data. 
// 
// 
void		LHEpipeviewGroup::SendStreamWithTime(int Count, OPCHANDLE *ItemHandleList, DWORD DataSize, WORD mask, DWORD tid) 
{ 
		int i, j, HdrSize, TotalSize; 
 
		// Allocate the Data Stream 
		// 
		HdrSize = sizeof(OPCGROUPHEADER) + Count * sizeof(OPCITEMHEADER1);	// with time 
		TotalSize = HdrSize + DataSize; 
 
		char * gp; 
		HGLOBAL gh; 
 
		gh = GlobalAlloc( GMEM_FIXED, TotalSize ); 
		if(gh == NULL) 
		{ 
			// If no memory, For now we just give up and return; 
			// But a better error handler would be nice 
			// 
			return; 
		} 
		gp = (char*)GlobalLock(gh); 
 
		OPCGROUPHEADER * GrpPtr; 
		OPCITEMHEADER1 * ItemHdr1;	// With Time 
		char * DataPtr; 
 
		GrpPtr = (OPCGROUPHEADER *) gp; 
		ItemHdr1 = (OPCITEMHEADER1 *) (gp + sizeof(OPCGROUPHEADER));	//With Time 
		DataPtr = gp + HdrSize; 
 
		// Fill in the Group header 
		// 
		GrpPtr->dwSize = TotalSize; 
		GrpPtr->dwItemCount = Count; 
		GrpPtr->hClientGroup =	m_ClientGroupHandle; 
		GrpPtr->hrStatus = S_OK; 
		GrpPtr->dwTransactionID = tid; 
 
		// Now fill in the Item Information 
		// 
 
		for(j=0; jGetValue( OPC_DS_CACHE, 
				&vtemp, 
				&ItemHdr1->wQuality, 
				&ItemHdr1->ftTimeStampItem);		// with time 
			ItemHdr1->hClient = ItemPtr(i)->GetHandle(); 
			ItemHdr1->dwValueOffset = DataPtr - gp; 
			ItemHdr1->wReserved = 0; 
 
			// Plus the variant 
			// Note that any 'pointer' in the variant will be meaningless 
			// 
			vsize = OPCVariantPack(DataPtr, &vtemp); 
			VariantClear(&vtemp); 
 
			// If this is an OnDataChange then clear the item changed flag 
			// else if this is an Async Read then do some special quality handling 
			// 
			if(mask) 
			{ 
				ItemPtr(i)->ClearChanged(mask);	//with time 
			} else 
			{ 
				// if async read then group must be active 
				// 
				if((m_AsyncReadSource == OPC_DS_CACHE) && !m_bActive) 
					ItemHdr1->wQuality = QUAL_NOTACTIVE; 
			} 
 
			// The update the pointers 
			// 
			ItemHdr1++; 
			DataPtr += vsize; 
		} 
 
		// And invoke the callback 
		// 
		STGMEDIUM stm; 
		FORMATETC fe; 
 
		fe.cfFormat = m_pILHEpipeviewDO->m_datatime;		// with time 
		fe.ptd = NULL; 
		fe.dwAspect = DVASPECT_CONTENT; 
		fe.lindex = -1; 
		fe.tymed = TYMED_HGLOBAL; 
 
		stm.tymed = TYMED_HGLOBAL; 
		stm.hGlobal = gh; 
		stm.pUnkForRelease = NULL; 
 
		m_pILHEpipeviewDO->m_datatimeCallback->OnDataChange(&fe, &stm); 
 
		// And free the storage 
		// 
		GlobalFree(gh); 
 
} 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::SendStream  (Without Time) 
// 
// This function is used to build the stream for OnDataChange and also 
// for async operations such as Read and Refresh 
// The two cases are 'almost' the same except for 
// a small bit of 'mask' and quality logic as you can see below 
// 
// Create the Stream which consists of: 
//  GroupHeader 
//  ItemHeaders 
//  Variants and Related Data. 
// 
void		LHEpipeviewGroup::SendStream(int Count, OPCHANDLE *ItemHandleList, DWORD DataSize, WORD mask, DWORD tid) 
{ 
		int i, j, HdrSize, TotalSize; 
 
		// Allocate the Data Stream 
		// 
		HdrSize = sizeof(OPCGROUPHEADER) + Count * sizeof(OPCITEMHEADER2);	// without time 
		TotalSize = HdrSize + DataSize; 
 
		char * gp; 
		HGLOBAL gh; 
 
		gh = GlobalAlloc( GMEM_FIXED, TotalSize ); 
		if(gh == NULL) 
		{ 
			// If no memory, For now we just give up and return; 
			// But a better error handler would be nice 
			// 
			return; 
		} 
		gp = (char*)GlobalLock(gh); 
 
		OPCGROUPHEADER * GrpPtr; 
		OPCITEMHEADER2 * ItemHdr1;	// Without Time 
		char * DataPtr; 
 
		GrpPtr = (OPCGROUPHEADER *) gp; 
		ItemHdr1 = (OPCITEMHEADER2 *) (gp + sizeof(OPCGROUPHEADER));	//Without Time 
		DataPtr = gp + HdrSize; 
 
		// Fill in the Group header 
		// 
		GrpPtr->dwSize = TotalSize; 
		GrpPtr->dwItemCount = Count; 
		GrpPtr->hClientGroup =	m_ClientGroupHandle; 
		GrpPtr->hrStatus = S_OK; 
		GrpPtr->dwTransactionID = tid; 
 
		// Now fill in the Item Information 
		// 
 
		for(j=0; jGetValue( OPC_DS_CACHE, 
				&vtemp, 
				&ItemHdr1->wQuality, 
				0);		// without time 
			ItemHdr1->hClient = ItemPtr(i)->GetHandle(); 
			ItemHdr1->dwValueOffset = DataPtr - gp; 
			ItemHdr1->wReserved = 0; 
 
			// Plus the variant 
			// Note that any 'pointer' in the variant will be meaningless 
			// 
			vsize = OPCVariantPack(DataPtr, &vtemp); 
			VariantClear(&vtemp); 
 
			// If this is an OnDataChange then clear the item changed flag 
			// else if this is an Async Read then do some special quality handling 
			// 
			if(mask) 
			{ 
				ItemPtr(i)->ClearChanged(mask);	//without time 
			} else 
			{ 
				// if async read then group must be active 
				// 
				if((m_AsyncReadSource == OPC_DS_CACHE) && !m_bActive) 
					ItemHdr1->wQuality = QUAL_NOTACTIVE; 
			} 
 
			// The update the pointers 
			// 
			ItemHdr1++; 
			DataPtr += vsize; 
		} 
 
		// And invoke the callback 
		// 
		STGMEDIUM stm; 
		FORMATETC fe; 
 
		fe.cfFormat = m_pILHEpipeviewDO->m_data;		// without time 
		fe.ptd = NULL; 
		fe.dwAspect = DVASPECT_CONTENT; 
		fe.lindex = -1; 
		fe.tymed = TYMED_HGLOBAL; 
 
		stm.tymed = TYMED_HGLOBAL; 
		stm.hGlobal = gh; 
		stm.pUnkForRelease = NULL; 
 
		m_pILHEpipeviewDO->m_dataCallback->OnDataChange(&fe, &stm);	// without time 
 
		// And free the storage 
		// 
		GlobalFree(gh); 
 
} 
/////////////////////////////////////// 
// LHEpipeviewGroup::SendWriteStream 
// 
// This function is used to build the stream for OnWriteComplete 
// 
// Create the Stream which consists of: 
//  GroupHeader 
//  ItemHeaders 
// 
void		LHEpipeviewGroup::SendWriteStream(int Count, OPCHANDLE *ItemHandleList, DWORD tid) 
{ 
		int i, j, HdrSize, TotalSize; 
 
		// Allocate the Data Stream 
		// 
		HdrSize = sizeof(OPCGROUPHEADERWRITE) + Count * sizeof(OPCITEMHEADERWRITE); 
		TotalSize = HdrSize ; 
 
		char * gp; 
		HGLOBAL gh; 
 
		gh = GlobalAlloc( GMEM_FIXED, TotalSize ); 
		if(gh == NULL) 
		{ 
			// If no memory, For now we just give up and return; 
			// But a better error handler would be nice 
			// 
			return; 
		} 
		gp = (char*)GlobalLock(gh); 
 
		OPCGROUPHEADERWRITE * GrpPtr; 
		OPCITEMHEADERWRITE * ItemHdr1; 
 
		GrpPtr = (OPCGROUPHEADERWRITE *) gp; 
		ItemHdr1 = (OPCITEMHEADERWRITE *) (gp + sizeof(OPCGROUPHEADERWRITE)); 
 
		// Fill in the Group header 
		// 
		GrpPtr->dwItemCount = Count; 
		GrpPtr->hClientGroup =	m_ClientGroupHandle; 
		GrpPtr->hrStatus = S_OK; 
		GrpPtr->dwTransactionID = tid; 
 
		// Now fill in the Item Information 
		// 
 
		for(j=0; jCheckDeviceWrite( &ItemHdr1->dwError); 
			ItemHdr1->hClient = ItemPtr(i)->GetHandle(); 
 
			// The update the pointers 
			// 
			ItemHdr1++; 
		} 
 
		// And invoke the callback 
		// 
		STGMEDIUM stm; 
		FORMATETC fe; 
 
		fe.cfFormat = m_pILHEpipeviewDO->m_write; 
		fe.ptd = NULL; 
		fe.dwAspect = DVASPECT_CONTENT; 
		fe.lindex = -1; 
		fe.tymed = TYMED_HGLOBAL; 
 
		stm.tymed = TYMED_HGLOBAL; 
		stm.hGlobal = gh; 
		stm.pUnkForRelease = NULL; 
 
		m_pILHEpipeviewDO->m_writeCallback->OnDataChange(&fe, &stm); 
 
		// And free the storage 
		// 
		GlobalFree(gh); 
} 
 
/////////////////////////////////////// 
// LHEpipeviewGroup::ComputeSize 
// 
// This function is used to compute the size of the HGLOBAL 
// (a.k.a. 'the stream') 
// needed to contain the OnDataChange info for IAdviseSink 
// 
long LHEpipeviewGroup::ComputeSize(int Count, OPCHANDLE *List) 
{ 
	int i, DataSize = 0L; 
 
	for(i=0; iGetValue(OPC_DS_CACHE, &vtemp, 0,0); 
		DataSize += OPCVariantSize(&vtemp); 
		VariantClear(&vtemp); 
	} 
	return DataSize; 
}