2402 lines
72 KiB
C++
2402 lines
72 KiB
C++
|
//
|
||
|
// MODULE: APGTSINF.CPP
|
||
|
//
|
||
|
// PURPOSE: Inference Engine Interface
|
||
|
// Completely implement class CInfer. VERY IMPORTANT STUFF!
|
||
|
// One of these is created for each user request
|
||
|
// Some utility functions at end of file.
|
||
|
//
|
||
|
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
|
||
|
//
|
||
|
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
|
||
|
//
|
||
|
// AUTHOR: Roman Mach, Joe Mabel
|
||
|
//
|
||
|
// ORIGINAL DATE: 8-2-96
|
||
|
//
|
||
|
// NOTES:
|
||
|
// 1. Many methods in this class could be const if BNTS had more appropriate use of const
|
||
|
// 2. Several places in this file you will see a space after %s in the format passed to
|
||
|
// CInfer::AppendMultilineNetProp() or CInfer::AppendMultilineNodeProp(). This is the
|
||
|
// upshot of some 12/98 correspondence between Microsoft and Saltmine. Many older DSC files
|
||
|
// were built with a tool that could not handle more than 255 characters in a string.
|
||
|
// The DSC feil format's "Array of string" was used to build up longer strings. Newer
|
||
|
// DSC files (and all Argon-produced DSC files) should use only the first element of this
|
||
|
// array.
|
||
|
// The older DSC files assumed that the separate strings would effectively be separated
|
||
|
// by white space, so we must maintain that situation.
|
||
|
// 3. >>> $MAINT - exception-handling strategy for push_back and other memory allocation
|
||
|
// functions is really overkill. If we run out of memory, we're screwed anyway. Really
|
||
|
// would suffice to handle try/catch just at the main function of the thread.
|
||
|
//
|
||
|
// Version Date By Comments
|
||
|
//--------------------------------------------------------------------
|
||
|
// V0.1 - RM Original
|
||
|
// V3.0 7-21-98 JM Major revision, deprecate IDH.
|
||
|
// 8-27-98 JM Totally new method of communicating with template
|
||
|
//
|
||
|
|
||
|
#pragma warning(disable:4786)
|
||
|
#include "stdafx.h"
|
||
|
#include "event.h"
|
||
|
#include "apgts.h"
|
||
|
#include "apgtsinf.h"
|
||
|
#include "apgtsmfc.h"
|
||
|
#include "apgtsassert.h"
|
||
|
#include "CharConv.h"
|
||
|
#include "maxbuf.h"
|
||
|
#include <algorithm>
|
||
|
#include <vector>
|
||
|
#include <map>
|
||
|
#include "Sniff.h"
|
||
|
#include "SniffController.h"
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
#include "SniffLocal.h"
|
||
|
#endif
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// Constructor/Destructor, other initialization
|
||
|
// -------------------------------------------------------------------
|
||
|
//
|
||
|
// INPUT *pCtxt is a buffer for building the string to pass back over the net.
|
||
|
CInfer::CInfer(CSniffConnector* pSniffConnector) :
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
m_pSniff(new CSniffLocal(pSniffConnector, NULL)),
|
||
|
#else
|
||
|
m_pSniff(NULL),
|
||
|
#endif
|
||
|
m_nidProblem(nidNil),
|
||
|
m_bDone(false),
|
||
|
m_bRecOK (false),
|
||
|
m_SniffedRecommendation(nidNil, SNIFF_FAILURE_RESULT),
|
||
|
m_bUseBackEndRedirection(false),
|
||
|
m_bRecycleSkippedNode(false),
|
||
|
m_nidRecycled(0),
|
||
|
m_bRecyclingInitialized(false),
|
||
|
m_nidSelected(nidNil),
|
||
|
m_bLastSniffedManually(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//
|
||
|
CInfer::~CInfer()
|
||
|
{
|
||
|
delete m_pSniff;
|
||
|
}
|
||
|
|
||
|
// The intention is that this be called only once.
|
||
|
// It would be ideal if this were part of the constructor, but the CTopic * is not
|
||
|
// yet available at time of construction.
|
||
|
// The expectation is that this should be called before calling any other function. (Some
|
||
|
// are technically OK to call, but it's smartest not to rely on that.)
|
||
|
void CInfer::SetTopic(CTopic *pTopic)
|
||
|
{
|
||
|
m_pTopic = pTopic;
|
||
|
if (m_pSniff)
|
||
|
m_pSniff->SetTopic(pTopic);
|
||
|
}
|
||
|
|
||
|
// This fn exists so APGTSContext can access *m_pSniff to tell it what the sniffing
|
||
|
// policies are.
|
||
|
CSniff* CInfer::GetSniff()
|
||
|
{
|
||
|
return m_pSniff;
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// First, we set the states of nodes, based on the query string we got from the HTML form
|
||
|
// -------------------------------------------------------------------
|
||
|
|
||
|
// Convert IDH to NID. Needed on some old query string formats
|
||
|
// "Almost vestigial", still supported in v3.2, but will be dropped in v4.0.
|
||
|
NID CInfer::NIDFromIDH(IDH idh) const
|
||
|
{
|
||
|
if (idh == m_pTopic->CNode() + idhFirst)
|
||
|
return nidProblemPage;
|
||
|
|
||
|
if (idh == nidService + idhFirst)
|
||
|
return nidService;
|
||
|
|
||
|
if (idh == IDH_FAIL)
|
||
|
return nidFailNode;
|
||
|
|
||
|
if (idh == IDH_BYE)
|
||
|
return nidByeNode;
|
||
|
|
||
|
ASSERT (idh >= idhFirst);
|
||
|
return idh - idhFirst;
|
||
|
}
|
||
|
|
||
|
// Associate a state with a node.
|
||
|
// INPUT nid
|
||
|
// INPUT ist - Normally, index of a state for that node.
|
||
|
// If nid == nidProblemPage, then ist is actually NID of selected problem
|
||
|
void CInfer::SetNodeState(NID nid, IST ist)
|
||
|
{
|
||
|
if (nid == nidNil)
|
||
|
return;
|
||
|
|
||
|
CString strTemp;
|
||
|
CString strTxt;
|
||
|
|
||
|
if (ist == ST_WORKED)
|
||
|
{
|
||
|
if (nid == nidFailNode || nid == nidSniffedAllCausesNormalNode
|
||
|
|| nid == nidService || nid == nidImpossibleNode)
|
||
|
{
|
||
|
if (m_pTopic->HasBES())
|
||
|
{
|
||
|
m_bUseBackEndRedirection = true;
|
||
|
CString strThrowaway; // we don't really care about this string;
|
||
|
// we call OutputBackend strictly for the side
|
||
|
// effect of setting m_strEncodedForm.
|
||
|
OutputBackend(strThrowaway);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_bDone = true;
|
||
|
AddToBasisForInference(nid, ist); // this node still needs to be present
|
||
|
// in m_arrBasisForInference, as it is
|
||
|
// present in m_SniffedStates.
|
||
|
|
||
|
// Add to the visited array to be displayed in the visible history page. RAB-20000628.
|
||
|
try
|
||
|
{
|
||
|
m_arrnidVisited.push_back( nid );
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ist == ST_ANY)
|
||
|
{
|
||
|
// We rely on the fact that only a service node offers ST_ANY
|
||
|
// ("Is there anything else I can try?")
|
||
|
m_bRecycleSkippedNode = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// We should never have service node go past this point (always ST_WORKED or ST_ANY).
|
||
|
|
||
|
if (nid == nidByeNode || nid == nidFailNode || nid == nidSniffedAllCausesNormalNode)
|
||
|
return;
|
||
|
|
||
|
if (ist == ST_UNKNOWN)
|
||
|
{
|
||
|
// Add it to the list of skipped nodes & visited nodes
|
||
|
try
|
||
|
{
|
||
|
m_arrnidSkipped.push_back(nid);
|
||
|
m_arrnidVisited.push_back(nid);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (nid == nidProblemPage)
|
||
|
{
|
||
|
if (!IsProblemNode(ist))
|
||
|
{
|
||
|
// Totally bogus query. Arbitrary course of action.
|
||
|
m_bRecycleSkippedNode = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Change this around to the way we would express it for any other node.
|
||
|
nid = ist;
|
||
|
ist = 1; // Set this problem node to a state value of 1 (in fact, we never
|
||
|
// explicitly set problem nodes to state value of 0)
|
||
|
|
||
|
m_nidProblem = nid; // special case: here instead of in m_arrnidVisited
|
||
|
AddToBasisForInference(nid, ist);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AddToBasisForInference(nid, ist);
|
||
|
|
||
|
// Store into our list of nodes obtained from the user
|
||
|
try
|
||
|
{
|
||
|
m_arrnidVisited.push_back(nid);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CInfer::AddToBasisForInference(NID nid, IST ist)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
m_BasisForInference.push_back(CNodeStatePair(nid, ist));
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add to the list of (previously) sniffed nodes.
|
||
|
void CInfer::AddToSniffed(NID nid, IST ist)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (ist == ST_WORKED && m_pTopic->IsCauseNode(nid))
|
||
|
{ // in case of cause node in abnormal state (which is ST_WORKED)
|
||
|
// we need to set state to "1" as if it was sniffed.
|
||
|
// This situation happens during manual sniffing of cause node that worked.
|
||
|
ist = 1;
|
||
|
}
|
||
|
m_SniffedStates.push_back(CNodeStatePair(nid, ist));
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Be careful not to call this redundantly: its call to CTopic::GetRecommendations()
|
||
|
// is expensive.
|
||
|
void CInfer::GetRecommendations()
|
||
|
{
|
||
|
// if we haven't previously sought a recommendation...
|
||
|
if ( m_SniffedRecommendation.nid() != nidNil )
|
||
|
{
|
||
|
// The one and only relevant recommendation is already forced, so don't bother
|
||
|
// getting recommendations.
|
||
|
// m_SniffedRecommendation.nid() is a Cause node in its abnormal state
|
||
|
m_Recommendations.empty();
|
||
|
try
|
||
|
{
|
||
|
m_Recommendations.push_back(m_SniffedRecommendation.nid());
|
||
|
m_bRecOK = true;
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Pass data into m_pTopic
|
||
|
// Get back recomendations.
|
||
|
int status = m_pTopic->GetRecommendations(m_BasisForInference, m_Recommendations);
|
||
|
m_bRecOK = (status == CTopic::RS_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// returns true if nid is a problem node of this network
|
||
|
bool CInfer::IsProblemNode(NID nid) const
|
||
|
{
|
||
|
// get data array of problem nodes
|
||
|
vector<NID>* parrnid = NULL;
|
||
|
|
||
|
m_pTopic->GetProblemArray(parrnid);
|
||
|
|
||
|
vector<NID>::const_iterator itnidBegin = parrnid->begin();
|
||
|
vector<NID>::const_iterator itnidEnd = parrnid->end();
|
||
|
vector<NID>::const_iterator itnidProblem = find(itnidBegin, itnidEnd, nid);
|
||
|
|
||
|
if (itnidProblem == itnidEnd)
|
||
|
return false;
|
||
|
else
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CInfer::IsInSniffedArray(NID nid) const
|
||
|
{
|
||
|
UINT nSniffedNodes = m_SniffedStates.size();
|
||
|
|
||
|
for (UINT i = 0; i < nSniffedNodes; i++)
|
||
|
{
|
||
|
if (m_SniffedStates[i].nid() == nid)
|
||
|
{
|
||
|
// Do not have to check for state, as m_SniffedStates will
|
||
|
// have only valid states (states, which are accepted by BNTS),
|
||
|
// no 102 or -1 states
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// For writing the new page after inference: the following texts are
|
||
|
// invariant for a given topic (aka network).
|
||
|
// -------------------------------------------------------------------
|
||
|
|
||
|
// CreateUnknownButtonText: Reads the network property for the
|
||
|
// unknown-state radio button from the network dsc file.
|
||
|
// Puts value in strUnknown
|
||
|
// This is specific to the radio button for "unknown" in the history table,
|
||
|
// that is, for a node which has previously been visited. This should not be
|
||
|
// used for the radio button for the "unknown" state of the present node.
|
||
|
void CInfer::CreateUnknownButtonText(CString & strUnknown) const
|
||
|
{
|
||
|
strUnknown = m_pTopic->GetNetPropItemStr(HTK_UNKNOWN_RBTN);
|
||
|
if (strUnknown.IsEmpty())
|
||
|
strUnknown = SZ_UNKNOWN;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// AppendNextButtonText: Reads the network property for the
|
||
|
// NEXT button from the network dsc file and append it to str.
|
||
|
void CInfer::AppendNextButtonText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HTK_NEXT_BTN);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_NEXT_BTN;
|
||
|
|
||
|
str += strTemp;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// AppendNextButtonText: Reads the network property for the
|
||
|
// NEXT button from the network dsc file and append it to str.
|
||
|
void CInfer::AppendStartOverButtonText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HTK_START_BTN);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_START_BTN;
|
||
|
|
||
|
str += strTemp;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// AppendBackButtonText: Reads the network property for the
|
||
|
// BACK button from the network dsc file and append it to str.
|
||
|
void CInfer::AppendBackButtonText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HTK_BACK_BTN);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_BACK_BTN;
|
||
|
|
||
|
str += strTemp;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// AppendPPSnifferButtonText: Reads the network property for the
|
||
|
// sniffer button from the network dsc file.
|
||
|
// NOTE that this button is related to "expensive" sniffing only.
|
||
|
// Appends to str.
|
||
|
void CInfer::AppendPPSnifferButtonText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HTK_SNIF_BTN);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_PP_SNIF_BTN;
|
||
|
|
||
|
str += strTemp;
|
||
|
}
|
||
|
|
||
|
// AppendManualSniffButtonText: Reads the network property for the
|
||
|
// manual sniff button from the network dsc file.
|
||
|
// Appends to str.
|
||
|
void CInfer::AppendManualSniffButtonText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_TEXT_SNIFF_ONE_NODE);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_SNIFF_ONE_NODE;
|
||
|
|
||
|
str += strTemp;
|
||
|
}
|
||
|
|
||
|
// AppendHistTableSniffedText: Reads the network property for the
|
||
|
// indication in history table that a node was sniffed.
|
||
|
// Appends to str.
|
||
|
void CInfer::AppendHistTableSniffedText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_HIST_TABLE_SNIFFED_TEXT);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_HIST_TABLE_SNIFFED_TEXT;
|
||
|
|
||
|
str+= _T("<BR>\n");
|
||
|
str += strTemp;
|
||
|
}
|
||
|
|
||
|
// AppendAllowSniffingText: Reads the network property for the
|
||
|
// label of the AllowSniffing checkbox from the network dsc file.
|
||
|
// Appends to str.
|
||
|
void CInfer::AppendAllowSniffingText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_ALLOW_SNIFFING_TEXT);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_ALLOW_SNIFFING_TEXT;
|
||
|
|
||
|
str += strTemp;
|
||
|
}
|
||
|
|
||
|
// AppendSniffFailedText: Reads the network property for the
|
||
|
// alert box to be used when manual sniffing fails from the network dsc file.
|
||
|
// Appends to str.
|
||
|
void CInfer::AppendSniffFailedText(CString & str) const
|
||
|
{
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_TEXT_SNIFF_ALERT_BOX);
|
||
|
|
||
|
if (strTemp.IsEmpty())
|
||
|
strTemp = SZ_SNIFF_FAILED;
|
||
|
|
||
|
str += strTemp;
|
||
|
}
|
||
|
|
||
|
// Appends an HTML link but makes it look like an HTML Form Button
|
||
|
// useful for Start Over in Online TS, because with no idea what browser user will have,
|
||
|
// we can't usefully use an onClick method (not supported in older browsers).
|
||
|
// Online TS runs in a "no scripting" environment.
|
||
|
// Pure HTML doesn't provide a means to put both a "Next" and a "Start Over" button
|
||
|
// in the same HTML form. Conversely, if Start Over btn was outside the form, pure HTML
|
||
|
// doesn't provide a means to align it with a button in the form.
|
||
|
// Note that x.gif does not exist: its absence creates a 1-pixel placeholder.
|
||
|
// >>>$MAINT We may want to change some of the rowspans to better emulate the exact size
|
||
|
// of a button; try to make it look perfect under IE
|
||
|
void CInfer::AppendLinkAsButton(
|
||
|
CString & str,
|
||
|
const CString & strTarget,
|
||
|
const CString & strLabel) const
|
||
|
{
|
||
|
str += _T("<!-- Begin pseudo button -->"
|
||
|
"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n"
|
||
|
"<tr>\n"
|
||
|
" <td rowspan=\"6\" bgcolor=\"white\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
|
||
|
" <td colspan=\"3\" bgcolor=\"white\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
|
||
|
"</tr>\n"
|
||
|
"<tr>\n"
|
||
|
" <td bgcolor=\"#C0C0C0\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"3\"></td>\n"
|
||
|
" <td rowspan=\"4\" bgcolor=\"#808080\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
|
||
|
" <td rowspan=\"4\" bgcolor=\"#000000\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
|
||
|
"</tr>\n"
|
||
|
"<tr>\n"
|
||
|
" <td bgcolor=\"#C0C0C0\">\n");
|
||
|
|
||
|
// >>> $MAINT might want to change the font/style in the following
|
||
|
str += _T("<font face=\"Arial\" size=\"2\"> \n"
|
||
|
" <a href=\"");
|
||
|
str += strTarget;
|
||
|
str += _T("\" style=\"text-decoration:none; color:black\">\n"
|
||
|
" <font color=\"black\">");
|
||
|
str += strLabel;
|
||
|
str += _T("</font></a>\n"
|
||
|
" </font></td>\n"
|
||
|
"</tr>\n"
|
||
|
"<tr>\n"
|
||
|
" <td bgcolor=\"#C0C0C0\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"3\"></td>\n"
|
||
|
"</tr>\n"
|
||
|
"<tr>\n"
|
||
|
" <td bgcolor=\"#808080\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
|
||
|
"</tr>\n"
|
||
|
"<tr>\n"
|
||
|
" <td colspan=\"3\" bgcolor=\"#000000\">\n"
|
||
|
" <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
|
||
|
"</tr>\n"
|
||
|
"</table>\n"
|
||
|
"<!-- End pseudo button -->\n");
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// Writing to the new HTML page. Miscellaneous low-level pieces.
|
||
|
// -------------------------------------------------------------------
|
||
|
|
||
|
// If the state name is missing or is simply "<hide>", return true.
|
||
|
// This indicates a state that should never be overtly presented to the user as a choice.
|
||
|
// Typically used in an informational node, this may describe a state that can be deduced with
|
||
|
// 100% certainty from certain other node/state combinations.
|
||
|
/* static */ bool CInfer::HideState(LPCTSTR szStateName)
|
||
|
{
|
||
|
if (szStateName && *szStateName && _tcscmp(szStateName, _T("<hide>") ) )
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// write a symbolic name (based on NID) to a string sz
|
||
|
// INPUT nid - node ID
|
||
|
// OUTPUT str - the string to which we write.
|
||
|
// RETURNS true if successful
|
||
|
// NOTE that this restores the "current" node when it is finished.
|
||
|
// Alternative would be side effect of setting current node (by omitting nidOld), but that
|
||
|
// would work strangely on "special" nodes (e.g. Service, Fail), which aren't in BNTS.
|
||
|
bool CInfer::SymbolicFromNID(CString & str, NID nid) const
|
||
|
{
|
||
|
if (nid == nidProblemPage)
|
||
|
{
|
||
|
str= NODE_PROBLEM_ASK;
|
||
|
return true;
|
||
|
}
|
||
|
if (nid == nidService)
|
||
|
{
|
||
|
str= NODE_SERVICE;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (nid == nidFailNode)
|
||
|
{
|
||
|
str= NODE_FAIL;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (nid == nidSniffedAllCausesNormalNode)
|
||
|
{
|
||
|
str= NODE_FAILALLCAUSESNORMAL;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (nid == nidImpossibleNode)
|
||
|
{
|
||
|
str= NODE_IMPOSSIBLE;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (nid == nidByeNode)
|
||
|
{
|
||
|
str= NODE_BYE;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// if it's a "normal" node, this will fill in the name
|
||
|
str= m_pTopic->GetNodeSymName(nid);
|
||
|
|
||
|
return (!str.IsEmpty() );
|
||
|
}
|
||
|
|
||
|
// append an HTML radio button to str
|
||
|
// INPUT/OUTPUT str - the string to which we append
|
||
|
// INPUT szName, szValue - For <INPUT TYPE=RADIO NAME=szName VALUE=szValue>
|
||
|
// INPUT szLabel - text to appear after the radio button but before a line break
|
||
|
/*static*/ void CInfer::AppendRadioButtonCurrentNode(
|
||
|
CString &str, LPCTSTR szName, LPCTSTR szValue, LPCTSTR szLabel, bool bChecked/*= false*/)
|
||
|
{
|
||
|
CString strTxt;
|
||
|
|
||
|
if ( ! HideState(szLabel))
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TR>\n<TD>\n";
|
||
|
|
||
|
strTxt.Format(_T("<INPUT TYPE=RADIO NAME=\"%s\" VALUE=\"%s\" %s> %s"),
|
||
|
szName, szValue, bChecked ? _T("CHECKED") : _T(""), szLabel);
|
||
|
str += strTxt;
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n</TD>\n</TR>\n";
|
||
|
else
|
||
|
str += "\n<BR>\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This is different than other radio buttons because it
|
||
|
// - has a different format for label szLabel.
|
||
|
// - vanishes if bShowHistory is false and this button isn't CHECKED
|
||
|
// - turns into a hidden field if bShowHistory is false and this button is CHECKED
|
||
|
// - writes SNIFFED_ values as applicable...although that's not in this function: it's
|
||
|
// handled in a separate call to AppendHiddenFieldSniffed()
|
||
|
// JM 11/12/99 previously, we special-cased hidden states here. However, per 11/11/99 email
|
||
|
// from John Locke, the only state we ever hide in the History Table (for v3.2) is the
|
||
|
// Unknown/skipped state, and that is handled elsewhere.
|
||
|
// INPUT/OUTPUT str - string to which we append the HTML for this button.
|
||
|
// INPUT nid - NID of node
|
||
|
// INPUT value - state
|
||
|
// INPUT bSet - true ==> button is CHECKED
|
||
|
// INPUT szctype - short name of the state
|
||
|
// INPUT bShowHistory - see explanation a few lines above
|
||
|
void CInfer::AppendRadioButtonVisited(
|
||
|
CString &str, NID nid, UINT value, bool bSet, LPCTSTR szLabel, bool bShowHistory) const
|
||
|
{
|
||
|
CString strTxt;
|
||
|
CString strSymbolic;
|
||
|
|
||
|
SymbolicFromNID(strSymbolic, nid);
|
||
|
|
||
|
if (bShowHistory)
|
||
|
strTxt.Format(_T("<INPUT TYPE=RADIO NAME=%s VALUE=%u%s>%-16s \n"),
|
||
|
strSymbolic, value, bSet ? _T(" CHECKED") : _T(""), szLabel);
|
||
|
else if (bSet)
|
||
|
strTxt.Format(_T("<INPUT TYPE=HIDDEN NAME=%s VALUE=%u>\n"),
|
||
|
strSymbolic, value);
|
||
|
|
||
|
str += strTxt;
|
||
|
}
|
||
|
|
||
|
// If this nid is an already sniffed node, then we append this fact as a
|
||
|
// "hidden" value in the HTML in str.
|
||
|
// For example, if a node with symbolic name FUBAR has been sniffed in state 1,
|
||
|
// we will append "<INPUT TYPE=HIDDEN NAME=SNIFFED_FUBAR VALUE=1>\n"
|
||
|
// INPUT: string to have appended; node ID
|
||
|
// OUTPUT: string with appended hidden field if node was sniffed
|
||
|
// RETURN: true id string is appended
|
||
|
void CInfer::AppendHiddenFieldSniffed(CString &str, NID nid) const
|
||
|
{
|
||
|
CString strSymbolic;
|
||
|
UINT nSniffedNodes = m_SniffedStates.size();
|
||
|
|
||
|
SymbolicFromNID(strSymbolic, nid);
|
||
|
|
||
|
for (UINT i = 0; i < nSniffedNodes; i++)
|
||
|
{
|
||
|
if (m_SniffedStates[i].nid() == nid)
|
||
|
{
|
||
|
// Do not have to check for state, as m_SniffedStates will
|
||
|
// have only valid states (states, which are accepted by BNTS),
|
||
|
// no 102 or -1 states
|
||
|
|
||
|
// In case that this is manually sniffed cause node in abnormal state
|
||
|
// (and we just re-submit previous page), we need not mention
|
||
|
// this node as sniffed.
|
||
|
|
||
|
if (!(IsManuallySniffedNode(nid) &&
|
||
|
m_SniffedStates[i].state() == 1 &&
|
||
|
m_pTopic->IsCauseNode(nid))
|
||
|
)
|
||
|
{
|
||
|
CString strTxt;
|
||
|
|
||
|
strTxt.Format(_T("<INPUT TYPE=HIDDEN NAME=%s%s VALUE=%u>\n"),
|
||
|
C_SNIFFTAG, strSymbolic, m_SniffedStates[i].state());
|
||
|
str += strTxt;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Appends (to str) info conveying whether Automatic Sniffing is allowed.
|
||
|
void CInfer::AddAllowAutomaticSniffingHiddenField(CString &str) const
|
||
|
{
|
||
|
CString strTxt;
|
||
|
|
||
|
strTxt.Format(_T("<INPUT TYPE=HIDDEN NAME=%s VALUE=%s>\n"),
|
||
|
C_ALLOW_AUTOMATIC_SNIFFING_NAME, C_ALLOW_AUTOMATIC_SNIFFING_CHECKED);
|
||
|
str += strTxt;
|
||
|
}
|
||
|
|
||
|
// Radio buttons for currently recommended node
|
||
|
// Each button will appear only if appropriate string property is defined
|
||
|
// Accounts for multi-state or simple binary node.
|
||
|
// INPUT nid - identifies a node of an appropriate type
|
||
|
// INPUT/OUTPUT str - string to which we are appending to build HTML page we send back.
|
||
|
// The detailed behavior of this function was changed at John Locke's request 11/30/98 for V3.0.
|
||
|
// Then for v3.1, handling of H_ST_AB_TXT_STR, H_ST_NORM_TXT_STR removed 8/19/99 per request
|
||
|
// from John Locke & Alex Sloley
|
||
|
void CInfer::AppendCurrentRadioButtons(NID nid, CString & str)
|
||
|
{
|
||
|
CString strSymbolic;
|
||
|
|
||
|
SymbolicFromNID(strSymbolic, nid);
|
||
|
|
||
|
CString strPropLongName; // long name of property
|
||
|
|
||
|
int nStates = m_pTopic->GetCountOfStates(nid);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TABLE>";
|
||
|
|
||
|
for (IST state=0; state < nStates; state ++)
|
||
|
{
|
||
|
TCHAR szStateNumber[MAXBUF]; // buffer for _itot()
|
||
|
CString strDisplayState = _itot( state, szStateNumber, 10 );
|
||
|
if (state == 1 && m_pTopic->IsCauseNode( nid ))
|
||
|
strDisplayState = SZ_ST_WORKED;
|
||
|
|
||
|
strPropLongName = _T("");
|
||
|
|
||
|
if (strPropLongName.IsEmpty())
|
||
|
// account for multistate node
|
||
|
strPropLongName = m_pTopic->GetNodePropItemStr(nid, MUL_ST_LONG_NAME_STR, state);
|
||
|
|
||
|
// if we're not past the end of states, append a button
|
||
|
if (!strPropLongName.IsEmpty())
|
||
|
AppendRadioButtonCurrentNode(str,
|
||
|
strSymbolic,
|
||
|
strDisplayState,
|
||
|
strPropLongName,
|
||
|
// check state button if this state was sniffed
|
||
|
m_SniffedRecommendation.state() == state ? true : false);
|
||
|
};
|
||
|
|
||
|
// "unknown" state (e.g. "I want to skip this")
|
||
|
strPropLongName = m_pTopic->GetNodePropItemStr(nid, H_ST_UKN_TXT_STR);
|
||
|
if (!strPropLongName.IsEmpty())
|
||
|
AppendRadioButtonCurrentNode(str, strSymbolic, SZ_ST_UNKNOWN, strPropLongName);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "</TABLE>\n";
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If we are showing the history table, place a localizable Full Name
|
||
|
// (e.g."Printouts appear garbled") of problem and a hidden-data
|
||
|
// field corresponding to this problem into str.
|
||
|
// Otherwise, just the hidden data field
|
||
|
void CInfer::CreateProblemVisitedText(CString & str, NID nidProblem, bool bShowHistory)
|
||
|
{
|
||
|
// This code is structured in pieces as sending all of these strings to a single
|
||
|
// CString::Format() results in a program exception. Did some research into this
|
||
|
// behavior but did not discover anything. RAB-981014.
|
||
|
CString tmpStr;
|
||
|
|
||
|
tmpStr.Format( _T("%s"), bShowHistory ? m_pTopic->GetNodeFullName(nidProblem) : _T("") );
|
||
|
str= tmpStr;
|
||
|
tmpStr.Format( _T("<INPUT TYPE=HIDDEN NAME=%s "), NODE_PROBLEM_ASK );
|
||
|
str+= tmpStr;
|
||
|
tmpStr.Format( _T("VALUE=%s>"), m_pTopic->GetNodeSymName(nidProblem) );
|
||
|
str+= tmpStr;
|
||
|
tmpStr.Format( _T("%s"), bShowHistory ? _T("") : _T("\n") );
|
||
|
str+= tmpStr;
|
||
|
str+= _T("\n");
|
||
|
}
|
||
|
|
||
|
// Append a NET property (for Belief Network as a whole, not for one
|
||
|
// particular node) to str.
|
||
|
// 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.
|
||
|
void CInfer::AppendMultilineNetProp(CString & str, LPCTSTR szPropName, LPCTSTR szFormat)
|
||
|
{
|
||
|
str += m_pTopic->GetMultilineNetProp(szPropName, szFormat);
|
||
|
}
|
||
|
|
||
|
// Like AppendMultilineNetProp, 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.
|
||
|
void CInfer::AppendMultilineNodeProp(CString & str, NID nid, LPCTSTR szPropName, LPCTSTR szFormat)
|
||
|
{
|
||
|
str += m_pTopic->GetMultilineNodeProp(nid, szPropName, szFormat);
|
||
|
}
|
||
|
|
||
|
|
||
|
// JSM V3.2 Wrapper for AppendMultilineNetProp to make it easier
|
||
|
// to fill in the Net properties in HTMLFragments
|
||
|
CString CInfer::ConvertNetProp(const CString &strNetPropName)
|
||
|
{
|
||
|
CString strNetPropVal;
|
||
|
AppendMultilineNetProp(strNetPropVal,strNetPropName,"%s");
|
||
|
return strNetPropVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If there is a pre-sniffed recommendation, remove it from the list & set m_SniffedRecommendation.
|
||
|
void CInfer::IdentifyPresumptiveCause()
|
||
|
{
|
||
|
vector<NID> arrnidNoSequence;
|
||
|
multimap<int, NID> mapSeqToNID;
|
||
|
|
||
|
// Find all presumptive causes
|
||
|
for (int i = 0; i < m_SniffedStates.size(); i++)
|
||
|
{
|
||
|
if (m_pTopic->IsCauseNode(m_SniffedStates[i].nid()) // cause node ...
|
||
|
&&
|
||
|
m_SniffedStates[i].state() == 1) // ... that is sniffed in abnormal (1) state
|
||
|
{
|
||
|
if (IsManuallySniffedNode(m_SniffedStates[i].nid()))
|
||
|
{
|
||
|
// now we have manually sniffed cause node in abnormal state.
|
||
|
// It means that we are re-submitting the page. We will set m_SniffedRecommendation
|
||
|
// to this node, and return.
|
||
|
m_SniffedRecommendation = CNodeStatePair(m_SniffedStates[i].nid(), 1 /*cause node abnormal state*/);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NID nid = m_SniffedStates[i].nid();
|
||
|
CString str = m_pTopic->GetNodePropItemStr(nid, H_NODE_CAUSE_SEQUENCE);
|
||
|
try
|
||
|
{
|
||
|
if (str.IsEmpty())
|
||
|
arrnidNoSequence.push_back(nid);
|
||
|
else
|
||
|
{
|
||
|
mapSeqToNID.insert(pair<int, NID>(_ttoi(str), nid));
|
||
|
}
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We want the first in sequence according to H_NODE_CAUSE_SEQUENCE numbering.
|
||
|
// If nothing has a number, we settle for the (arbitrary) first in the array of
|
||
|
// unnumbered Cause nodes.
|
||
|
if (mapSeqToNID.size() > 0)
|
||
|
m_SniffedRecommendation = CNodeStatePair( (mapSeqToNID.begin()->second), 1 /*cause node abnormal state*/);
|
||
|
else if (arrnidNoSequence.size() > 0)
|
||
|
m_SniffedRecommendation = CNodeStatePair( *(arrnidNoSequence.begin()), 1 /*cause node abnormal state*/);
|
||
|
|
||
|
// now remove the matching nid from the incoming arrays
|
||
|
if (m_SniffedRecommendation.nid() != nidNil)
|
||
|
{
|
||
|
for (i = 0; i < m_BasisForInference.size(); i++)
|
||
|
{
|
||
|
if (m_BasisForInference[i].nid() == m_SniffedRecommendation.nid())
|
||
|
{
|
||
|
m_BasisForInference.erase(m_BasisForInference.begin() + i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < m_SniffedStates.size(); i++)
|
||
|
{
|
||
|
if (m_SniffedStates[i].nid() == m_SniffedRecommendation.nid())
|
||
|
{
|
||
|
m_SniffedStates.erase(m_SniffedStates.begin() + i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < m_arrnidVisited.size(); i++)
|
||
|
{
|
||
|
if (m_arrnidVisited[i] == m_SniffedRecommendation.nid())
|
||
|
{
|
||
|
m_arrnidVisited.erase(m_arrnidVisited.begin() + i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return true if every Cause node in the topic is determined to be normal;
|
||
|
// this would imply that there is nothing useful this topic can do for us.
|
||
|
bool CInfer::AllCauseNodesNormal()
|
||
|
{
|
||
|
// for every node in this Belief Network (but taking action only on "cause" nodes)
|
||
|
// see if each of these is known to be Normal
|
||
|
for(int nid = 0; nid < m_pTopic->CNode(); nid++)
|
||
|
{
|
||
|
if (m_pTopic->IsCauseNode(nid))
|
||
|
{
|
||
|
bool bFound=false;
|
||
|
|
||
|
for (CBasisForInference::iterator p= m_SniffedStates.begin();
|
||
|
p != m_SniffedStates.end();
|
||
|
++p)
|
||
|
{
|
||
|
if (p->nid() == nid)
|
||
|
{
|
||
|
if (p->state() != 0)
|
||
|
// found a Cause node in an abnormal state (or skipped)
|
||
|
return false;
|
||
|
|
||
|
bFound = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!bFound)
|
||
|
// found a Cause node for which no state is set
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------
|
||
|
// Writing pieces of the new HTML page. This builds a structure to be used under HTI
|
||
|
// control to represent the recommended node and the (visible or invisible) history table.
|
||
|
// -------------------------------------------------------------
|
||
|
void CInfer::FillInHTMLFragments(CHTMLFragmentsTS &frag)
|
||
|
{
|
||
|
vector<NID>arrnidPresumptiveCause;
|
||
|
|
||
|
// First, a side effect: get the URL for the Online TS Start Over link / pseudo-button
|
||
|
m_strStartOverLink = frag.GetStartOverLink();
|
||
|
|
||
|
// Then on to the main business at hand. In practice (at least as of 11/99)
|
||
|
// bIncludesHistoryTable and bIncludesHiddenHistory are mutually exclusive,
|
||
|
// but this class doesn't need that knowledge.
|
||
|
const bool bIncludesHistoryTable = frag.IncludesHistoryTable();
|
||
|
const bool bIncludesHiddenHistory = frag.IncludesHiddenHistory();
|
||
|
|
||
|
{
|
||
|
// JSM V3.2: convert the net properties in the HTML fragment
|
||
|
// The HTI template may indicate that certain net properties are to be written
|
||
|
// directly into the resulting page. We get a list of these properties and
|
||
|
// fill in a structrue in frag to contain their values.
|
||
|
CString strNetPropName;
|
||
|
for(;frag.IterateNetProp(strNetPropName);)
|
||
|
frag.SetNetProp(strNetPropName,ConvertNetProp(strNetPropName));
|
||
|
}
|
||
|
{
|
||
|
// JM V3.2 to handle sniffing correctly, must do this before history table: sniffing
|
||
|
// on the fly (which happens in AppendCurrentNodeText()) could add to the history.
|
||
|
CString strCurrentNode;
|
||
|
AppendCurrentNodeText(strCurrentNode);
|
||
|
frag.SetCurrentNodeText(strCurrentNode);
|
||
|
}
|
||
|
|
||
|
CString strHiddenHistory;
|
||
|
if (m_nidProblem != nidNil)
|
||
|
{
|
||
|
CString strProblem;
|
||
|
CreateProblemVisitedText(strProblem, m_nidProblem, frag.IncludesHistoryTable());
|
||
|
|
||
|
// OK V3.2 We use hidden field to save the value returned by the "AllowSniffing"
|
||
|
// checkbox (on the problem page) and pass it to each subsequent page.
|
||
|
// We effectively place this before the history table.
|
||
|
if (m_pSniff)
|
||
|
if (m_pSniff->GetAllowAutomaticSniffingPolicy())
|
||
|
AddAllowAutomaticSniffingHiddenField(strProblem);
|
||
|
|
||
|
// Added for V3.2 sniffing
|
||
|
// Not lovely, but this is where we insert sniffed presumptive causes (as hidden
|
||
|
// fields).
|
||
|
// >>> $MAINT Once we integrate with a launcher, this may require
|
||
|
// further thought: what if we sniff presumptive causes before we have an
|
||
|
// identified problem? Where do we put those hidden fields?
|
||
|
for (UINT i=0; i<m_arrnidVisited.size(); i++)
|
||
|
{
|
||
|
NID nid = m_arrnidVisited[i];
|
||
|
int stateSet = SNIFF_FAILURE_RESULT;
|
||
|
|
||
|
{
|
||
|
UINT nSetNodes = m_SniffedStates.size();
|
||
|
for (UINT ii = 0; ii < nSetNodes; ii++)
|
||
|
if (m_SniffedStates[ii].nid() == nid) {
|
||
|
stateSet = m_SniffedStates[ii].state();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_pTopic->IsCauseNode(nid) && stateSet == 1)
|
||
|
{
|
||
|
// This is a cause node sniffed as abnormal, to be presented eventually
|
||
|
// as a "presumptive" cause. All we put in the History table is hidden
|
||
|
AppendStateText(strProblem, nid, 1, true, false, false, stateSet);
|
||
|
try
|
||
|
{
|
||
|
arrnidPresumptiveCause.push_back(nid);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bIncludesHistoryTable)
|
||
|
frag.SetProblemText(strProblem);
|
||
|
if (bIncludesHiddenHistory)
|
||
|
strHiddenHistory = strProblem;
|
||
|
}
|
||
|
|
||
|
|
||
|
UINT nVisitedNodes = m_arrnidVisited.size();
|
||
|
// iVisited incremented for every visited node, iHistory only for a subset:
|
||
|
// if we have a visible History table, iHistory provides an index of nodes visible
|
||
|
// to the end user. If not, iHistory is a harmless irrelevance
|
||
|
for (UINT iVisited=0, iHistory=0; iVisited<nVisitedNodes; iVisited++)
|
||
|
{
|
||
|
NID nid = m_arrnidVisited[iVisited];
|
||
|
int nStates = m_pTopic->GetCountOfStates(nid);
|
||
|
int stateSet = -1;
|
||
|
|
||
|
if (IsSkipped(nid))
|
||
|
{
|
||
|
// "skipped" node.
|
||
|
// instead of ST_UNKNOWN (==102), stateSet uses the number immediately
|
||
|
// past the last valid state of this node. Most nodes have only states
|
||
|
// 0, 1, and 102 so typically stateSet is set = 2, but a multistate
|
||
|
// node can use a different number
|
||
|
stateSet = nStates;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UINT nSetNodes = m_BasisForInference.size();
|
||
|
for (UINT ii = 0; ii < nSetNodes; ii++)
|
||
|
if (m_BasisForInference[ii].nid() == nid) {
|
||
|
stateSet = m_BasisForInference[ii].state();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The following test added for V3.2 sniffing
|
||
|
// Weed out cause node sniffed as abnormal, to be presented eventually
|
||
|
// as a "presumptive" cause. Handled above as a hidden field.
|
||
|
if (find(arrnidPresumptiveCause.begin(), arrnidPresumptiveCause.end(), nid)
|
||
|
!= arrnidPresumptiveCause.end())
|
||
|
{
|
||
|
// cause node sniffed as abnormal
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (bIncludesHistoryTable)
|
||
|
{
|
||
|
CString strVisitedNode;
|
||
|
AppendVisitedNodeText(strVisitedNode, nid, true);
|
||
|
frag.PushBackVisitedNodeText(strVisitedNode);
|
||
|
}
|
||
|
|
||
|
for (UINT iState=0; iState <= nStates; iState++)
|
||
|
{
|
||
|
if (bIncludesHistoryTable)
|
||
|
{
|
||
|
CString strState;
|
||
|
|
||
|
AppendStateText(strState, nid, iState, iState == stateSet,
|
||
|
iState == nStates, true, stateSet);
|
||
|
|
||
|
// If we are processing last state, and we need to attach
|
||
|
// hidden field for this node as sniffed one
|
||
|
// (if it ts really sniffed)
|
||
|
if (iState == nStates)
|
||
|
AppendHiddenFieldSniffed(strState, nid);
|
||
|
|
||
|
// We need not have empty entry in CHTMLFragment's array,
|
||
|
// describing history table, so by applying "numPresumptiveCauseNodesEncounered"
|
||
|
// we make this array continuous
|
||
|
frag.PushBackStateText(iHistory, strState);
|
||
|
}
|
||
|
if (bIncludesHiddenHistory)
|
||
|
{
|
||
|
AppendStateText(strHiddenHistory, nid, iState, iState == stateSet,
|
||
|
iState == nStates, false, stateSet);
|
||
|
|
||
|
// same as in case of visible history table applies.
|
||
|
if (iState == nStates)
|
||
|
AppendHiddenFieldSniffed(strHiddenHistory, nid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bIncludesHistoryTable)
|
||
|
{
|
||
|
// Check if we need to mark this as visibly sniffed.
|
||
|
UINT nSniffedNodes = m_SniffedStates.size();
|
||
|
for (UINT i = 0; i < nSniffedNodes; i++)
|
||
|
{
|
||
|
if (m_SniffedStates[i].nid() == nid)
|
||
|
{
|
||
|
// mark it visibly as sniffed
|
||
|
CString strState;
|
||
|
AppendHistTableSniffedText( strState );
|
||
|
frag.PushBackStateText(iHistory, strState);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
iHistory++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (frag.IncludesHiddenHistory())
|
||
|
frag.SetHiddenHistoryText(strHiddenHistory);
|
||
|
|
||
|
frag.SetSuccessBool(m_bDone);
|
||
|
}
|
||
|
|
||
|
// Append the text for the current (recommended) node to str
|
||
|
void CInfer::AppendCurrentNodeText(CString & str)
|
||
|
{
|
||
|
CString strSave = str;
|
||
|
|
||
|
if (m_nidProblem == nidNil)
|
||
|
// show first page (radio-button list of possible problems)
|
||
|
AppendProblemPage(str);
|
||
|
else if (m_bDone && !ManuallySniffedNodeExists())
|
||
|
AppendNIDPage(nidByeNode, str);
|
||
|
else if ( m_SniffedRecommendation.nid() != nidNil )
|
||
|
// we already have a recommendation, presumably from a sniffer
|
||
|
AppendNIDPage(m_SniffedRecommendation.nid(), str);
|
||
|
else
|
||
|
{
|
||
|
// sniff/resniff all, as needed
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
{
|
||
|
// Before we mess with m_BasisForInference, determine if the only node with a
|
||
|
// state is the problem node
|
||
|
// [BC - 20010301] - Added check for size of skipped node count when setting
|
||
|
// bHaveOnlyProblem here. This catches case where user selects to skip first
|
||
|
// node presented, when that node is sniffed in abnormal state.
|
||
|
bool bHaveOnlyProblem = (m_BasisForInference.size() == 1) &&
|
||
|
(m_arrnidSkipped.size() == 0);
|
||
|
|
||
|
|
||
|
if (m_pSniff)
|
||
|
{
|
||
|
long nExplicitlySetByUser = 0;
|
||
|
CBasisForInference arrManuallySniffed; // can contain max 1 element;
|
||
|
// used to prevent resniffing
|
||
|
// of already sniffed node.
|
||
|
// We need arrayOrderRestorer in order to make sure that when sniffed
|
||
|
// nodes are first removed from the array of visited nodes, then restored,
|
||
|
// we maintain the same sequence in which nodes were visited in the first
|
||
|
// place. This order is important in our caching strategy and also provides
|
||
|
// a sense of consistency for the end user.
|
||
|
CArrayOrderRestorer arrayOrderRestorer(m_arrnidVisited);
|
||
|
|
||
|
if (ManuallySniffedNodeExists())
|
||
|
{
|
||
|
arrManuallySniffed.push_back(m_SniffedStates[m_SniffedStates.size()-1]);
|
||
|
}
|
||
|
|
||
|
// Remove all sniffed nodes from m_BasisForInference
|
||
|
m_BasisForInference -= m_SniffedStates;
|
||
|
|
||
|
// remove m_SniffedStates from m_arrnidVisited
|
||
|
m_arrnidVisited -= m_SniffedStates;
|
||
|
nExplicitlySetByUser = m_arrnidVisited.size();
|
||
|
|
||
|
if (bHaveOnlyProblem)
|
||
|
{
|
||
|
// sniff all since we're in problem page
|
||
|
m_pSniff->SniffAll(m_SniffedStates);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CBasisForInference arrSniffed;
|
||
|
|
||
|
// resniff all except recently sniffed manually (if any)
|
||
|
arrSniffed = m_SniffedStates;
|
||
|
arrSniffed -= arrManuallySniffed;
|
||
|
m_pSniff->Resniff(arrSniffed);
|
||
|
arrSniffed += arrManuallySniffed;
|
||
|
m_SniffedStates = arrSniffed;
|
||
|
}
|
||
|
|
||
|
// add updated m_SniffedStates to m_arrnidVisited
|
||
|
m_arrnidVisited += m_SniffedStates;
|
||
|
|
||
|
arrayOrderRestorer.Restore(nExplicitlySetByUser, m_arrnidVisited);
|
||
|
|
||
|
// Add all sniffed nodes into m_BasisForInference
|
||
|
m_BasisForInference += m_SniffedStates;
|
||
|
|
||
|
if (bHaveOnlyProblem && AllCauseNodesNormal())
|
||
|
{
|
||
|
// We just sniffed at startup & we already know all Cause nodes
|
||
|
// are in their normal states. There is absolutely nothing this
|
||
|
// troubleshooting topic can do to help this user.
|
||
|
AppendSniffAllCausesNormalPage(str);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// in case that we do not have sniffed recommendation from manual sniffing
|
||
|
if (m_SniffedRecommendation.nid() == nidNil)
|
||
|
{
|
||
|
// Did we get a presumptive cause out of that?
|
||
|
IdentifyPresumptiveCause();
|
||
|
}
|
||
|
if ( m_SniffedRecommendation.nid() != nidNil )
|
||
|
{
|
||
|
AppendNIDPage(m_SniffedRecommendation.nid(), str);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool bSniffSucceeded = true;
|
||
|
while (bSniffSucceeded)
|
||
|
{
|
||
|
IST state = -1;
|
||
|
NID nidNew = nidNil;
|
||
|
|
||
|
GetRecommendations();
|
||
|
|
||
|
if (!m_bRecOK)
|
||
|
{
|
||
|
str = strSave;
|
||
|
AppendImpossiblePage(str);
|
||
|
return;
|
||
|
}
|
||
|
else if (m_Recommendations.empty())
|
||
|
{
|
||
|
str = strSave;
|
||
|
AppendNIDPage(nidFailNode, str);
|
||
|
return;
|
||
|
}
|
||
|
else // Have Recommendations
|
||
|
{
|
||
|
// Find a recommendation from list of recommendations that is
|
||
|
// not in the skip list. This is normally the first node in the
|
||
|
// list.
|
||
|
int n = m_Recommendations.size();
|
||
|
|
||
|
for (UINT i=0; i<n; i++)
|
||
|
{
|
||
|
if (!IsSkipped(m_Recommendations[i]))
|
||
|
{
|
||
|
nidNew = m_Recommendations[i];
|
||
|
str = strSave;
|
||
|
AppendNIDPage(nidNew, str);
|
||
|
break; // out of for loop: just one recommendation is actually
|
||
|
// reported back to user.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// It is our first pass, no sniffed node pages
|
||
|
// were composed earlier in this loop
|
||
|
if (nidNew == nidNil)
|
||
|
{
|
||
|
// We fell though if the entire list of recommendations has been skipped
|
||
|
// via "ST_UNKNOWN" selection by the user.
|
||
|
if (m_bRecycleSkippedNode)
|
||
|
RecycleSkippedNode(); // this can affect m_bRecycleSkippedNode
|
||
|
|
||
|
if (m_bRecycleSkippedNode)
|
||
|
{
|
||
|
// The user got the service node earlier and now wants to review
|
||
|
// the nodes they marked "Unknown". We already removed the first
|
||
|
// "Unknown" node from the skip list and put its NID in
|
||
|
// m_nidRecycled. Now we just do a normal display of the page
|
||
|
// for that node.
|
||
|
nidNew = m_nidRecycled;
|
||
|
str = strSave;
|
||
|
AppendNIDPage(nidNew, str);
|
||
|
return;
|
||
|
}
|
||
|
else if (!m_arrnidSkipped.empty())
|
||
|
{
|
||
|
// We've got "Unknowns", they weren't just in the service page,
|
||
|
// so give 'em the service page
|
||
|
str = strSave;
|
||
|
AppendNIDPage(nidService, str);
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// no unknowns. Fail. Believed never to arise here, but coded
|
||
|
// this way for safety.
|
||
|
str = strSave;
|
||
|
AppendNIDPage(nidFailNode, str);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bSniffSucceeded = false;
|
||
|
|
||
|
// sniffing on the fly
|
||
|
if (m_pSniff)
|
||
|
bSniffSucceeded = m_pSniff->SniffNode(nidNew, &state);
|
||
|
|
||
|
if (bSniffSucceeded)
|
||
|
{
|
||
|
// if it's a cause node and was sniffed as abnormal
|
||
|
if (m_pTopic->IsCauseNode(nidNew) && state == 1)
|
||
|
{
|
||
|
// Display this page as a presumptive cause.
|
||
|
m_SniffedRecommendation = CNodeStatePair( nidNew, state );
|
||
|
str = strSave;
|
||
|
AppendNIDPage(nidNew, str);
|
||
|
return;
|
||
|
}
|
||
|
CNodeStatePair nodestateNew(nidNew, state);
|
||
|
try
|
||
|
{
|
||
|
m_SniffedStates.push_back(nodestateNew);
|
||
|
m_BasisForInference.push_back(nodestateNew);
|
||
|
m_arrnidVisited.push_back(nidNew);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Write radio buttons describing what was decided by user in a previous node. Part
|
||
|
// of "the table" (aka "the visited node table" or "table of previous responses").
|
||
|
//
|
||
|
// Note that Cause nodes are specially handled. On a cause node:
|
||
|
// state 0 = "no, this didn't fix it"
|
||
|
// state 1 = This wasn't OK, so we have a diagnosis. In that case, we
|
||
|
// wouldn't be displaying these radio buttons.
|
||
|
// We don't want the user selecting that value from the history table.
|
||
|
// We append this only if it's been sniffed and must be presented as a presumptive
|
||
|
// cause, and even then we always append it "hidden"
|
||
|
// state 2 = "skipped"
|
||
|
//
|
||
|
// In other words, on a cause node, the only possibilities we offer to the user through
|
||
|
// a visible history table are state 0 () and "skip".
|
||
|
//
|
||
|
// In the case where a Cause node has been sniffed abnormal, THE CALLING ROUTINE is
|
||
|
// responsible to call this only for the abnormal state. Otherwise, call for all states.
|
||
|
//
|
||
|
// OUTPUT str - string to which we append
|
||
|
// INPUT nid node of which this is a state
|
||
|
// INPUT state state number; for ST_UNKNOWN, this is the count of states, not 102
|
||
|
// INPUT bSet true = this is the current state of this node
|
||
|
// INPUT bSkipped true = this is the "skipped" state, not a normal node state known to BNTS
|
||
|
// INPUT bShowHistory true = we are showing a history table, false = history is stored
|
||
|
// invisibly in the HTML.
|
||
|
void CInfer::AppendStateText(CString & str, NID nid, UINT state, bool bSet, bool bSkipped,
|
||
|
bool bShowHistory, int nStateSet)
|
||
|
{
|
||
|
// Check if this selection worked.
|
||
|
// If so only display the "it worked" text in the history table.
|
||
|
if (m_pTopic->IsCauseNode(nid) && nStateSet == ST_WORKED)
|
||
|
{
|
||
|
if (state == 1) // it is presumptive cause ...
|
||
|
AppendRadioButtonVisited( str, nid, state, true,
|
||
|
m_pTopic->GetStateName(nid, state), bShowHistory);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (bSkipped)
|
||
|
{
|
||
|
CString strUnknownLongName = m_pTopic->GetNodePropItemStr(nid, H_ST_UKN_TXT_STR);
|
||
|
// The following test is per 11/11/99 email from John Locke
|
||
|
if (HideState(strUnknownLongName))
|
||
|
return; // totally omit Unknown from history table: Unknown cannot be
|
||
|
// selected for this node.
|
||
|
|
||
|
// Previous calls to AppendStateText have looped through the states known to BNTS;
|
||
|
// now we handle "skipped", which is a concept BNTS lacks.
|
||
|
CString strUnknown;
|
||
|
|
||
|
CreateUnknownButtonText(strUnknown);
|
||
|
AppendRadioButtonVisited(str, nid, ST_UNKNOWN, bSet, strUnknown, bShowHistory);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_pTopic->IsCauseNode(nid) && state == 1) // it is presumptive cause ...
|
||
|
{
|
||
|
if (IsInSniffedArray(nid)) //... taken from sniffed array, but NOT current node.
|
||
|
{
|
||
|
// We are about to add entry for presumptive cause node.
|
||
|
// Actually, since this is sniffed node, we need to have two entries:
|
||
|
// one hidden fiels with node name and one hidden field with node name
|
||
|
// prefixed by "SNIFFED" prefix.
|
||
|
if (bSet)
|
||
|
{
|
||
|
// "bSet" will always set to true, as sniffed presumptive cause will never
|
||
|
// be visible.
|
||
|
AppendRadioButtonVisited(str, nid, state, bSet, m_pTopic->GetStateName(nid, state), false);
|
||
|
AppendHiddenFieldSniffed(str, nid);
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AppendRadioButtonVisited(str, nid, state, bSet, m_pTopic->GetStateName(nid, state), bShowHistory);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// This is used to get the name of a node that has already been visited (for the
|
||
|
// history table).
|
||
|
// INPUT nid - node ID of desired node
|
||
|
// OUTPUT str - The "full name" of the node is appended to this, something like
|
||
|
// "Disable IBM AntiVirus" or "Make all paths less than 66 characters"
|
||
|
// If its value was sniffed, we append the appropriate string to mark it visibly
|
||
|
// as sniffed (typically, just "SNIFFED").
|
||
|
// INPUT bShowHistory
|
||
|
// If !bShowHistory, no appending: no need to show full name in a hidden table.
|
||
|
// Symbolic name will be written in a hidden field.
|
||
|
// Note that our CString, unlike MFC's, won't throw an exception on += out of memory
|
||
|
// RETURNS true if node number exists
|
||
|
bool CInfer::AppendVisitedNodeText(CString & str, NID nid, bool bShowHistory) const
|
||
|
{
|
||
|
if (!bShowHistory)
|
||
|
return true;
|
||
|
|
||
|
CString strTemp = m_pTopic->GetNodeFullName(nid);
|
||
|
if ( !strTemp.IsEmpty() )
|
||
|
{
|
||
|
str += strTemp;
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// Writing to the new HTML page. Representing the recommended node.
|
||
|
// This is what is often called the page, although it is really only part of
|
||
|
// the body of the HTML page, along with history.
|
||
|
// -------------------------------------------------------------------
|
||
|
|
||
|
// AppendImpossiblePage: Gets the body of text that is
|
||
|
// displayed when the network is in an unreliable state.
|
||
|
void CInfer::AppendImpossiblePage(CString & str)
|
||
|
{
|
||
|
CString strHeader, strText;
|
||
|
|
||
|
strHeader = m_pTopic->GetMultilineNetProp(HTK_IMPOSSIBLE_HEADER, _T("<H4> %s </H4>\n"));
|
||
|
strText = m_pTopic->GetMultilineNetProp(HTK_IMPOSSIBLE_TEXT , _T("%s "));
|
||
|
|
||
|
if (!strHeader.IsEmpty() && !strText.IsEmpty())
|
||
|
{
|
||
|
str = strHeader + strText + _T("<BR>\n<BR>\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strHeader = m_pTopic->GetMultilineNetProp(HX_FAIL_HD_STR , _T("<H4> %s </H4>\n"));
|
||
|
strText = m_pTopic->GetMultilineNetProp(HX_FAIL_TXT_STR , _T("%s "));
|
||
|
|
||
|
if (!strHeader.IsEmpty() && !strText.IsEmpty())
|
||
|
{
|
||
|
str = strHeader + strText + _T("<BR>\n<BR>\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
str = SZ_I_NO_RESULT_PAGE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make a radio button with name = NODE_IMPOSSIBLE & value = SZ_ST_WORKED
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HX_IMPOSSIBLE_NORM_STR);
|
||
|
if (strTemp.IsEmpty()) // fall back on Fail node's property
|
||
|
strTemp = m_pTopic->GetNetPropItemStr(HX_FAIL_NORM_STR);
|
||
|
if (!strTemp.IsEmpty())
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TABLE>";
|
||
|
|
||
|
AppendRadioButtonCurrentNode(str, NODE_IMPOSSIBLE, SZ_ST_WORKED, strTemp);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "</TABLE>\n";
|
||
|
}
|
||
|
|
||
|
str += _T("<P>");
|
||
|
AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
|
||
|
}
|
||
|
|
||
|
// AppendSniffAllCausesNormalPage: Gets the body of text that is displayed when sniffing
|
||
|
// on startup detects that all Cause nodes are in their Normal states.
|
||
|
void CInfer::AppendSniffAllCausesNormalPage(CString & str)
|
||
|
{
|
||
|
CString strHeader, strText;
|
||
|
|
||
|
strHeader = m_pTopic->GetMultilineNetProp(HTK_SNIFF_FAIL_HEADER, _T("<H4> %s </H4>\n"));
|
||
|
strText = m_pTopic->GetMultilineNetProp(HTK_SNIFF_FAIL_TEXT , _T("%s "));
|
||
|
|
||
|
if (!strHeader.IsEmpty() && !strText.IsEmpty())
|
||
|
{
|
||
|
str = strHeader + strText + _T("<BR>\n<BR>\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strHeader = m_pTopic->GetMultilineNetProp(HX_FAIL_HD_STR , _T("<H4> %s </H4>\n"));
|
||
|
strText = m_pTopic->GetMultilineNetProp(HX_FAIL_TXT_STR , _T("%s "));
|
||
|
|
||
|
if (!strHeader.IsEmpty() && !strText.IsEmpty())
|
||
|
{
|
||
|
str = strHeader + strText + _T("<BR>\n<BR>\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
str = SZ_I_NO_RESULT_PAGE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make a radio button with name = NODE_FAILALLCAUSESNORMAL & value = SZ_ST_WORKED
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HX_SNIFF_FAIL_NORM);
|
||
|
if (strTemp.IsEmpty()) // fall back on Fail node's property
|
||
|
strTemp = m_pTopic->GetNetPropItemStr(HX_FAIL_NORM_STR);
|
||
|
if (!strTemp.IsEmpty())
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TABLE>";
|
||
|
|
||
|
AppendRadioButtonCurrentNode(str, NODE_FAILALLCAUSESNORMAL, SZ_ST_WORKED, strTemp);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "</TABLE>\n";
|
||
|
}
|
||
|
|
||
|
str += _T("<P>");
|
||
|
AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
|
||
|
}
|
||
|
|
||
|
// OUTPUT str - string to which we are appending to build HTML page we send back.
|
||
|
// Append (to str) a group of radio buttons, one for each "problem" node in the Belief Network
|
||
|
void CInfer::AppendProblemPage(CString & str)
|
||
|
{
|
||
|
CString strTemp;
|
||
|
|
||
|
m_nidSelected = nidProblemPage;
|
||
|
|
||
|
// text to precede list of problems. Introduced 8/98 for version 3.0.
|
||
|
// space after %s in next line: see note at head of file
|
||
|
str += m_pTopic->GetMultilineNetProp(H_PROB_PAGE_TXT_STR, _T("%s "));
|
||
|
|
||
|
// write problem header. This is text written as HTML <H4>.
|
||
|
strTemp.Format(_T("<H4> %s </H4>\n\n"), m_pTopic->GetNetPropItemStr(H_PROB_HD_STR));
|
||
|
str += strTemp;
|
||
|
|
||
|
// Write a comment in the HTML in service of automated test program
|
||
|
str += _T("<!-- IDH = PROBLEM -->\n");
|
||
|
//str += "<BR>";
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TABLE>";
|
||
|
|
||
|
AppendProblemNodes(str);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n</TABLE>\n";
|
||
|
|
||
|
if (m_pTopic->UsesSniffer())
|
||
|
{
|
||
|
AppendActionButtons (str, k_BtnNext|k_BtnPPSniffing);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AppendActionButtons (str, k_BtnNext);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Helper routine for AppendProblemPage
|
||
|
void CInfer::AppendProblemNodes(CString & str)
|
||
|
{
|
||
|
vector<NID> arrnidNoSequence;
|
||
|
multimap<int, NID> mapSeqToNID;
|
||
|
|
||
|
// for every node in this Belief Network (but taking action only on "problem" nodes)
|
||
|
// put this nid in arrnidNoSequence if it has no sequence number or mapSeqToNID if it
|
||
|
// has one.
|
||
|
for(int nid = 0; nid < m_pTopic->CNode(); nid++)
|
||
|
{
|
||
|
if (m_pTopic->IsProblemNode(nid))
|
||
|
{
|
||
|
CString strSpecial = m_pTopic->GetNodePropItemStr(nid, H_PROB_SPECIAL);
|
||
|
// if it's not marked as a "hidden" problem, we'll want it in the problem page
|
||
|
if (strSpecial.CompareNoCase(_T("hide")) != 0)
|
||
|
{
|
||
|
CString str = m_pTopic->GetNodePropItemStr(nid, H_NODE_PROB_SEQUENCE);
|
||
|
try
|
||
|
{
|
||
|
if (str.IsEmpty())
|
||
|
arrnidNoSequence.push_back(nid);
|
||
|
else
|
||
|
mapSeqToNID.insert(pair<int, NID>(_ttoi(str), nid));
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (multimap<int, NID>::const_iterator ppair=mapSeqToNID.begin();
|
||
|
ppair != mapSeqToNID.end();
|
||
|
ppair++)
|
||
|
{
|
||
|
// Create a radio button with "ProblemAsk" as its name & this problem
|
||
|
// as its value
|
||
|
AppendRadioButtonCurrentNode(
|
||
|
str,
|
||
|
NODE_PROBLEM_ASK,
|
||
|
m_pTopic->GetNodeSymName(ppair->second),
|
||
|
m_pTopic->GetNodePropItemStr(ppair->second, H_PROB_TXT_STR));
|
||
|
}
|
||
|
|
||
|
|
||
|
for (vector<NID>::const_iterator pnid=arrnidNoSequence.begin();
|
||
|
pnid != arrnidNoSequence.end();
|
||
|
pnid++)
|
||
|
{
|
||
|
// Create a radio button with "ProblemAsk" as its name & this problem
|
||
|
// as its value
|
||
|
AppendRadioButtonCurrentNode(
|
||
|
str,
|
||
|
NODE_PROBLEM_ASK,
|
||
|
m_pTopic->GetNodeSymName(*pnid),
|
||
|
m_pTopic->GetNodePropItemStr(*pnid, H_PROB_TXT_STR));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append this network's "BYE" page to str
|
||
|
// OUTPUT str - string to append to
|
||
|
void CInfer::AppendByeMsg(CString & str)
|
||
|
{
|
||
|
str += _T("<!-- "BYE" (success) PAGE -->\n");
|
||
|
|
||
|
// Write a comment in the HTML in service of automated test program
|
||
|
str += _T("<!-- IDH = IDH_BYE -->\n");
|
||
|
|
||
|
// Write this troubleshooter's "Bye" header and text
|
||
|
// space after %s in next 2 lines: see note at head of file
|
||
|
AppendMultilineNetProp(str, HX_BYE_HD_STR, _T("<H4> %s </H4>\n"));
|
||
|
AppendMultilineNetProp(str, HX_BYE_TXT_STR, _T("%s "));
|
||
|
str += _T("<P>\n");
|
||
|
|
||
|
AppendActionButtons (str, k_BtnBack|k_BtnStartOver);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Append this network's "FAIL" page to str
|
||
|
// OUTPUT str - string to append to
|
||
|
void CInfer::AppendFailMsg(CString & str)
|
||
|
{
|
||
|
str += _T("<!-- "FAIL" PAGE -->\n");
|
||
|
|
||
|
// Write a comment in the HTML in service of automated test program
|
||
|
str += _T("<!-- IDH = IDH_FAIL -->\n");
|
||
|
|
||
|
// Write this topic's "Fail" header and text
|
||
|
// space after %s in next 2 lines: see note at head of file
|
||
|
AppendMultilineNetProp(str, HX_FAIL_HD_STR, _T("<H4> %s </H4>\n"));
|
||
|
AppendMultilineNetProp(str, HX_FAIL_TXT_STR, _T("%s "));
|
||
|
str += _T("<BR>\n<BR>\n");
|
||
|
|
||
|
// Make a radio button with name = NODE_FAIL & value = SZ_ST_WORKED
|
||
|
CString strTemp = m_pTopic->GetNetPropItemStr(HX_FAIL_NORM_STR);
|
||
|
if (!strTemp.IsEmpty())
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TABLE>";
|
||
|
|
||
|
AppendRadioButtonCurrentNode(str, NODE_FAIL, SZ_ST_WORKED, strTemp);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "</TABLE>\n";
|
||
|
}
|
||
|
|
||
|
AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Append content of the "service" page to str (Offers 2 possibilities: seek help elsewhere
|
||
|
// or go back and try something you skipped)
|
||
|
// OUTPUT str - string to append to
|
||
|
void CInfer::AppendServiceMsg(CString & str)
|
||
|
{
|
||
|
CString strTemp;
|
||
|
|
||
|
str += _T("<!-- "SERVICE" PAGE -->\n");
|
||
|
str += _T("<!-- Offers to seek help elsewhere or go back and try something you skipped -->\n");
|
||
|
// Write a comment in the HTML in service of automated test program
|
||
|
str += _T("<!-- IDH = SERVICE -->\n");
|
||
|
|
||
|
// Write this troubleshooter's "Service" header and text
|
||
|
// space after %s in next 2 lines: see note at head of file
|
||
|
AppendMultilineNetProp(str, HX_SER_HD_STR, _T("<H4> %s </H4>\n"));
|
||
|
AppendMultilineNetProp(str, HX_SER_TXT_STR, _T("%s "));
|
||
|
str += _T("<BR>\n<BR>\n");
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "\n<TABLE>";
|
||
|
|
||
|
// Make a radio button with name = Service & value = SZ_ST_WORKED
|
||
|
// Typical text is "I will try to get help elsewhere.";
|
||
|
strTemp = m_pTopic->GetNetPropItemStr(HX_SER_NORM_STR);
|
||
|
if (!strTemp.IsEmpty())
|
||
|
AppendRadioButtonCurrentNode(str, NODE_SERVICE, SZ_ST_WORKED, strTemp);
|
||
|
|
||
|
// Make a radio button with name = Service & value = SZ_ST_ANY
|
||
|
// Typical text is "Retry any steps that I have skipped."
|
||
|
strTemp = m_pTopic->GetNetPropItemStr(HX_SER_AB_STR);
|
||
|
if (!strTemp.IsEmpty())
|
||
|
AppendRadioButtonCurrentNode(str, NODE_SERVICE, SZ_ST_ANY, strTemp);
|
||
|
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
str += "</TABLE>\n";
|
||
|
|
||
|
str += _T("<P>");
|
||
|
|
||
|
AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Depending on the value of nid, this fn can build
|
||
|
// - a BYE page
|
||
|
// - a FAIL page
|
||
|
// - a SERVICE page
|
||
|
// - a page for a normal node (fixable/observable, fixable/unobservable, unfixable, or
|
||
|
// informational).
|
||
|
// If none of these cases apply, returns with no action taken
|
||
|
// INPUT nid - ID of a node
|
||
|
// OUTPUT str - string to append to
|
||
|
void CInfer::AppendNIDPage(NID nid, CString & str)
|
||
|
{
|
||
|
CString strTxt;
|
||
|
|
||
|
m_nidSelected = nid;
|
||
|
|
||
|
if (nid == nidByeNode)
|
||
|
AppendByeMsg(str);
|
||
|
else if (nid == nidFailNode)
|
||
|
AppendFailMsg(str);
|
||
|
else if (nid == nidSniffedAllCausesNormalNode)
|
||
|
AppendSniffAllCausesNormalPage(str);
|
||
|
else if (nid == nidService)
|
||
|
AppendServiceMsg(str);
|
||
|
else if (m_pTopic->IsValidNID(nid))
|
||
|
{
|
||
|
bool bShowManualSniffingButton = false;
|
||
|
|
||
|
if (m_pSniff)
|
||
|
if (nid != m_SniffedRecommendation.nid())
|
||
|
// we're NOT showing sniffed node.
|
||
|
bShowManualSniffingButton = m_pSniff->GetSniffController()->AllowManualSniffing(nid);
|
||
|
|
||
|
// Write a comment in the HTML in service of automated test program
|
||
|
str += _T("<!-- IDH = ");
|
||
|
str += m_pTopic->GetNodeSymName(nid);
|
||
|
str += _T(" -->\n");
|
||
|
|
||
|
// Write this node's header & text
|
||
|
// space after %s in next several lines: see note at head of file
|
||
|
AppendMultilineNodeProp(str, nid, H_NODE_HD_STR, _T("<H4> %s </H4>\n"));
|
||
|
if (bShowManualSniffingButton)
|
||
|
AppendMultilineNodeProp(str, nid, H_NODE_MANUAL_SNIFF_TEXT, _T("%s "));
|
||
|
if (m_SniffedRecommendation.nid() == nid)
|
||
|
{
|
||
|
CString tmp;
|
||
|
AppendMultilineNodeProp(tmp, nid, H_NODE_DCT_STR, _T("%s "));
|
||
|
if (tmp.IsEmpty())
|
||
|
AppendMultilineNodeProp(str, nid, H_NODE_TXT_STR, _T("%s "));
|
||
|
else
|
||
|
str += tmp;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AppendMultilineNodeProp(str, nid, H_NODE_TXT_STR, _T("%s "));
|
||
|
}
|
||
|
str += _T("\n<BR>\n<BR>\n");
|
||
|
|
||
|
// Write appropriate radio buttons depending on what kind of node it is.
|
||
|
if (m_pTopic->IsCauseNode(nid) || m_pTopic->IsInformationalNode(nid))
|
||
|
AppendCurrentRadioButtons(nid, str);
|
||
|
|
||
|
AppendActionButtons (
|
||
|
str,
|
||
|
k_BtnNext|k_BtnBack|k_BtnStartOver|(bShowManualSniffingButton ? k_BtnManualSniffing : 0),
|
||
|
nid);
|
||
|
}
|
||
|
// else nothing we can do with this
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// BES
|
||
|
// -------------------------------------------------------------------
|
||
|
|
||
|
// Historically:
|
||
|
// Returns true if we are supposed to show the full BES page (& let the user edit the
|
||
|
// search string) vs. extracting the search string & starting the search without
|
||
|
// any possible user intervention
|
||
|
// However, we no longer offer that option as of 981021.
|
||
|
bool CInfer::ShowFullBES()
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// returns true in the circumstances where we wish to show a Back End Search
|
||
|
bool CInfer::TimeForBES()
|
||
|
{
|
||
|
return (m_pTopic->HasBES() && m_bUseBackEndRedirection);
|
||
|
}
|
||
|
|
||
|
// If it is time to do a Back End Search redirection, append the "redirection" string
|
||
|
// to str and return true
|
||
|
// Otherwise, return false
|
||
|
// str should represent the header of an HTML page.
|
||
|
// For browsers which support redirection, this is how we overide service node (or fail node)
|
||
|
// when BES is present
|
||
|
bool CInfer::AppendBESRedirection(CString & str)
|
||
|
{
|
||
|
if (m_pTopic->HasBES() && TimeForBES() && !ShowFullBES() && !m_strEncodedForm.IsEmpty())
|
||
|
{
|
||
|
str += _T("Location: ");
|
||
|
str += m_strEncodedForm;
|
||
|
str += _T("\r\n");
|
||
|
return( true );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Append HTML representing BES to OUTPUT str and build m_strEncodedForm,
|
||
|
// This is a distinct new algorithm in Ver 3.0, replacing the old "word list" approach.
|
||
|
void CInfer::OutputBackend(CString & str)
|
||
|
{
|
||
|
vector<CString>arrstrSearch;
|
||
|
|
||
|
int nNodesInBasis = m_BasisForInference.size();
|
||
|
|
||
|
for (int i = 0; i<nNodesInBasis; i++)
|
||
|
{
|
||
|
NID nid = m_BasisForInference[i].nid();
|
||
|
IST state = m_BasisForInference[i].state();
|
||
|
|
||
|
CString strSearchState;
|
||
|
|
||
|
// First account for binary nodes w/ special property names
|
||
|
if (state == 0)
|
||
|
strSearchState = m_pTopic->GetNodePropItemStr(nid, H_NODE_NORM_SRCH_STR);
|
||
|
else if (state == 1)
|
||
|
strSearchState = m_pTopic->GetNodePropItemStr(nid, H_NODE_AB_SRCH_STR);
|
||
|
else
|
||
|
strSearchState = _T("");
|
||
|
|
||
|
if (strSearchState.IsEmpty())
|
||
|
// multistate node
|
||
|
strSearchState = m_pTopic->GetNodePropItemStr(nid, MUL_ST_SRCH_STR, state);
|
||
|
|
||
|
if (! strSearchState.IsEmpty())
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
arrstrSearch.push_back(strSearchState);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Build the full BES page
|
||
|
CString strRaw;
|
||
|
|
||
|
m_pTopic->GenerateBES(arrstrSearch, m_strEncodedForm, strRaw);
|
||
|
str += strRaw;
|
||
|
}
|
||
|
|
||
|
|
||
|
// -------------------------------------------------------------
|
||
|
// Logging
|
||
|
// -------------------------------------------------------------
|
||
|
|
||
|
|
||
|
// Return NID of page ultimately selected. If no such page, returns nidNil.
|
||
|
NID CInfer::NIDSelected() const
|
||
|
{
|
||
|
return m_nidSelected;
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------
|
||
|
// Effectively, a method on m_arrnidSkipped
|
||
|
// -------------------------------------------------------------
|
||
|
// INPUT nid
|
||
|
// RETURNS true if nid is node in the "skip list" (ST_UNKNOWN, "Try something else").
|
||
|
bool CInfer::IsSkipped(NID nid) const
|
||
|
{
|
||
|
vector<NID>::const_iterator itBegin= m_arrnidSkipped.begin();
|
||
|
vector<NID>::const_iterator itEnd= m_arrnidSkipped.end();
|
||
|
|
||
|
return (find(itBegin, itEnd, nid) != itEnd);
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------
|
||
|
// Buttons
|
||
|
// -------------------------------------------------------------
|
||
|
// appends only <INPUT TYPE=...> clause
|
||
|
void CInfer::AppendNextButton(CString & str) const
|
||
|
{
|
||
|
str += SZ_INPUT_TAG_NEXT; // _T("<INPUT tag=next TYPE=SUBMIT VALUE=\"")
|
||
|
AppendNextButtonText(str);
|
||
|
str += _T("\">");
|
||
|
}
|
||
|
|
||
|
// For local TS, appends only <INPUT TYPE=...> clause
|
||
|
// For Online TS, must build a pseudo button.
|
||
|
void CInfer::AppendStartOverButton(CString & str) const
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
{
|
||
|
str += SZ_INPUT_TAG_STARTOVER; // _T("<INPUT tag=startover TYPE=BUTTON VALUE=\"")
|
||
|
AppendStartOverButtonText(str);
|
||
|
str += _T("\" onClick=\"starter()\">");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Added for V3.2
|
||
|
CString strLabel; // visible label for pseudo button
|
||
|
|
||
|
AppendStartOverButtonText(strLabel);
|
||
|
|
||
|
AppendLinkAsButton(str, m_strStartOverLink, strLabel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// appends only <INPUT TYPE=...> clause
|
||
|
void CInfer::AppendBackButton(CString & str) const
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
{
|
||
|
str += SZ_INPUT_TAG_BACK; // _T("<INPUT tag=back TYPE=BUTTON VALUE=\"")
|
||
|
AppendBackButtonText(str);
|
||
|
str += _T("\" onClick=\"generated_previous()\">");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AppendManualSniffButton will generate script something like this, but this
|
||
|
// comment is not being carefully maintained, so see actual code for details.
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// function sniffManually() { //
|
||
|
// var stateSniffed = parent.t3.PerformSniffingJS("NodeName", "", "");//
|
||
|
// //
|
||
|
// if(stateSniffed == -1) { //
|
||
|
// stateSniffed = parent.t3.PerformSniffingVB("NodeName", "", "");//
|
||
|
// } //
|
||
|
// //
|
||
|
// if(stateSniffed == -1) { //
|
||
|
// alert("Could not sniff this node"); //
|
||
|
// } else { //
|
||
|
// if(stateSniffed > NumOfStates) { //
|
||
|
// alert("Could not sniff this node"); //
|
||
|
// } else { //
|
||
|
// /////////////////////////////////////////////////////// //
|
||
|
// IF IS CAUSE NODE: //
|
||
|
// if (stateSniffed == 1) //
|
||
|
// document.all.Sniffed_NodeName.value = 101; //
|
||
|
// else //
|
||
|
// document.all.Sniffed_NodeName.value = stateSniffed; //
|
||
|
// /////////////////////////////////////////////////////// //
|
||
|
// IF IS NOT CAUSE NODE: //
|
||
|
// document.all.Sniffed_NodeName.value = stateSniffed; //
|
||
|
// /////////////////////////////////////////////////////// //
|
||
|
// document.all.NodeState[stateSniffed].checked = true; //
|
||
|
// document.ButtonForm.onsubmit(); //
|
||
|
// } //
|
||
|
// } //
|
||
|
// } //
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
void CInfer::AppendManualSniffButton(CString & str, NID nid) const
|
||
|
{
|
||
|
if (RUNNING_LOCAL_TS())
|
||
|
{
|
||
|
CString strNodeName;
|
||
|
CString strTmp;
|
||
|
SymbolicFromNID(strNodeName, nid);
|
||
|
bool bIsCause = m_pTopic->IsCauseNode(nid);
|
||
|
|
||
|
str += _T(
|
||
|
"\n\n<script language=\"JavaScript\">\n"
|
||
|
"function sniffManually() {\n"
|
||
|
" var stateSniffed = parent.t3.PerformSniffingJavaScript(\"");
|
||
|
str += strNodeName;
|
||
|
str += _T(
|
||
|
"\", \"\", \"\");\n");
|
||
|
|
||
|
str += _T(
|
||
|
" if(stateSniffed == -1) {\n"
|
||
|
" stateSniffed = parent.t3.PerformSniffingVBScript(\"");
|
||
|
str += strNodeName;
|
||
|
str += _T(
|
||
|
"\", \"\", \"\");\n"
|
||
|
"}\n");
|
||
|
|
||
|
str += _T(
|
||
|
" if(stateSniffed == -1) {\n"
|
||
|
" alert(\"");
|
||
|
AppendSniffFailedText(str);
|
||
|
str += _T(
|
||
|
"\");\n"
|
||
|
" } else {\n"
|
||
|
" if(stateSniffed >");
|
||
|
|
||
|
CString strStates;
|
||
|
strStates.Format(_T("%d"), m_pTopic->GetCountOfStates(nid) -1);
|
||
|
str += strStates;
|
||
|
|
||
|
str += _T(
|
||
|
") {\n"
|
||
|
" alert(\"");
|
||
|
AppendSniffFailedText(str);
|
||
|
str += _T(
|
||
|
"\");\n"
|
||
|
" } else {\n");
|
||
|
if (bIsCause)
|
||
|
{
|
||
|
str += _T(
|
||
|
" if (stateSniffed == 1)\n"
|
||
|
" document.all.");
|
||
|
str += C_SNIFFTAG;
|
||
|
str += strNodeName;
|
||
|
str += _T(".value = ");
|
||
|
str += SZ_ST_WORKED;
|
||
|
str += _T(";\n");
|
||
|
str += _T(
|
||
|
" else\n");
|
||
|
}
|
||
|
str += _T(
|
||
|
" document.all.");
|
||
|
str += C_SNIFFTAG;
|
||
|
str += strNodeName;
|
||
|
str += _T(".value = stateSniffed;\n");
|
||
|
|
||
|
str += _T(
|
||
|
" document.all.");
|
||
|
str += C_LAST_SNIFFED_MANUALLY;
|
||
|
str += _T(".value = ");
|
||
|
str += SZ_ST_SNIFFED_MANUALLY_TRUE;
|
||
|
str += _T(";\n");
|
||
|
|
||
|
|
||
|
str += _T(
|
||
|
" document.all.");
|
||
|
str += strNodeName;
|
||
|
str += _T(
|
||
|
"[stateSniffed].checked = true;\n");
|
||
|
|
||
|
str += _T(
|
||
|
" document.ButtonForm.onsubmit();\n");
|
||
|
|
||
|
str += _T(
|
||
|
" }\n"
|
||
|
" }\n"
|
||
|
"}\n"
|
||
|
"</script>\n\n");
|
||
|
|
||
|
str += _T(
|
||
|
"<INPUT tag=sniff TYPE=BUTTON VALUE=\"");
|
||
|
AppendManualSniffButtonText(str);
|
||
|
str += _T(
|
||
|
"\" onClick=\"sniffManually()\">\n");
|
||
|
|
||
|
str += _T(
|
||
|
"<INPUT type=\"HIDDEN\" name=\"");
|
||
|
str += C_SNIFFTAG;
|
||
|
str += strNodeName;
|
||
|
str += _T("\" value=\"");
|
||
|
strTmp.Format(_T("%d"), SNIFF_FAILURE_RESULT);
|
||
|
str += strTmp;
|
||
|
str += _T("\">\n");
|
||
|
|
||
|
str += _T(
|
||
|
"<INPUT type=\"HIDDEN\" name=\"");
|
||
|
str += C_LAST_SNIFFED_MANUALLY;
|
||
|
str += _T("\" value=\"");
|
||
|
str += SZ_ST_SNIFFED_MANUALLY_FALSE;
|
||
|
str += _T("\">\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// appends only <INPUT TYPE=...> clause
|
||
|
void CInfer::AppendPPSnifferButton(CString & str) const
|
||
|
{
|
||
|
str += SZ_INPUT_TAG_SNIFFER; // _T("<INPUT tag=sniffer TYPE=BUTTON VALUE=\"")
|
||
|
AppendPPSnifferButtonText(str);
|
||
|
str += _T("\" onClick=\"runtest()\">");
|
||
|
}
|
||
|
|
||
|
|
||
|
void CInfer::AppendActionButtons(CString & str, ActionButtonSet btns, NID nid /*=-1*/) const
|
||
|
{
|
||
|
// Online TS's Start Over "button" is actually a link, and will implicitly
|
||
|
// start a new line unless we do something about it.
|
||
|
bool bGenerateTable = (!RUNNING_LOCAL_TS() && (btns & k_BtnStartOver));
|
||
|
|
||
|
if (bGenerateTable)
|
||
|
str += _T("<TABLE><tr><td>");
|
||
|
|
||
|
if (btns & k_BtnNext)
|
||
|
{
|
||
|
AppendNextButton(str);
|
||
|
str += _T("\n");
|
||
|
}
|
||
|
|
||
|
if (btns & k_BtnBack)
|
||
|
{
|
||
|
AppendBackButton(str);
|
||
|
str += _T("\n");
|
||
|
}
|
||
|
|
||
|
if (bGenerateTable)
|
||
|
str += _T("</td><td>");
|
||
|
|
||
|
|
||
|
if (btns & k_BtnStartOver)
|
||
|
{
|
||
|
AppendStartOverButton(str);
|
||
|
str += _T("\n");
|
||
|
}
|
||
|
|
||
|
if (bGenerateTable)
|
||
|
str += _T("</td><td>");
|
||
|
|
||
|
if (btns & k_BtnPPSniffing)
|
||
|
{
|
||
|
AppendPPSnifferButton(str);
|
||
|
str += _T("\n");
|
||
|
}
|
||
|
|
||
|
if ((btns & k_BtnManualSniffing) && nid != -1)
|
||
|
{
|
||
|
AppendManualSniffButton(str, nid);
|
||
|
str += _T("\n");
|
||
|
}
|
||
|
|
||
|
if (bGenerateTable)
|
||
|
str += _T("</td></tr></TABLE>");
|
||
|
|
||
|
str += _T("<BR><BR>");
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------
|
||
|
// MISCELLANY
|
||
|
// -------------------------------------------------------------
|
||
|
|
||
|
// RETURN true for cause (vs. informational or problem) node.
|
||
|
// Note that a cause may be either a fixable node or an "unfixable" node which
|
||
|
// "can be fixed with infinite effort"
|
||
|
/* static */ bool CInfer::IsCause (ESTDLBL lbl)
|
||
|
{
|
||
|
return (lbl == ESTDLBL_fixobs || lbl == ESTDLBL_fixunobs || lbl == ESTDLBL_unfix);
|
||
|
}
|
||
|
|
||
|
|
||
|
// This code can take a previously skipped node and bring it back again as a recommendation.
|
||
|
// It is relevant only if the user received the service node in the previous call
|
||
|
// to the DLL and now wants to see if there is "Anything Else I Can Try".
|
||
|
//
|
||
|
// This code will remove the first node from the skip list so that it may be delivered to
|
||
|
// the user again.
|
||
|
//
|
||
|
// Of course, m_arrnidSkipped, m_arrnidVisited must be filled in before this is called.
|
||
|
//
|
||
|
void CInfer::RecycleSkippedNode()
|
||
|
{
|
||
|
// Only should take effect once per instance of this object, because peels the first
|
||
|
// entry off of m_arrnidSkipped. We guarantee that with the following:
|
||
|
if (m_bRecyclingInitialized)
|
||
|
return;
|
||
|
|
||
|
m_bRecyclingInitialized = true;
|
||
|
|
||
|
// Only relevant if the query asks for a previously skipped node brought back again
|
||
|
// as a recommendation.
|
||
|
if (!m_bRecycleSkippedNode)
|
||
|
return;
|
||
|
|
||
|
// This is a safety check to bail out if there are no skipped nodes.
|
||
|
// This would be a bogus query, because the Service Node should only have been
|
||
|
// offered if there were skipped recommendations to try.
|
||
|
if (m_arrnidSkipped.empty())
|
||
|
{
|
||
|
m_bRecycleSkippedNode = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// OK,now down to business.
|
||
|
|
||
|
// Get a value for m_nidRecycled from the first item skipped
|
||
|
m_nidRecycled = m_arrnidSkipped.front();
|
||
|
|
||
|
// Remove skipped item from skip table
|
||
|
m_arrnidSkipped.erase(m_arrnidSkipped.begin());
|
||
|
|
||
|
// Fix table of nodes that will be placed into the output table
|
||
|
// to not include the first node skipped
|
||
|
|
||
|
vector<NID>::const_iterator itnidBegin = m_arrnidVisited.begin();
|
||
|
vector<NID>::const_iterator itnidEnd = m_arrnidVisited.end();
|
||
|
vector<NID>::const_iterator itnidAnythingElse = find(itnidBegin, itnidEnd, m_nidRecycled);
|
||
|
|
||
|
if (itnidAnythingElse != itnidEnd)
|
||
|
m_arrnidVisited.erase( const_cast<vector<NID>::iterator>(itnidAnythingElse) );
|
||
|
}
|
||
|
|
||
|
bool CInfer::ManuallySniffedNodeExists() const
|
||
|
{
|
||
|
// If last element in m_BasisForInference is sniffed,
|
||
|
// it means, that this element was set by manual sniffing
|
||
|
// function.
|
||
|
if (m_BasisForInference.size() && m_SniffedStates.size())
|
||
|
return m_bLastSniffedManually;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool CInfer::IsManuallySniffedNode(NID nid) const
|
||
|
{
|
||
|
if (ManuallySniffedNodeExists())
|
||
|
return nid == m_SniffedStates[m_SniffedStates.size()-1].nid();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CInfer::SetLastSniffedManually(bool set)
|
||
|
{
|
||
|
m_bLastSniffedManually = set;
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// CInfer::CArrayOrderRestorer implementation
|
||
|
// -------------------------------------------------------------------
|
||
|
//
|
||
|
// CInfer::CArrayOrderRestorer exists so that after re-sniffing, we can restore an array
|
||
|
// of visited nodes to its original order, as saved in m_arrInitial.
|
||
|
//
|
||
|
// INPUT: nBaseLength = number of elements in fixed locations at head of array, which will
|
||
|
// never be moved (typically nodes explicitly set by user rather than sniffed).
|
||
|
// INPUT/OUTPUT: arrToRestore = array to restore: input dictates content of output, but
|
||
|
// (beyond nBaseLength) does not dictate the order. Order comes from m_arrInitial.
|
||
|
// OUTPUT: arrToRestore = array with restored order
|
||
|
// RETURN: true if successful
|
||
|
bool CInfer::CArrayOrderRestorer::Restore(long nBaseLength, vector<NID>& arrToRestore)
|
||
|
{
|
||
|
if (nBaseLength > arrToRestore.size())
|
||
|
return false;
|
||
|
|
||
|
long i;
|
||
|
vector<NID>::iterator i_base;
|
||
|
vector<NID>::iterator i_additional;
|
||
|
vector<NID> arrBase;
|
||
|
vector<NID> arrAdditional;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
for (i = 0; i < nBaseLength; i++)
|
||
|
arrBase.push_back(arrToRestore[i]);
|
||
|
|
||
|
for (i = nBaseLength; i < arrToRestore.size(); i++)
|
||
|
arrAdditional.push_back(arrToRestore[i]);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
arrToRestore.clear();
|
||
|
|
||
|
for (i = 0, i_base = arrBase.begin();
|
||
|
i < m_arrInitial.size();
|
||
|
i++)
|
||
|
{
|
||
|
if (arrBase.end() != find(arrBase.begin(), arrBase.end(), m_arrInitial[i]))
|
||
|
{
|
||
|
if (i_base != arrBase.end())
|
||
|
i_base++;
|
||
|
}
|
||
|
else if (arrAdditional.end() != (i_additional = find(arrAdditional.begin(), arrAdditional.end(), m_arrInitial[i])))
|
||
|
{
|
||
|
i_base = arrBase.insert(i_base, m_arrInitial[i]);
|
||
|
i_base++;
|
||
|
arrAdditional.erase(i_additional);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arrToRestore = arrBase;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
for (i = 0; i < arrAdditional.size(); i++)
|
||
|
arrToRestore.push_back(arrAdditional[i]);
|
||
|
}
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|