565 lines
14 KiB
C++
565 lines
14 KiB
C++
|
//
|
||
|
// MODULE: BN.cpp
|
||
|
//
|
||
|
// PURPOSE: implementation of the CBeliefNetwork class
|
||
|
//
|
||
|
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
|
||
|
//
|
||
|
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
|
||
|
//
|
||
|
// AUTHOR: Joe Mabel
|
||
|
//
|
||
|
// ORIGINAL DATE: 8-31-98
|
||
|
//
|
||
|
// NOTES:
|
||
|
// 1. Based on old apgtsdtg.cpp
|
||
|
// 2. all methods (except constructor/destructor) must LOCKOBJECT around code that uses BNTS.
|
||
|
// BNTS has "state". These functions are all written so that they make no assumptions about
|
||
|
// state on entry, presenting the calling class with a stateless object.
|
||
|
// 3. In theory, we could have separate locking for the cache independent of locking
|
||
|
// CBeliefNetwork. The idea would be that if you needed only the cache to get your
|
||
|
// inference, you wouldn't have to wait for access to BNTS.
|
||
|
// >>>(ignore for V3.0) This is one of our best bets if performance is not good enough. JM 9/29/98
|
||
|
//
|
||
|
// Version Date By Comments
|
||
|
//---------------------------------------------------------------------
|
||
|
// V3.0 8-31-98 JM
|
||
|
//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "propnames.h"
|
||
|
#include "BN.h"
|
||
|
#include "CharConv.h"
|
||
|
#include "event.h"
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
#include "CHMFileReader.h"
|
||
|
#else
|
||
|
#include "fileread.h"
|
||
|
#endif
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// Construction/Destruction
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CBeliefNetwork::CBeliefNetwork(LPCTSTR path)
|
||
|
:
|
||
|
CDSCReader( CPhysicalFileReader::makeReader( path ) ),
|
||
|
m_bInitialized(false),
|
||
|
m_bSnifferIntegration(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CBeliefNetwork::~CBeliefNetwork()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CBeliefNetwork::Initialize()
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
if (! m_bInitialized)
|
||
|
{
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts)
|
||
|
{
|
||
|
m_bSnifferIntegration = false;
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////
|
||
|
// Does not matter for online TS (list is empty on initialization),
|
||
|
// but for local TS m_Cache can contain cache data read from file.
|
||
|
//m_Cache.Clear();
|
||
|
///////////////////////////////////////////////////////////////////
|
||
|
|
||
|
m_arrnidProblem.clear();
|
||
|
m_arrNodeTypeAll.clear();
|
||
|
|
||
|
// loop through nodes looking for problem nodes and build local problem node array
|
||
|
// also, determine if any node has a property which implies the intent of
|
||
|
// integrating with a sniffer.
|
||
|
int acnid= CNode();
|
||
|
for (NID anid=0; anid < acnid; anid++)
|
||
|
{
|
||
|
if (pbnts->BNodeSetCurrent(anid))
|
||
|
{
|
||
|
ESTDLBL albl = pbnts->ELblNode(); // type of node (information/problem/fixable etc)
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if (albl == ESTDLBL_problem)
|
||
|
m_arrnidProblem.push_back(anid);
|
||
|
m_arrNodeTypeAll.push_back(SNodeType(anid, albl));
|
||
|
}
|
||
|
catch (exception& x)
|
||
|
{
|
||
|
CString str;
|
||
|
// Note STL exception in event log.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
CCharConversion::ConvertACharToString(x.what(), str),
|
||
|
_T(""),
|
||
|
EV_GTS_STL_EXCEPTION );
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
LPCTSTR psz;
|
||
|
if (pbnts->BNodePropItemStr(H_NODE_DCT_STR, 0)
|
||
|
&& (psz = pbnts->SzcResult()) != NULL
|
||
|
&& *psz)
|
||
|
{
|
||
|
// There's a non-null property which only makes sense for a sniffer
|
||
|
// integration, so we assume that's what they've got in mind.
|
||
|
m_bSnifferIntegration = true;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
m_bInitialized = true;
|
||
|
}
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// Access the relevant BNTS
|
||
|
// Calling function should have a lock before calling this (although probably harmless
|
||
|
// is it doesn't!)
|
||
|
BNTS * CBeliefNetwork::pBNTS()
|
||
|
{
|
||
|
if (!IsRead())
|
||
|
return NULL;
|
||
|
return &m_Network;
|
||
|
};
|
||
|
|
||
|
// clear all node states
|
||
|
// We can't use BNTS::Clear() because that actually throws away the model itself.
|
||
|
void CBeliefNetwork::ResetNodes(const CBasisForInference & BasisForInference)
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts)
|
||
|
{
|
||
|
int cnid = BasisForInference.size();
|
||
|
|
||
|
// Set all node states to NIL in BNTS storage
|
||
|
for (UINT i = 0; i < cnid; i++)
|
||
|
{
|
||
|
pbnts->BNodeSetCurrent(BasisForInference[i].nid());
|
||
|
pbnts->BNodeSet(-1, false); // Nil value
|
||
|
}
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// Associate states with nodes.
|
||
|
// INPUT BasisForInference
|
||
|
// Note that all states must be valid states for the nodes, not (say) ST_UNKNOWN.
|
||
|
// Caller's responsibility.
|
||
|
bool CBeliefNetwork::SetNodes(const CBasisForInference & BasisForInference)
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
bool bOK = true;
|
||
|
if (pbnts)
|
||
|
{
|
||
|
int nNodes = BasisForInference.size();
|
||
|
for (int i= 0; i<nNodes; i++)
|
||
|
{
|
||
|
pbnts->BNodeSetCurrent(BasisForInference[i].nid());
|
||
|
if (!pbnts->BNodeSet(BasisForInference[i].state(), false))
|
||
|
bOK = false; // failed to set state. This should never happen on valid
|
||
|
// user query.
|
||
|
}
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return bOK;
|
||
|
}
|
||
|
|
||
|
// OUTPUT Recommendations: list of recommendations
|
||
|
// RETURN:
|
||
|
// RS_OK = SUCCESS. Note that Recommendations can return empty if there is nothing to recommend.
|
||
|
// RS_Impossible = Recommendations will return empty.
|
||
|
// RS_Broken = Recommendations will return empty.
|
||
|
int CBeliefNetwork::GetRecommendations(
|
||
|
const CBasisForInference & BasisForInference,
|
||
|
CRecommendations & Recommendations)
|
||
|
{
|
||
|
int ret = RS_OK;
|
||
|
|
||
|
LOCKOBJECT();
|
||
|
Initialize();
|
||
|
Recommendations.clear();
|
||
|
|
||
|
// see if we've already cached a result for this state of the world
|
||
|
if (m_Cache.FindCacheItem(BasisForInference, Recommendations))
|
||
|
{
|
||
|
// Great. We have a cache hit & return values have been filled in.
|
||
|
m_countCacheHit.Increment();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_countCacheMiss.Increment();
|
||
|
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts)
|
||
|
{
|
||
|
SetNodes(BasisForInference);
|
||
|
|
||
|
if (pbnts->BImpossible())
|
||
|
ret = RS_Impossible;
|
||
|
else if ( ! pbnts->BGetRecommendations())
|
||
|
ret = RS_Broken;
|
||
|
else
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
const int cnid = pbnts->CInt(); // Recommendation count
|
||
|
if (cnid > 0)
|
||
|
{
|
||
|
// At least one recommendation
|
||
|
const int *pInt = pbnts->RgInt();
|
||
|
for (int i=0; i<cnid; i++)
|
||
|
Recommendations.push_back(pInt[i]);
|
||
|
}
|
||
|
|
||
|
// We've got our return values together, but before we return, cache them.
|
||
|
m_Cache.AddCacheItem(BasisForInference, Recommendations);
|
||
|
}
|
||
|
catch (exception& x)
|
||
|
{
|
||
|
CString str;
|
||
|
// Note STL exception in event log.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
CCharConversion::ConvertACharToString(x.what(), str),
|
||
|
_T(""),
|
||
|
EV_GTS_STL_EXCEPTION );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ResetNodes(BasisForInference);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// return the number of nodes in the model
|
||
|
int CBeliefNetwork::CNode ()
|
||
|
{
|
||
|
int ret = 0;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts)
|
||
|
ret = pbnts->CNode();
|
||
|
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// Return the index of a node given its symbolic name
|
||
|
int CBeliefNetwork::INode (LPCTSTR szNodeName)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts)
|
||
|
ret = pbnts->INode(szNodeName);
|
||
|
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// OUTPUT *parrnid - refernce to array of NIDs of all problem nodes
|
||
|
// RETURN number of values in *parrnid
|
||
|
int CBeliefNetwork::GetProblemArray(vector<NID>* &parrnid)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
LOCKOBJECT();
|
||
|
Initialize();
|
||
|
parrnid = &m_arrnidProblem;
|
||
|
ret = m_arrnidProblem.size();
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// OUTPUT arrOut - refernce to array of NIDs of all nodes, that have type listed in arrTypeInclude
|
||
|
// RETURN number of values in arrOut
|
||
|
int CBeliefNetwork::GetNodeArrayIncludeType(vector<NID>& arrOut, const vector<ESTDLBL>& arrTypeInclude)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
LOCKOBJECT();
|
||
|
arrOut.clear();
|
||
|
Initialize();
|
||
|
for (vector<SNodeType>::iterator i = m_arrNodeTypeAll.begin(); i < m_arrNodeTypeAll.end(); i++)
|
||
|
{
|
||
|
for (vector<ESTDLBL>::const_iterator j = arrTypeInclude.begin(); j < arrTypeInclude.end(); j++)
|
||
|
if (i->Type == *j)
|
||
|
break;
|
||
|
|
||
|
if (j != arrTypeInclude.end())
|
||
|
arrOut.push_back(i->Nid);
|
||
|
}
|
||
|
ret = arrOut.size();
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// OUTPUT arrOut - refernce to array of NIDs of all nodes, that do NOT have type listed in arrTypeExclude
|
||
|
// RETURN number of values in arrOut
|
||
|
int CBeliefNetwork::GetNodeArrayExcludeType(vector<NID>& arrOut, const vector<ESTDLBL>& arrTypeExclude)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
LOCKOBJECT();
|
||
|
arrOut.clear();
|
||
|
Initialize();
|
||
|
for (vector<SNodeType>::iterator i = m_arrNodeTypeAll.begin(); i < m_arrNodeTypeAll.end(); i++)
|
||
|
{
|
||
|
for (vector<ESTDLBL>::const_iterator j = arrTypeExclude.begin(); j < arrTypeExclude.end(); j++)
|
||
|
if (i->Type == *j)
|
||
|
break;
|
||
|
|
||
|
if (j == arrTypeExclude.end())
|
||
|
arrOut.push_back(i->Nid);
|
||
|
}
|
||
|
ret = arrOut.size();
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------
|
||
|
// simple properties
|
||
|
// ----------------------------------------
|
||
|
|
||
|
// return a STRING property of the net
|
||
|
CString CBeliefNetwork::GetNetPropItemStr(LPCTSTR szPropName)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (!pbnts)
|
||
|
return CString(_T(""));
|
||
|
|
||
|
if (pbnts->BNetPropItemStr(szPropName, 0))
|
||
|
strRet = pbnts->SzcResult();
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
// return a REAL property of the net
|
||
|
bool CBeliefNetwork::GetNetPropItemNum(LPCTSTR szPropName, double& numOut)
|
||
|
{
|
||
|
bool bRet = false;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (!pbnts)
|
||
|
return false;
|
||
|
|
||
|
bRet = pbnts->BNetPropItemReal(szPropName, 0, numOut) ? true : false;
|
||
|
UNLOCKOBJECT();
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
// return a STRING property of a node or state
|
||
|
// For most properties, state is irrelevant, and default of 0 is the appropriate input.
|
||
|
// However, if there are per-state values, passing in the appropriate state number
|
||
|
// will get you the appropriate value.
|
||
|
CString CBeliefNetwork::GetNodePropItemStr(NID nid, LPCTSTR szPropName, IST state /*= 0 */)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
if (pbnts->BNodePropItemStr(szPropName, state))
|
||
|
strRet = pbnts->SzcResult();
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
// $MAINT - This function is not currently used in any of the troubleshooters. RAB-19991103.
|
||
|
// return a REAL property of a node or state
|
||
|
// For most properties, state is irrelevant, and default of 0 is the appropriate input.
|
||
|
// However, if there are per-state values, passing in the appropriate state number
|
||
|
// will get you the appropriate value.
|
||
|
bool CBeliefNetwork::GetNodePropItemNum(NID nid, LPCTSTR szPropName, double& numOut, IST state /*= 0*/)
|
||
|
{
|
||
|
bool bRet = false;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
bRet = pbnts->BNodePropItemReal(szPropName, state, numOut) ? true : false;
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
CString CBeliefNetwork::GetNodeSymName(NID nid)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
pbnts->NodeSymName();
|
||
|
strRet = pbnts->SzcResult();
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
CString CBeliefNetwork::GetNodeFullName(NID nid)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
pbnts->NodeFullName();
|
||
|
strRet = pbnts->SzcResult();
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
CString CBeliefNetwork::GetStateName(NID nid, IST state)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
pbnts->NodeStateName(state);
|
||
|
strRet = pbnts->SzcResult();
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ----------------------------------------
|
||
|
// "multiline" properties
|
||
|
// these date back to when there was a 255-byte limit on STRING and longer strings
|
||
|
// had to be represented by ARRAY OF STRING, later concatenated.
|
||
|
// Backward compatibility still needed.
|
||
|
// ----------------------------------------
|
||
|
|
||
|
// Append a NET property (for Belief Network as a whole, not for one
|
||
|
// particular node) to str.
|
||
|
// INPUT szPropName - Property name
|
||
|
// INPUT szFormat - string to format each successive line. Should contain one %s, otherwise
|
||
|
// constant text.
|
||
|
CString CBeliefNetwork::GetMultilineNetProp(LPCTSTR szPropName, LPCTSTR szFormat)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts)
|
||
|
{
|
||
|
CString strTxt;
|
||
|
|
||
|
for (int i = 0; pbnts->BNetPropItemStr(szPropName, i); i++)
|
||
|
{
|
||
|
strTxt.Format( szFormat, pbnts->SzcResult());
|
||
|
strRet += strTxt;
|
||
|
}
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
// Like GetMultilineNetProp, but for a NODE property item, for one particular node.
|
||
|
// INPUT/OUTPUT str - string to append to
|
||
|
// INPUT item - Property name
|
||
|
// INPUT szFormat - string to format each successive line. Should contain one %s, otherwise
|
||
|
// constant text.
|
||
|
CString CBeliefNetwork::GetMultilineNodeProp(NID nid, LPCTSTR szPropName, LPCTSTR szFormat)
|
||
|
{
|
||
|
CString strRet;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
CString strTxt;
|
||
|
|
||
|
for (int i = 0; pbnts->BNodePropItemStr(szPropName, i); i++)
|
||
|
{
|
||
|
strTxt.Format( szFormat, pbnts->SzcResult());
|
||
|
strRet += strTxt;
|
||
|
}
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return strRet;
|
||
|
}
|
||
|
|
||
|
int CBeliefNetwork::GetCountOfStates(NID nid)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
ret = pbnts->INodeCst();
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// returns true only for NIDs valid in the context of an abstract belief network.
|
||
|
// Doesn't know about troubleshooter-specific stuff like nidService.
|
||
|
bool CBeliefNetwork::IsValidNID(NID nid)
|
||
|
{
|
||
|
return ( nid < CNode() );
|
||
|
}
|
||
|
|
||
|
bool CBeliefNetwork::IsCauseNode(NID nid)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
ESTDLBL lbl = pbnts->ELblNode();
|
||
|
ret= (lbl == ESTDLBL_fixobs || lbl == ESTDLBL_fixunobs || lbl == ESTDLBL_unfix);
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool CBeliefNetwork::IsProblemNode(NID nid)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
ret= (pbnts->ELblNode() == ESTDLBL_problem);
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool CBeliefNetwork::IsInformationalNode(NID nid)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
LOCKOBJECT();
|
||
|
BNTS * pbnts = pBNTS();
|
||
|
if (pbnts && pbnts->BNodeSetCurrent(nid))
|
||
|
{
|
||
|
ret= (pbnts->ELblNode() == ESTDLBL_info);
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool CBeliefNetwork::UsesSniffer()
|
||
|
{
|
||
|
bool ret = false;
|
||
|
LOCKOBJECT();
|
||
|
Initialize();
|
||
|
ret = m_bSnifferIntegration;
|
||
|
UNLOCKOBJECT();
|
||
|
return ret;
|
||
|
}
|