📄 opcdrvitem.cpp
字号:
//
////////////////////////////////////////////////////////////////
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 + -