⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 opcdrvitem.cpp

📁 基于Intellution开发包的开发的OPC服务器
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
////////////////////////////////////////////////////////////////
HRESULT COPCDrvItem::WriteValue(VARIANT *pvWriteValue)
{
	HRESULT		hr				= S_OK;

	IOSTAT		IoStat;
	
	int			nNumElements	= 1;


	Lock();

	// Initialize the local data
	//
    memset(&IoStat, 0, sizeof(IOSTAT));

	// Do the write...
	//
	nio_sendEx(&IoStat, &m_IovSpec, &m_EguRec, &m_wQuality, nNumElements,
			   pvWriteValue, m_vtRequested);

	if (FE_OK != IoStat.iostatus)
	{
		hr = E_FAIL;
	}

	UnLock();
	return hr;
}


////////////////////////////////////////////////////////////////
// ReadValue()
//
// Read a value from NIO
//
// Returns:
//	void
//
////////////////////////////////////////////////////////////////
void COPCDrvItem::ReadValue(void)
{
	Lock();

	// Do the read...
	//
	nio_readEx(&m_IoStat, &m_IovSpec, &m_EguRec, &m_ftLastReadTime, &m_wQuality,
			   m_wNumElements, &m_vReturnedData, m_vtRequested);

	// Check for the status not OK or any alarms
	//
	if ((((QUALITY_STATE *)&m_wQuality)->nQuality == QUALITY_BAD) || (m_IoStat.iostatus))
	{
		UnLock();
		return;
	}

	// Save the canonical type
	//
	m_vtCanonical = m_vReturnedData.vt;

	// We have done a successful read, so set the flag to indicate that
	// we have a last known value, in the event of a COMM failure or something.
	//
	m_bLastKnown = TRUE;

	UnLock();
}


////////////////////////////////////////////////////////////////
// UpdateDataCache()
//
// Item Value read from device.
//
// This function is basically a router to nio_readEx().
// It will call the appropriate read function and update the
// Item's data cache containing the data read by the client.
// This function makes up the end of the "polling engine"
// used to keep the server updated with the values from the
// hardware.
//
////////////////////////////////////////////////////////////////
void COPCDrvItem::UpdateDataCache(void)
{
	VARIANT		vData;

	
	WORD		wTmpQuality		= 0;
	
	short		nTmpIoStatus	= 0;


	// Initialize the local variables
	//
	VariantInit(&vData);

	Lock();

	// Save away the current data state
	//
	wTmpQuality		= this->m_wQuality;
	nTmpIoStatus	= this->m_IoStat.iostatus;

	// Do the read...
	//
	nio_readEx(&m_IoStat, &m_IovSpec, &m_EguRec, &m_ftLastReadTime, 
				  &m_wQuality, m_wNumElements, &vData, m_vtRequested);

	// See if anything changed. The client only wants to be updated
	// when data has changed beyond the deadband. This is used when
	// the client has Advised with the server.
	//
	if (DidDataChange(vData, wTmpQuality, nTmpIoStatus))
	{
		HRESULT	hr;

		m_bLastKnown = TRUE;

		// Save the data away
		//
		m_vtCanonical = vData.vt;
		VariantClear(&m_vReturnedData);
		hr = VariantCopy(&m_vReturnedData, &vData);
		if (FAILED(hr))
		{
			m_bLastKnown = FALSE;
			m_wQuality = WORD_QUALITY_BAD;
			TRACE("VariantCopy() failed (%x), File %s, Line %d\n", 
				hr, __FILE__, __LINE__);
		}

		// Mark the data as changed, so we will inform the client
		// through the IAdviseSink.
		//
		MarkAsChanged(OPC_ODC_ANY);
	}

	UnLock();

	// Clear any memory allocated by the temp variant
	VariantClear(&vData);
}


////////////////////////////////////////////////////////////////
// CheckDeviceRead()
//
// Check Async Read
//
// Return TRUE if complete
// Return FALSE if not complete.
//
// Note:	This server implements async reads immediately, so we
//			always return TRUE here.
//
////////////////////////////////////////////////////////////////
BOOL COPCDrvItem::CheckDeviceRead(HRESULT *hr)
{
	if (hr)
	{
		*hr = S_OK;
	}

	return TRUE;
}


////////////////////////////////////////////////////////////////
// CheckDeviceWrite()
//
// Check Async Write compete
//
// Return TRUE if complete
// Return FALSE if not complete.
//
// Note:	This server implements async writes immediately, so we
//			always return TRUE here.
//
////////////////////////////////////////////////////////////////
BOOL COPCDrvItem::CheckDeviceWrite(HRESULT *hr)
{
	if (hr) 
	{
		*hr = S_OK;
	}

	return TRUE;
}


////////////////////////////////////////////////////////////////
// MarkAsChanged()
//
// Mark cached data as changed
//
////////////////////////////////////////////////////////////////
void COPCDrvItem::MarkAsChanged(WORD wFlag)
{
	// If item is active then mark it for callback
	//
	if (m_bActive)
	{
		m_AsyncMask |= wFlag; 
	}
}


////////////////////////////////////////////////////////////////
// Changed()
//
// Determine if cached item has changed
//
////////////////////////////////////////////////////////////////
BOOL COPCDrvItem::Changed(WORD wFlag)
{
	if ((m_bActive) && (m_AsyncMask & wFlag)) 
	{
		return TRUE; 
	}

	return FALSE;
}


////////////////////////////////////////////////////////////////
// ClearChanged()
//
// Clear cached item changed
//
////////////////////////////////////////////////////////////////
void COPCDrvItem::ClearChanged(WORD wFlag)
{
	m_AsyncMask &= ~wFlag;
}


////////////////////////////////////////////////////////////////
// DidDataChange()
//
// Determines if the new data has changed. This is
// used for asynchronous reads.
//
// Returns TRUE if the data has changed by the deadband.
// Returns FALSE if it has not.
//
////////////////////////////////////////////////////////////////
BOOL COPCDrvItem::DidDataChange(VARIANT			&vNewData,
								WORD			wOldQuality,
								short			nOldIoStatus)
{
	// If we don't have a last known value update it regardless. This is needed
	// because on startup, the internal cache value is initialized to 0, so if the
	// deadband is 10 and an initial value of 9 is read, the data won't be updated.
	// Also, if we are reading strings, there won't be any valid data in the bstrVal
	// for the internal data cache. This will take care of both problems.
	//
	if (FALSE == this->m_bLastKnown)
	{
		return TRUE;
	}

	// If the VARTYPEs are different for the new data and the 
	// current data, then the data is clearly dirrent, so return TRUE.
	//
	if (vNewData.vt != m_vReturnedData.vt)
	{
		return TRUE;
	}

	switch(vNewData.vt)
	{
	default:
	case VT_R4:
		{
			float	fPercentDeadBand	= m_pParentGroup->m_Deadband / 100;
			float	fSpan				= m_EguRec.urec.arec.span;

			// If the percent deadband is 0, then basically we should
			// do a callback only when the new data is not equal to the old.
			//
			if (0 == fPercentDeadBand)
			{
				if (m_vReturnedData.fltVal != vNewData.fltVal)
				{
					return TRUE;
				}
				break;
			}

			// The formula for an exception is as follows (per the OPC spec):
			//	if (AbsoluteVal(LastCachedVal - CurrentVal) > %DeadBand * (HighEGU - LoEGU))
			//
			if (fabs(m_vReturnedData.fltVal - vNewData.fltVal) >= fPercentDeadBand * (fSpan))
			{
				return TRUE;
			}
			break;
		}

	case VT_BOOL:
		if (m_vReturnedData.boolVal != vNewData.boolVal)
		{
			return TRUE;
		}
		break;

	case VT_BSTR:
		{
			int nLength = wcslen(vNewData.bstrVal);

			// NULL out the new data just in case
			//
			*(vNewData.bstrVal + nLength) = NULL;
			if (wcscmp(vNewData.bstrVal, m_vReturnedData.bstrVal) != 0)
			{
				return TRUE;
			}
			break;
		}
	}

	// If the data quality has changed, then we want to inform the user
	//
	if (wOldQuality != this->m_wQuality)
	{
		return TRUE;
	}

	// If the IoStatus has changed, then we want to inform the user
	//
	if (nOldIoStatus != this->m_IoStat.iostatus)
	{
		return TRUE;
	}

	return FALSE;
}


////////////////////////////////////////////////////////////////
// GetDataSize()
//
// Simply fills a VARIANT with the data. This is used only 
// by ComputeSize() in the Group object. Therefore, not error
// checking is done to make sure the VariantChangeType() call
// succeeds.
//
// Returns: The size of the data stored in the VARIANT
//
////////////////////////////////////////////////////////////////
int COPCDrvItem::GetDataSize(void)
{
	return OPCVariantSize(&m_vReturnedData);
}


////////////////////////////////////////////////////////////////
//
// The following utility functions are used to simplify the
// parsing out of the Item ID.
//
////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////
// ParseItemIDOptions()
//
// Parses out the SIGNAL,LOEGU,HIEGU,HARDWAREOPTIONS portion
// of the ItemID
//
// Returns:
//		HRESULT	-	S_OK if all fields parsed
//				-	S_FALSE if some fields not entered and 
//					defaults are used.
//
////////////////////////////////////////////////////////////////
HRESULT	COPCDrvItem::ParseItemIDOptions(CString			strTmpID,				/* IN  */
										CString			&strSignalCond,			/* OUT */
										CString			&strLowEGU,				/* OUT */
										CString			&strHighEGU,			/* OUT */
										CString			&strHardwareOptions)	/* OUT */
{
	int		nIndex = 0;


	if (strTmpID.IsEmpty())
	{
		strSignalCond		= g_strDefaultSignalConditioning;
		strLowEGU			= g_strDefaultLoEGU;
		strHighEGU			= g_strDefaultHiEGU;
		strHardwareOptions	= g_strDefaultHardwareOptions;
		return S_FALSE;
	}

	//
	// Get the signal conditioning
	//

	if((nIndex = strTmpID.FindOneOf(g_psSignalCondParameterSeperator)) == -1)
	{
		strSignalCond		= strTmpID;
		strLowEGU			= g_strDefaultLoEGU;
		strHighEGU			= g_strDefaultHiEGU;
		strHardwareOptions	= g_strDefaultHardwareOptions;
		return S_FALSE;
	}
	strSignalCond = strTmpID.Left(nIndex);

	// If the string is empty, then set a default of 0
	if (strSignalCond.IsEmpty())
	{
		strSignalCond = g_strDefaultSignalConditioning;
	}


	//
	// Get the Low EGU
	//

	strTmpID = strTmpID.Mid(nIndex + 1);

	if((nIndex = strTmpID.FindOneOf(g_psSignalCondParameterSeperator)) == -1)
	{
		strLowEGU			= strTmpID;
		strHighEGU			= g_strDefaultHiEGU;
		strHardwareOptions	= g_strDefaultHardwareOptions;
		return S_FALSE;
	}
	strLowEGU = strTmpID.Left(nIndex);

	// If the string is empty, then set a default of 0
	if (strLowEGU.IsEmpty())
	{
		strLowEGU = g_strDefaultLoEGU;
	}


	//
	// Get the High EGU
	//

	strTmpID = strTmpID.Mid(nIndex + 1);

	if((nIndex = strTmpID.FindOneOf(g_psSignalCondParameterSeperator)) == -1)
	{
		strHighEGU			= strTmpID;
		strHardwareOptions	= g_strDefaultHardwareOptions;
		return S_FALSE;
	}
	strHighEGU = strTmpID.Left(nIndex);

	// If the string is empty, then set a default of 0
	if (strHighEGU.IsEmpty())
	{
		strHighEGU = g_strDefaultHiEGU;
	}


	//
	// Get the Hardware Options
	//

	if ((nIndex + 1) == strTmpID.GetLength())
	{
		strHardwareOptions = g_strDefaultHardwareOptions;
	}
	else
	{
		strHardwareOptions = strTmpID.Mid(nIndex + 1);
	}

	// We will read 1 register or bit
	m_wNumElements = 1;

	return S_OK;
}


////////////////////////////////////////////////////////////////
// GetNIOBlockType()
//
// Determines and saves the NIO block type dependant on the
// passed in Variant datatype
//
// Returns:
//		short	- The NIO block type. Either EGU_A	- analog
//											 EGU_D	- digital
//											 EGU_X	- ASCII
//
////////////////////////////////////////////////////////////////
short COPCDrvItem::GetNIOBlockType(VARTYPE	vtRequested)
{
	if (VT_BOOL == vtRequested)
	{
		// digital
		m_EguRec.type	= EGU_D;
		m_wNumElements	= 1;
		m_vtCanonical	= VT_BOOL;
	}
	else if (VT_BSTR == vtRequested)
	{
		// ASCII
		m_EguRec.type	= EGU_X;
		m_wNumElements	= g_wStringLength;
		m_vtCanonical	= VT_BSTR;
	}
	else
	{
		// analog
		m_EguRec.type	= EGU_A;
		m_wNumElements	= 1;
		m_vtCanonical	= VT_R4;
	}

	return (m_EguRec.type);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -