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


// item.cpp 
// 
//  This file contains the implementation of 
//  an LHEpipeview Item Object for the OPC server. 
// 
//  Note that there is nothing in the Custom interface definition 
//  that actually requires the creation of a COM based Item Object. 
//  However doing so is consistant with the approach taken for the 
//  vendor specific Server and Group objects and, 
//  although not required, is certainly a reasonable approach. 
//  In addition it will provide a convenient place to attach 
//  the Item Automation interface. 
// 
//  As always there are many ways to address the implementation and 
//  many tradeoffs in complexity vs clarity vs speed. 
//  Please note that the intent of THIS code is to emphasize clarity 
//  over everthing else. 
// 
// 
//	(c) COPYRIGHT 1996,1997 INTELLUTION INC. 
// ALL RIGHTS RESERVED 
// 
// Original Author: Al Chisholm 
// 
// Modification Log: 
//	Vers    Date   By    Notes 
//	----  -------- ---   ----- 
//	0.00  12/18/96 ACC 
// 0.02  03/01/97 acc   fix constructor to init item&accesspath ptrs 
//                       add support for Variant Conversions 
//                       other minor reorganizations for clarity 
//                       make the data change on each read for better demos 
// 0.90  04/08/97 ACC   add async logic 
//                       and simulate function 
//                       Fix SetDataType to store vtRequested 
// 
 
#define WIN32_LEAN_AND_MEAN 
 
#include "OPCLHEpipeview.h" 
#include "OPCerror.h" 
 
#include  
 
 
///////////////////////////////////////////////////////////////////////////// 
// Constructor /Destructor functions 
// 
 
/////////////////////////////////////// 
// LHEpipeviewItem() 
//   Constructor for the Sample Item. 
// 
/////////////////////////////////////// 
LHEpipeviewItem::LHEpipeviewItem( LHEpipeviewGroup *parent) 
{ 
	mRefCount = 0; 
 
	// Record Parent Group 
	// 
	m_Parent = parent; 
 
	// init pointers 
	// 
	m_szItemID = NULL;		//acc002 
	m_szAccessPath = NULL; 
 
	// Default state is to generate simulated data 
	m_GenData = 1; 
	m_LastWriteError = S_OK; 
	m_AsyncMask = 0; 
} 
 
 
 
/////////////////////////////////////// 
// ~LHEpipeviewItem() 
//   Destructor for the OPC Sample Item. 
// 
/////////////////////////////////////// 
LHEpipeviewItem::~LHEpipeviewItem( void) 
{ 
	// Free memory associated with the Item 
	// 
	if(m_szItemID) delete [] m_szItemID; 
	if(m_szAccessPath) delete [] m_szAccessPath; 
} 
 
 
///////////////////////////////////////////////////////////////////////////// 
// IUnknown functions 
// 
 
/////////////////////////////////////// 
// IUnknown::AddRef() 
//   Standard IUnknown implementation 
// 
/////////////////////////////////////// 
STDMETHODIMP_(ULONG) LHEpipeviewItem::AddRef( void) 
{ 
	return ++mRefCount; 
} 
 
 
 
/////////////////////////////////////// 
// IUnknown::Release() 
//   Standard IUnknown implementation 
// 
/////////////////////////////////////// 
STDMETHODIMP_(ULONG) LHEpipeviewItem::Release( void) 
{ 
	ULONG currentCount = --mRefCount; 
 
	// If no references left for this Item 
	if ( currentCount == 0) 
	{ 
		// Then delete this Item. 
		delete this; 
	} 
	return currentCount; 
} 
 
 
/////////////////////////////////////// 
// IUnknown::QueryInterface() 
//   Standard IUnknown implementation 
// 
/////////////////////////////////////// 
STDMETHODIMP LHEpipeviewItem::QueryInterface( REFIID iid, LPVOID* ppInterface) 
{ 
	if ( ppInterface == NULL) 
		return E_INVALIDARG; 
 
	if ( iid == IID_IUnknown ) 
		*ppInterface = (IUnknown*) this; 
 
	else 
	{ 
		*ppInterface = NULL; 
	} 
 
	if ( *ppInterface == NULL) 
		return E_NOINTERFACE; 
 
	AddRef(); 
	return S_OK; 
} 
 
 
///////////////////////////////////////////////////////////////////////////// 
// Member functions 
// 
 
/////////////////////////////////////// 
// Init 
// Initialize the ITEM per the OPCITEMDEF		acc002 
// This is the least 'real' function in this sample server 
// 
// Normally this logic would 'connect' the LHEpipeviewItem to a value 
// in the 'real' data source; PLC, DCS, etc 
// If the specified ItemID did not exist in the 'real' source 
// we would return an error. 
// 
// However this sample code just accepts any ItemID 
// and creates some 'dummy' data for it. 
// 
/////////////////////////////////////// 
HRESULT LHEpipeviewItem::Init( int j, OPCITEMDEF *def, OPCITEMRESULT *ir) 
{ 
	HRESULT hr; 
	SYSTEMTIME SystemTime; 
 
	// Initialize some dummy values for testing		acc002 
	// 
	m_vtCanonical = VT_R4; 
	m_Value = (float) 0.0; 
	m_SimValue = (float) 0.0 + (FLOAT) j; 
	m_Quality = 0xC0;					// Quality = 'Good' (11000000) 
	GetSystemTime(&SystemTime);		// Get current UTC Time 
	SystemTimeToFileTime(&SystemTime, &m_TimeStamp); // and store it 
 
	// Now make sure requested data type is OK 
	// 
	hr = SetDatatype(def->vtRequestedDataType);		//acc002 
	if(FAILED(hr)) return hr; 
 
	// If so then copy the ItemID and Accesspath 
	// Memory allocation errors are possible here and should be 
	// checked by a real server... 
	// 
	m_szItemID = WSTRClone(def->szItemID, NULL); 
	m_szAccessPath = WSTRClone(def->szAccessPath, NULL); 
 
	// Copy other Itemdef info to Item object 
	// (Note Blob stuff is not supported/required by this example) 
	// 
	m_hServerItem = j; 
	m_hClientItem = def->hClient; 
	m_bActive = def->bActive; 
 
	// Mark data as 'changed' for OnDataChange 
	// 
	MarkAsChanged(OPC_ODC_ANY); 
 
	// Set ITEM RESULT 
	// 
	ir->hServer = m_hServerItem; 
	ir->vtCanonicalDataType = m_vtCanonical; 
	ir->dwAccessRights = OPC_WRITEABLE | OPC_READABLE; 
	ir->pBlob = NULL; 
	ir->dwBlobSize = 0; 
 
	return S_OK; 
} 
 
/////////////////////////////////////// 
// SetActive 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::SetActive(BOOL a) 
{ 
	m_bActive = a; 
 
	// If going to active then also queue an OnDataChange 
	// else if going inactive then clear DataChange 
	// 
	if(m_bActive) MarkAsChanged(OPC_ODC_ANY); 
	else ClearChanged(OPC_ODC_ANY); 
} 
 
/////////////////////////////////////// 
// GetActive 
// 
/////////////////////////////////////// 
BOOL LHEpipeviewItem::GetActive(void) 
{ 
	return m_bActive; 
} 
 
/////////////////////////////////////// 
// SetHandle 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::SetHandle(OPCHANDLE h) 
{ 
	m_hClientItem = h; 
} 
 
/////////////////////////////////////// 
// GetHandle 
// 
/////////////////////////////////////// 
OPCHANDLE LHEpipeviewItem::GetHandle(void) 
{ 
	return m_hClientItem ; 
} 
 
/////////////////////////////////////// 
// SetDataType 
// 
/////////////////////////////////////// 
HRESULT LHEpipeviewItem::SetDatatype(VARTYPE vt) 
{ 
	// Per the OPC Spec" 
	// We should verify here that the requested type 
	// is compatible with the canonical type 
	// (e.g. by making a copy of the value and trying 'VariantChangeType'). 
	// 
	// However this part of the sample code is not complete at this time. 
	// 
	m_vtRequested = vt; 
	return S_OK; 
} 
 
/////////////////////////////////////// 
// GetDataType 
// 
/////////////////////////////////////// 
VARTYPE LHEpipeviewItem::GetDatatype(void) 
{ 
	return m_vtRequested; 
} 
 
 
/////////////////////////////////////// 
// Clone and item 
// (this is similar to a copy constructor) 
// 
/////////////////////////////////////// 
LHEpipeviewItem * LHEpipeviewItem::Clone(LHEpipeviewGroup * newparent, OPCHANDLE newserveritemhandle) 
{ 
	LHEpipeviewItem * item; 
	item = new LHEpipeviewItem(newparent); 
 
	item->m_hServerItem = newserveritemhandle; 
 
	item->m_szItemID = WSTRClone(m_szItemID, NULL); 
	item->m_szAccessPath = WSTRClone(m_szAccessPath, NULL); 
	item->m_hClientItem = m_hClientItem; 
	item->m_vtRequested = m_vtRequested; 
	item->m_bActive = m_bActive; 
 
	return item; 
} 
 
 
/////////////////////////////////////// 
// Fill in an OPCITEMATTRIBUTE 
// This is similar to IAClone() in itemutil.cpp 
// Caller should delete contents of the IA 
// using IAFree() 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::IAGet(OPCITEMATTRIBUTES *newIA) 
{ 
	// Note Blob and EUInfo not supported at present 
 
	// 
	// the easy stuff... 
	// 
	newIA->bActive = m_bActive; 
	newIA->hServer = m_hServerItem; 
	newIA->hClient = m_hClientItem; 
	newIA->dwAccessRights = OPC_READABLE | OPC_WRITEABLE; 
	newIA->dwBlobSize = 0;	// not supported 
	newIA->pBlob = NULL; 
	newIA->vtRequestedDataType = m_vtRequested; 
	newIA->vtCanonicalDataType = m_vtCanonical; 
	newIA->dwEUType = OPC_NOENUM;		// not supported 
	newIA->vEUInfo.vt = VT_EMPTY; 
 
	// strings... 
	// Use local memory 
	// 
	newIA->szAccessPath = WSTRClone( m_szAccessPath, NULL); 
	newIA->szItemID = WSTRClone(m_szItemID, NULL); 
 
} 
 
 
 
/////////////////////////////////////// 
// Get Item Value 
// 
// 
/////////////////////////////////////// 
HRESULT LHEpipeviewItem::GetValue( OPCDATASOURCE ds, VARIANT * v, WORD *q, FILETIME*t) 
{ 
	HRESULT r1 = S_OK; 
 
	// First handle Quality 
	// 
	if(q) switch(ds) 
	{ 
	case OPC_DS_CACHE: 
		if(m_bActive) *q = m_Quality; 
		else *q = QUAL_NOTACTIVE; 
		break; 
 
	case OPC_DS_DEVICE: 
		*q = m_Quality; 
		break; 
	} 
 
	// Then Timestamp 
	// 
	if(t) *t = m_TimeStamp; 
 
	// Then return the data 
	// 
	if(v) 
	{ 
		double temp; 
  		temp = m_Value; 
 
		// First set the target to the canonical type (VT_R4) 
		// If caller has requested a conversion 
		//	Then try to convert to requested type (this should succeed). 
		// else 
		//	just return the canonical type 
		// 
 
		v->vt = m_vtCanonical;		// note passed variant is known to be empty. 
		v->fltVal= (float) temp; 
		r1 = S_OK; 
		if(m_vtRequested != VT_EMPTY) 
		{ 
			// Convert to requested type 
			// and check error 
			// 
			r1 = VariantChangeType(v, v, 0, m_vtRequested); 
			if(FAILED(r1)) 
			{ 
				if(q) *q = 0;		// BAD 
			} 
		} 
	} 
 
	return r1; 
} 
 
 
/////////////////////////////////////// 
// Set Item Value 
// 
// 
/////////////////////////////////////// 
HRESULT LHEpipeviewItem::SetValue(VARIANT * v) 
{ 
	HRESULT r1; 
 
	// First change the data to the canonical type (VT_R4) 
	// 
	r1 = VariantChangeType(v, v, 0, m_vtCanonical); 
	if(FAILED(r1)) return r1; 
 
	// Store the new value into the data item value. 
	// A real server would need to trigger some sort 
	// of write to the hardware. 
	// 
	m_Value = v->fltVal; 
 
	// On Write, this sample code marks the data as changed 
	// for any OnDataChange operations. 
	// 
	MarkAsChanged(OPC_ODC_ANY); 
 
	// If we ever WRITE to this value - stop generating simulated data 
	// 
	m_GenData = 0; 
 
	// This TimeStamp update on 'write' is for the sample code only. 
	// A real server would update the time when the value 
	// was read from the real hardware device 
	// 
	SYSTEMTIME SystemTime; 
	GetSystemTime(&SystemTime);		// Get current UTC Time 
	SystemTimeToFileTime(&SystemTime, &m_TimeStamp); // and store it 
 
	return S_OK; 
} 
 
 
/////////////////////////////////////// 
// Simulate Item Value read from device 
// 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::Simulate( void ) 
{ 
	double temp; 
#define pi 3.14159 
	if(m_GenData & m_bActive) 
	{ 
		// Generate some interesting data that increments over time... 
		// 
		m_SimValue += (float)(pi/60.0); 
		if(m_SimValue > (2.0 * pi)) m_SimValue = (float)(0.0); 
		temp = sin(m_SimValue); 
		m_Value = (float)((temp + 1.0) * 50.0);	// scale = 0.0 to 100.0 
 
		// Mark the data as changed for any OnDataChange operations. 
		// A real server would set this on any change to 
		// Value or Quality. 
		// 
		MarkAsChanged(OPC_ODC_ANY); 
	} 
} 
 
/////////////////////////////////////// 
// Queue an Async Read 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::QueDeviceRead( short mask ) 
{ 
	// For this sample server nothing is actually required here 
	// A real server might initiate some sort of transaction 
	// with the hardware device. 
	// 
} 
 
/////////////////////////////////////// 
// Check Async Read 
// return 1 if complete 
// 
/////////////////////////////////////// 
BOOL LHEpipeviewItem::CheckDeviceRead( HRESULT *r ) 
{ 
	// For this sample server read completes instantly 
	// 
	if(r) *r = S_OK; 
	return 1; 
} 
 
/////////////////////////////////////// 
// Queue an Async Write 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::QueDeviceWrite( VARIANT *v ) 
{ 
	m_LastWriteError = SetValue(v); 
} 
 
/////////////////////////////////////// 
// Check Async Write compete 
// return 1 is complete 
// 
/////////////////////////////////////// 
BOOL LHEpipeviewItem::CheckDeviceWrite( HRESULT *r ) 
{ 
	// For this sample server Write completes instantly 
	// 
	if(r) *r = m_LastWriteError; 
	return 1; 
} 
 
/////////////////////////////////////// 
// Mark cached data as changed 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::MarkAsChanged( WORD flg ) 
{ 
	// If item is active then mark it for callback 
	// 
	if(m_bActive) m_AsyncMask |= flg; 
} 
 
/////////////////////////////////////// 
// Determine if cached item has changed 
// 
/////////////////////////////////////// 
BOOL LHEpipeviewItem::Changed( WORD flg) 
{ 
	if(m_bActive && (m_AsyncMask & flg)) return 1; 
	return 0; 
} 
 
/////////////////////////////////////// 
// Clear cached item changed 
// 
/////////////////////////////////////// 
void LHEpipeviewItem::ClearChanged( WORD flg ) 
{ 
	m_AsyncMask &= ~flg; 
}