Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2401 lines
75 KiB

  1. //
  2. // MODULE: APGTSINF.CPP
  3. //
  4. // PURPOSE: Inference Engine Interface
  5. // Completely implement class CInfer. VERY IMPORTANT STUFF!
  6. // One of these is created for each user request
  7. // Some utility functions at end of file.
  8. //
  9. // PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
  10. //
  11. // COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
  12. //
  13. // AUTHOR: Roman Mach, Joe Mabel
  14. //
  15. // ORIGINAL DATE: 8-2-96
  16. //
  17. // NOTES:
  18. // 1. Many methods in this class could be const if BNTS had more appropriate use of const
  19. // 2. Several places in this file you will see a space after %s in the format passed to
  20. // CInfer::AppendMultilineNetProp() or CInfer::AppendMultilineNodeProp(). This is the
  21. // upshot of some 12/98 correspondence between Microsoft and Saltmine. Many older DSC files
  22. // were built with a tool that could not handle more than 255 characters in a string.
  23. // The DSC feil format's "Array of string" was used to build up longer strings. Newer
  24. // DSC files (and all Argon-produced DSC files) should use only the first element of this
  25. // array.
  26. // The older DSC files assumed that the separate strings would effectively be separated
  27. // by white space, so we must maintain that situation.
  28. // 3. >>> $MAINT - exception-handling strategy for push_back and other memory allocation
  29. // functions is really overkill. If we run out of memory, we're screwed anyway. Really
  30. // would suffice to handle try/catch just at the main function of the thread.
  31. //
  32. // Version Date By Comments
  33. //--------------------------------------------------------------------
  34. // V0.1 - RM Original
  35. // V3.0 7-21-98 JM Major revision, deprecate IDH.
  36. // 8-27-98 JM Totally new method of communicating with template
  37. //
  38. #pragma warning(disable:4786)
  39. #include "stdafx.h"
  40. #include "event.h"
  41. #include "apgts.h"
  42. #include "apgtsinf.h"
  43. #include "apgtsmfc.h"
  44. #include "apgtsassert.h"
  45. #include "CharConv.h"
  46. #include "maxbuf.h"
  47. #include <algorithm>
  48. #include <vector>
  49. #include <map>
  50. #include "Sniff.h"
  51. #include "SniffController.h"
  52. #ifdef LOCAL_TROUBLESHOOTER
  53. #include "SniffLocal.h"
  54. #endif
  55. // -------------------------------------------------------------------
  56. // Constructor/Destructor, other initialization
  57. // -------------------------------------------------------------------
  58. //
  59. // INPUT *pCtxt is a buffer for building the string to pass back over the net.
  60. CInfer::CInfer(CSniffConnector* pSniffConnector) :
  61. #ifdef LOCAL_TROUBLESHOOTER
  62. m_pSniff(new CSniffLocal(pSniffConnector, NULL)),
  63. #else
  64. m_pSniff(NULL),
  65. #endif
  66. m_nidProblem(nidNil),
  67. m_bDone(false),
  68. m_bRecOK (false),
  69. m_SniffedRecommendation(nidNil, SNIFF_FAILURE_RESULT),
  70. m_bUseBackEndRedirection(false),
  71. m_bRecycleSkippedNode(false),
  72. m_nidRecycled(0),
  73. m_bRecyclingInitialized(false),
  74. m_nidSelected(nidNil),
  75. m_bLastSniffedManually(false)
  76. {
  77. }
  78. //
  79. //
  80. CInfer::~CInfer()
  81. {
  82. delete m_pSniff;
  83. }
  84. // The intention is that this be called only once.
  85. // It would be ideal if this were part of the constructor, but the CTopic * is not
  86. // yet available at time of construction.
  87. // The expectation is that this should be called before calling any other function. (Some
  88. // are technically OK to call, but it's smartest not to rely on that.)
  89. void CInfer::SetTopic(CTopic *pTopic)
  90. {
  91. m_pTopic = pTopic;
  92. if (m_pSniff)
  93. m_pSniff->SetTopic(pTopic);
  94. }
  95. // This fn exists so APGTSContext can access *m_pSniff to tell it what the sniffing
  96. // policies are.
  97. CSniff* CInfer::GetSniff()
  98. {
  99. return m_pSniff;
  100. }
  101. // -------------------------------------------------------------------
  102. // First, we set the states of nodes, based on the query string we got from the HTML form
  103. // -------------------------------------------------------------------
  104. // Convert IDH to NID. Needed on some old query string formats
  105. // "Almost vestigial", still supported in v3.2, but will be dropped in v4.0.
  106. NID CInfer::NIDFromIDH(IDH idh) const
  107. {
  108. if (idh == m_pTopic->CNode() + idhFirst)
  109. return nidProblemPage;
  110. if (idh == nidService + idhFirst)
  111. return nidService;
  112. if (idh == IDH_FAIL)
  113. return nidFailNode;
  114. if (idh == IDH_BYE)
  115. return nidByeNode;
  116. ASSERT (idh >= idhFirst);
  117. return idh - idhFirst;
  118. }
  119. // Associate a state with a node.
  120. // INPUT nid
  121. // INPUT ist - Normally, index of a state for that node.
  122. // If nid == nidProblemPage, then ist is actually NID of selected problem
  123. void CInfer::SetNodeState(NID nid, IST ist)
  124. {
  125. if (nid == nidNil)
  126. return;
  127. CString strTemp;
  128. CString strTxt;
  129. if (ist == ST_WORKED)
  130. {
  131. if (nid == nidFailNode || nid == nidSniffedAllCausesNormalNode
  132. || nid == nidService || nid == nidImpossibleNode)
  133. {
  134. if (m_pTopic->HasBES())
  135. {
  136. m_bUseBackEndRedirection = true;
  137. CString strThrowaway; // we don't really care about this string;
  138. // we call OutputBackend strictly for the side
  139. // effect of setting m_strEncodedForm.
  140. OutputBackend(strThrowaway);
  141. return;
  142. }
  143. }
  144. m_bDone = true;
  145. AddToBasisForInference(nid, ist); // this node still needs to be present
  146. // in m_arrBasisForInference, as it is
  147. // present in m_SniffedStates.
  148. // Add to the visited array to be displayed in the visible history page. RAB-20000628.
  149. try
  150. {
  151. m_arrnidVisited.push_back( nid );
  152. }
  153. catch (exception& x)
  154. {
  155. CString str;
  156. // Note STL exception in event log.
  157. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  158. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  159. SrcLoc.GetSrcFileLineStr(),
  160. CCharConversion::ConvertACharToString(x.what(), str),
  161. _T(""),
  162. EV_GTS_STL_EXCEPTION );
  163. }
  164. return;
  165. }
  166. if (ist == ST_ANY)
  167. {
  168. // We rely on the fact that only a service node offers ST_ANY
  169. // ("Is there anything else I can try?")
  170. m_bRecycleSkippedNode = true;
  171. return;
  172. }
  173. // We should never have service node go past this point (always ST_WORKED or ST_ANY).
  174. if (nid == nidByeNode || nid == nidFailNode || nid == nidSniffedAllCausesNormalNode)
  175. return;
  176. if (ist == ST_UNKNOWN)
  177. {
  178. // Add it to the list of skipped nodes & visited nodes
  179. try
  180. {
  181. m_arrnidSkipped.push_back(nid);
  182. m_arrnidVisited.push_back(nid);
  183. }
  184. catch (exception& x)
  185. {
  186. CString str;
  187. // Note STL exception in event log.
  188. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  189. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  190. SrcLoc.GetSrcFileLineStr(),
  191. CCharConversion::ConvertACharToString(x.what(), str),
  192. _T(""),
  193. EV_GTS_STL_EXCEPTION );
  194. }
  195. return;
  196. }
  197. if (nid == nidProblemPage)
  198. {
  199. if (!IsProblemNode(ist))
  200. {
  201. // Totally bogus query. Arbitrary course of action.
  202. m_bRecycleSkippedNode = true;
  203. return;
  204. }
  205. // Change this around to the way we would express it for any other node.
  206. nid = ist;
  207. ist = 1; // Set this problem node to a state value of 1 (in fact, we never
  208. // explicitly set problem nodes to state value of 0)
  209. m_nidProblem = nid; // special case: here instead of in m_arrnidVisited
  210. AddToBasisForInference(nid, ist);
  211. return;
  212. }
  213. AddToBasisForInference(nid, ist);
  214. // Store into our list of nodes obtained from the user
  215. try
  216. {
  217. m_arrnidVisited.push_back(nid);
  218. }
  219. catch (exception& x)
  220. {
  221. CString str;
  222. // Note STL exception in event log.
  223. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  224. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  225. SrcLoc.GetSrcFileLineStr(),
  226. CCharConversion::ConvertACharToString(x.what(), str),
  227. _T(""),
  228. EV_GTS_STL_EXCEPTION );
  229. }
  230. }
  231. void CInfer::AddToBasisForInference(NID nid, IST ist)
  232. {
  233. try
  234. {
  235. m_BasisForInference.push_back(CNodeStatePair(nid, ist));
  236. }
  237. catch (exception& x)
  238. {
  239. CString str;
  240. // Note STL exception in event log.
  241. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  242. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  243. SrcLoc.GetSrcFileLineStr(),
  244. CCharConversion::ConvertACharToString(x.what(), str),
  245. _T(""),
  246. EV_GTS_STL_EXCEPTION );
  247. }
  248. }
  249. // Add to the list of (previously) sniffed nodes.
  250. void CInfer::AddToSniffed(NID nid, IST ist)
  251. {
  252. try
  253. {
  254. if (ist == ST_WORKED && m_pTopic->IsCauseNode(nid))
  255. { // in case of cause node in abnormal state (which is ST_WORKED)
  256. // we need to set state to "1" as if it was sniffed.
  257. // This situation happens during manual sniffing of cause node that worked.
  258. ist = 1;
  259. }
  260. m_SniffedStates.push_back(CNodeStatePair(nid, ist));
  261. }
  262. catch (exception& x)
  263. {
  264. CString str;
  265. // Note STL exception in event log.
  266. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  267. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  268. SrcLoc.GetSrcFileLineStr(),
  269. CCharConversion::ConvertACharToString(x.what(), str),
  270. _T(""),
  271. EV_GTS_STL_EXCEPTION );
  272. }
  273. }
  274. // Be careful not to call this redundantly: its call to CTopic::GetRecommendations()
  275. // is expensive.
  276. void CInfer::GetRecommendations()
  277. {
  278. // if we haven't previously sought a recommendation...
  279. if ( m_SniffedRecommendation.nid() != nidNil )
  280. {
  281. // The one and only relevant recommendation is already forced, so don't bother
  282. // getting recommendations.
  283. // m_SniffedRecommendation.nid() is a Cause node in its abnormal state
  284. m_Recommendations.empty();
  285. try
  286. {
  287. m_Recommendations.push_back(m_SniffedRecommendation.nid());
  288. m_bRecOK = true;
  289. }
  290. catch (exception& x)
  291. {
  292. CString str;
  293. // Note STL exception in event log.
  294. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  295. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  296. SrcLoc.GetSrcFileLineStr(),
  297. CCharConversion::ConvertACharToString(x.what(), str),
  298. _T(""),
  299. EV_GTS_STL_EXCEPTION );
  300. }
  301. }
  302. else
  303. {
  304. // Pass data into m_pTopic
  305. // Get back recomendations.
  306. int status = m_pTopic->GetRecommendations(m_BasisForInference, m_Recommendations);
  307. m_bRecOK = (status == CTopic::RS_OK);
  308. }
  309. }
  310. // returns true if nid is a problem node of this network
  311. bool CInfer::IsProblemNode(NID nid) const
  312. {
  313. // get data array of problem nodes
  314. vector<NID>* parrnid = NULL;
  315. m_pTopic->GetProblemArray(parrnid);
  316. vector<NID>::const_iterator itnidBegin = parrnid->begin();
  317. vector<NID>::const_iterator itnidEnd = parrnid->end();
  318. vector<NID>::const_iterator itnidProblem = find(itnidBegin, itnidEnd, nid);
  319. if (itnidProblem == itnidEnd)
  320. return false;
  321. else
  322. return true;
  323. }
  324. bool CInfer::IsInSniffedArray(NID nid) const
  325. {
  326. UINT nSniffedNodes = m_SniffedStates.size();
  327. for (UINT i = 0; i < nSniffedNodes; i++)
  328. {
  329. if (m_SniffedStates[i].nid() == nid)
  330. {
  331. // Do not have to check for state, as m_SniffedStates will
  332. // have only valid states (states, which are accepted by BNTS),
  333. // no 102 or -1 states
  334. return true;
  335. }
  336. }
  337. return false;
  338. }
  339. // -------------------------------------------------------------------
  340. // For writing the new page after inference: the following texts are
  341. // invariant for a given topic (aka network).
  342. // -------------------------------------------------------------------
  343. // CreateUnknownButtonText: Reads the network property for the
  344. // unknown-state radio button from the network dsc file.
  345. // Puts value in strUnknown
  346. // This is specific to the radio button for "unknown" in the history table,
  347. // that is, for a node which has previously been visited. This should not be
  348. // used for the radio button for the "unknown" state of the present node.
  349. void CInfer::CreateUnknownButtonText(CString & strUnknown) const
  350. {
  351. strUnknown = m_pTopic->GetNetPropItemStr(HTK_UNKNOWN_RBTN);
  352. if (strUnknown.IsEmpty())
  353. strUnknown = SZ_UNKNOWN;
  354. return;
  355. }
  356. // AppendNextButtonText: Reads the network property for the
  357. // NEXT button from the network dsc file and append it to str.
  358. void CInfer::AppendNextButtonText(CString & str) const
  359. {
  360. CString strTemp = m_pTopic->GetNetPropItemStr(HTK_NEXT_BTN);
  361. if (strTemp.IsEmpty())
  362. strTemp = SZ_NEXT_BTN;
  363. str += strTemp;
  364. return;
  365. }
  366. // AppendNextButtonText: Reads the network property for the
  367. // NEXT button from the network dsc file and append it to str.
  368. void CInfer::AppendStartOverButtonText(CString & str) const
  369. {
  370. CString strTemp = m_pTopic->GetNetPropItemStr(HTK_START_BTN);
  371. if (strTemp.IsEmpty())
  372. strTemp = SZ_START_BTN;
  373. str += strTemp;
  374. return;
  375. }
  376. // AppendBackButtonText: Reads the network property for the
  377. // BACK button from the network dsc file and append it to str.
  378. void CInfer::AppendBackButtonText(CString & str) const
  379. {
  380. CString strTemp = m_pTopic->GetNetPropItemStr(HTK_BACK_BTN);
  381. if (strTemp.IsEmpty())
  382. strTemp = SZ_BACK_BTN;
  383. str += strTemp;
  384. return;
  385. }
  386. // AppendPPSnifferButtonText: Reads the network property for the
  387. // sniffer button from the network dsc file.
  388. // NOTE that this button is related to "expensive" sniffing only.
  389. // Appends to str.
  390. void CInfer::AppendPPSnifferButtonText(CString & str) const
  391. {
  392. CString strTemp = m_pTopic->GetNetPropItemStr(HTK_SNIF_BTN);
  393. if (strTemp.IsEmpty())
  394. strTemp = SZ_PP_SNIF_BTN;
  395. str += strTemp;
  396. }
  397. // AppendManualSniffButtonText: Reads the network property for the
  398. // manual sniff button from the network dsc file.
  399. // Appends to str.
  400. void CInfer::AppendManualSniffButtonText(CString & str) const
  401. {
  402. CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_TEXT_SNIFF_ONE_NODE);
  403. if (strTemp.IsEmpty())
  404. strTemp = SZ_SNIFF_ONE_NODE;
  405. str += strTemp;
  406. }
  407. // AppendHistTableSniffedText: Reads the network property for the
  408. // indication in history table that a node was sniffed.
  409. // Appends to str.
  410. void CInfer::AppendHistTableSniffedText(CString & str) const
  411. {
  412. CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_HIST_TABLE_SNIFFED_TEXT);
  413. if (strTemp.IsEmpty())
  414. strTemp = SZ_HIST_TABLE_SNIFFED_TEXT;
  415. str+= _T("<BR>\n");
  416. str += strTemp;
  417. }
  418. // AppendAllowSniffingText: Reads the network property for the
  419. // label of the AllowSniffing checkbox from the network dsc file.
  420. // Appends to str.
  421. void CInfer::AppendAllowSniffingText(CString & str) const
  422. {
  423. CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_ALLOW_SNIFFING_TEXT);
  424. if (strTemp.IsEmpty())
  425. strTemp = SZ_ALLOW_SNIFFING_TEXT;
  426. str += strTemp;
  427. }
  428. // AppendSniffFailedText: Reads the network property for the
  429. // alert box to be used when manual sniffing fails from the network dsc file.
  430. // Appends to str.
  431. void CInfer::AppendSniffFailedText(CString & str) const
  432. {
  433. CString strTemp = m_pTopic->GetNetPropItemStr(H_NET_TEXT_SNIFF_ALERT_BOX);
  434. if (strTemp.IsEmpty())
  435. strTemp = SZ_SNIFF_FAILED;
  436. str += strTemp;
  437. }
  438. // Appends an HTML link but makes it look like an HTML Form Button
  439. // useful for Start Over in Online TS, because with no idea what browser user will have,
  440. // we can't usefully use an onClick method (not supported in older browsers).
  441. // Online TS runs in a "no scripting" environment.
  442. // Pure HTML doesn't provide a means to put both a "Next" and a "Start Over" button
  443. // in the same HTML form. Conversely, if Start Over btn was outside the form, pure HTML
  444. // doesn't provide a means to align it with a button in the form.
  445. // Note that x.gif does not exist: its absence creates a 1-pixel placeholder.
  446. // >>>$MAINT We may want to change some of the rowspans to better emulate the exact size
  447. // of a button; try to make it look perfect under IE
  448. void CInfer::AppendLinkAsButton(
  449. CString & str,
  450. const CString & strTarget,
  451. const CString & strLabel) const
  452. {
  453. str += _T("<!-- Begin pseudo button -->"
  454. "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n"
  455. "<tr>\n"
  456. " <td rowspan=\"6\" bgcolor=\"white\">\n"
  457. " <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
  458. " <td colspan=\"3\" bgcolor=\"white\">\n"
  459. " <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
  460. "</tr>\n"
  461. "<tr>\n"
  462. " <td bgcolor=\"#C0C0C0\">\n"
  463. " <img src=\"x.gif\" width=\"1\" height=\"3\"></td>\n"
  464. " <td rowspan=\"4\" bgcolor=\"#808080\">\n"
  465. " <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
  466. " <td rowspan=\"4\" bgcolor=\"#000000\">\n"
  467. " <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
  468. "</tr>\n"
  469. "<tr>\n"
  470. " <td bgcolor=\"#C0C0C0\">\n");
  471. // >>> $MAINT might want to change the font/style in the following
  472. str += _T("<font face=\"Arial\" size=\"2\">&nbsp;&nbsp;&nbsp;\n"
  473. " <a href=\"");
  474. str += strTarget;
  475. str += _T("\" style=\"text-decoration:none; color:black\">\n"
  476. " <font color=\"black\">");
  477. str += strLabel;
  478. str += _T("</font></a>\n"
  479. " &nbsp;&nbsp;&nbsp;</font></td>\n"
  480. "</tr>\n"
  481. "<tr>\n"
  482. " <td bgcolor=\"#C0C0C0\">\n"
  483. " <img src=\"x.gif\" width=\"1\" height=\"3\"></td>\n"
  484. "</tr>\n"
  485. "<tr>\n"
  486. " <td bgcolor=\"#808080\">\n"
  487. " <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
  488. "</tr>\n"
  489. "<tr>\n"
  490. " <td colspan=\"3\" bgcolor=\"#000000\">\n"
  491. " <img src=\"x.gif\" width=\"1\" height=\"1\"></td>\n"
  492. "</tr>\n"
  493. "</table>\n"
  494. "<!-- End pseudo button -->\n");
  495. }
  496. // -------------------------------------------------------------------
  497. // Writing to the new HTML page. Miscellaneous low-level pieces.
  498. // -------------------------------------------------------------------
  499. // If the state name is missing or is simply "<hide>", return true.
  500. // This indicates a state that should never be overtly presented to the user as a choice.
  501. // Typically used in an informational node, this may describe a state that can be deduced with
  502. // 100% certainty from certain other node/state combinations.
  503. /* static */ bool CInfer::HideState(LPCTSTR szStateName)
  504. {
  505. if (szStateName && *szStateName && _tcscmp(szStateName, _T("<hide>") ) )
  506. return false;
  507. return true;
  508. }
  509. // write a symbolic name (based on NID) to a string sz
  510. // INPUT nid - node ID
  511. // OUTPUT str - the string to which we write.
  512. // RETURNS true if successful
  513. // NOTE that this restores the "current" node when it is finished.
  514. // Alternative would be side effect of setting current node (by omitting nidOld), but that
  515. // would work strangely on "special" nodes (e.g. Service, Fail), which aren't in BNTS.
  516. bool CInfer::SymbolicFromNID(CString & str, NID nid) const
  517. {
  518. if (nid == nidProblemPage)
  519. {
  520. str= NODE_PROBLEM_ASK;
  521. return true;
  522. }
  523. if (nid == nidService)
  524. {
  525. str= NODE_SERVICE;
  526. return true;
  527. }
  528. if (nid == nidFailNode)
  529. {
  530. str= NODE_FAIL;
  531. return true;
  532. }
  533. if (nid == nidSniffedAllCausesNormalNode)
  534. {
  535. str= NODE_FAILALLCAUSESNORMAL;
  536. return true;
  537. }
  538. if (nid == nidImpossibleNode)
  539. {
  540. str= NODE_IMPOSSIBLE;
  541. return true;
  542. }
  543. if (nid == nidByeNode)
  544. {
  545. str= NODE_BYE;
  546. return true;
  547. }
  548. // if it's a "normal" node, this will fill in the name
  549. str= m_pTopic->GetNodeSymName(nid);
  550. return (!str.IsEmpty() );
  551. }
  552. // append an HTML radio button to str
  553. // INPUT/OUTPUT str - the string to which we append
  554. // INPUT szName, szValue - For <INPUT TYPE=RADIO NAME=szName VALUE=szValue>
  555. // INPUT szLabel - text to appear after the radio button but before a line break
  556. /*static*/ void CInfer::AppendRadioButtonCurrentNode(
  557. CString &str, LPCTSTR szName, LPCTSTR szValue, LPCTSTR szLabel, bool bChecked/*= false*/)
  558. {
  559. CString strTxt;
  560. if ( ! HideState(szLabel))
  561. {
  562. if (RUNNING_LOCAL_TS())
  563. str += "\n<TR>\n<TD>\n";
  564. strTxt.Format(_T("<INPUT TYPE=RADIO NAME=\"%s\" VALUE=\"%s\" %s> %s"),
  565. szName, szValue, bChecked ? _T("CHECKED") : _T(""), szLabel);
  566. str += strTxt;
  567. if (RUNNING_LOCAL_TS())
  568. str += "\n</TD>\n</TR>\n";
  569. else
  570. str += "\n<BR>\n";
  571. }
  572. }
  573. // This is different than other radio buttons because it
  574. // - has a different format for label szLabel.
  575. // - vanishes if bShowHistory is false and this button isn't CHECKED
  576. // - turns into a hidden field if bShowHistory is false and this button is CHECKED
  577. // - writes SNIFFED_ values as applicable...although that's not in this function: it's
  578. // handled in a separate call to AppendHiddenFieldSniffed()
  579. // JM 11/12/99 previously, we special-cased hidden states here. However, per 11/11/99 email
  580. // from John Locke, the only state we ever hide in the History Table (for v3.2) is the
  581. // Unknown/skipped state, and that is handled elsewhere.
  582. // INPUT/OUTPUT str - string to which we append the HTML for this button.
  583. // INPUT nid - NID of node
  584. // INPUT value - state
  585. // INPUT bSet - true ==> button is CHECKED
  586. // INPUT szctype - short name of the state
  587. // INPUT bShowHistory - see explanation a few lines above
  588. void CInfer::AppendRadioButtonVisited(
  589. CString &str, NID nid, UINT value, bool bSet, LPCTSTR szLabel, bool bShowHistory) const
  590. {
  591. CString strTxt;
  592. CString strSymbolic;
  593. SymbolicFromNID(strSymbolic, nid);
  594. if (bShowHistory)
  595. strTxt.Format(_T("<INPUT TYPE=RADIO NAME=%s VALUE=%u%s>%-16s \n"),
  596. strSymbolic, value, bSet ? _T(" CHECKED") : _T(""), szLabel);
  597. else if (bSet)
  598. strTxt.Format(_T("<INPUT TYPE=HIDDEN NAME=%s VALUE=%u>\n"),
  599. strSymbolic, value);
  600. str += strTxt;
  601. }
  602. // If this nid is an already sniffed node, then we append this fact as a
  603. // "hidden" value in the HTML in str.
  604. // For example, if a node with symbolic name FUBAR has been sniffed in state 1,
  605. // we will append "<INPUT TYPE=HIDDEN NAME=SNIFFED_FUBAR VALUE=1>\n"
  606. // INPUT: string to have appended; node ID
  607. // OUTPUT: string with appended hidden field if node was sniffed
  608. // RETURN: true id string is appended
  609. void CInfer::AppendHiddenFieldSniffed(CString &str, NID nid) const
  610. {
  611. CString strSymbolic;
  612. UINT nSniffedNodes = m_SniffedStates.size();
  613. SymbolicFromNID(strSymbolic, nid);
  614. for (UINT i = 0; i < nSniffedNodes; i++)
  615. {
  616. if (m_SniffedStates[i].nid() == nid)
  617. {
  618. // Do not have to check for state, as m_SniffedStates will
  619. // have only valid states (states, which are accepted by BNTS),
  620. // no 102 or -1 states
  621. // In case that this is manually sniffed cause node in abnormal state
  622. // (and we just re-submit previous page), we need not mention
  623. // this node as sniffed.
  624. if (!(IsManuallySniffedNode(nid) &&
  625. m_SniffedStates[i].state() == 1 &&
  626. m_pTopic->IsCauseNode(nid))
  627. )
  628. {
  629. CString strTxt;
  630. strTxt.Format(_T("<INPUT TYPE=HIDDEN NAME=%s%s VALUE=%u>\n"),
  631. C_SNIFFTAG, strSymbolic, m_SniffedStates[i].state());
  632. str += strTxt;
  633. return;
  634. }
  635. }
  636. }
  637. }
  638. // Appends (to str) info conveying whether Automatic Sniffing is allowed.
  639. void CInfer::AddAllowAutomaticSniffingHiddenField(CString &str) const
  640. {
  641. CString strTxt;
  642. strTxt.Format(_T("<INPUT TYPE=HIDDEN NAME=%s VALUE=%s>\n"),
  643. C_ALLOW_AUTOMATIC_SNIFFING_NAME, C_ALLOW_AUTOMATIC_SNIFFING_CHECKED);
  644. str += strTxt;
  645. }
  646. // Radio buttons for currently recommended node
  647. // Each button will appear only if appropriate string property is defined
  648. // Accounts for multi-state or simple binary node.
  649. // INPUT nid - identifies a node of an appropriate type
  650. // INPUT/OUTPUT str - string to which we are appending to build HTML page we send back.
  651. // The detailed behavior of this function was changed at John Locke's request 11/30/98 for V3.0.
  652. // Then for v3.1, handling of H_ST_AB_TXT_STR, H_ST_NORM_TXT_STR removed 8/19/99 per request
  653. // from John Locke & Alex Sloley
  654. void CInfer::AppendCurrentRadioButtons(NID nid, CString & str)
  655. {
  656. CString strSymbolic;
  657. SymbolicFromNID(strSymbolic, nid);
  658. CString strPropLongName; // long name of property
  659. int nStates = m_pTopic->GetCountOfStates(nid);
  660. if (RUNNING_LOCAL_TS())
  661. str += "\n<TABLE>";
  662. for (IST state=0; state < nStates; state ++)
  663. {
  664. TCHAR szStateNumber[MAXBUF]; // buffer for _itot()
  665. CString strDisplayState = _itot( state, szStateNumber, 10 );
  666. if (state == 1 && m_pTopic->IsCauseNode( nid ))
  667. strDisplayState = SZ_ST_WORKED;
  668. strPropLongName = _T("");
  669. if (strPropLongName.IsEmpty())
  670. // account for multistate node
  671. strPropLongName = m_pTopic->GetNodePropItemStr(nid, MUL_ST_LONG_NAME_STR, state);
  672. // if we're not past the end of states, append a button
  673. if (!strPropLongName.IsEmpty())
  674. AppendRadioButtonCurrentNode(str,
  675. strSymbolic,
  676. strDisplayState,
  677. strPropLongName,
  678. // check state button if this state was sniffed
  679. m_SniffedRecommendation.state() == state ? true : false);
  680. };
  681. // "unknown" state (e.g. "I want to skip this")
  682. strPropLongName = m_pTopic->GetNodePropItemStr(nid, H_ST_UKN_TXT_STR);
  683. if (!strPropLongName.IsEmpty())
  684. AppendRadioButtonCurrentNode(str, strSymbolic, SZ_ST_UNKNOWN, strPropLongName);
  685. if (RUNNING_LOCAL_TS())
  686. str += "</TABLE>\n";
  687. return;
  688. }
  689. // If we are showing the history table, place a localizable Full Name
  690. // (e.g."Printouts appear garbled") of problem and a hidden-data
  691. // field corresponding to this problem into str.
  692. // Otherwise, just the hidden data field
  693. void CInfer::CreateProblemVisitedText(CString & str, NID nidProblem, bool bShowHistory)
  694. {
  695. // This code is structured in pieces as sending all of these strings to a single
  696. // CString::Format() results in a program exception. Did some research into this
  697. // behavior but did not discover anything. RAB-981014.
  698. CString tmpStr;
  699. tmpStr.Format( _T("%s"), bShowHistory ? m_pTopic->GetNodeFullName(nidProblem) : _T("") );
  700. str= tmpStr;
  701. tmpStr.Format( _T("<INPUT TYPE=HIDDEN NAME=%s "), NODE_PROBLEM_ASK );
  702. str+= tmpStr;
  703. tmpStr.Format( _T("VALUE=%s>"), m_pTopic->GetNodeSymName(nidProblem) );
  704. str+= tmpStr;
  705. tmpStr.Format( _T("%s"), bShowHistory ? _T("") : _T("\n") );
  706. str+= tmpStr;
  707. str+= _T("\n");
  708. }
  709. // Append a NET property (for Belief Network as a whole, not for one
  710. // particular node) to str.
  711. // INPUT/OUTPUT str - string to append to
  712. // INPUT item - Property name
  713. // INPUT szFormat - string to format each successive line. Should contain one %s, otherwise
  714. // constant text.
  715. void CInfer::AppendMultilineNetProp(CString & str, LPCTSTR szPropName, LPCTSTR szFormat)
  716. {
  717. str += m_pTopic->GetMultilineNetProp(szPropName, szFormat);
  718. }
  719. // Like AppendMultilineNetProp, but for a NODE property item, for one particular node.
  720. // INPUT/OUTPUT str - string to append to
  721. // INPUT item - Property name
  722. // INPUT szFormat - string to format each successive line. Should contain one %s, otherwise
  723. // constant text.
  724. void CInfer::AppendMultilineNodeProp(CString & str, NID nid, LPCTSTR szPropName, LPCTSTR szFormat)
  725. {
  726. str += m_pTopic->GetMultilineNodeProp(nid, szPropName, szFormat);
  727. }
  728. // JSM V3.2 Wrapper for AppendMultilineNetProp to make it easier
  729. // to fill in the Net properties in HTMLFragments
  730. CString CInfer::ConvertNetProp(const CString &strNetPropName)
  731. {
  732. CString strNetPropVal;
  733. AppendMultilineNetProp(strNetPropVal,strNetPropName,"%s");
  734. return strNetPropVal;
  735. }
  736. // If there is a pre-sniffed recommendation, remove it from the list & set m_SniffedRecommendation.
  737. void CInfer::IdentifyPresumptiveCause()
  738. {
  739. vector<NID> arrnidNoSequence;
  740. multimap<int, NID> mapSeqToNID;
  741. // Find all presumptive causes
  742. for (int i = 0; i < m_SniffedStates.size(); i++)
  743. {
  744. if (m_pTopic->IsCauseNode(m_SniffedStates[i].nid()) // cause node ...
  745. &&
  746. m_SniffedStates[i].state() == 1) // ... that is sniffed in abnormal (1) state
  747. {
  748. if (IsManuallySniffedNode(m_SniffedStates[i].nid()))
  749. {
  750. // now we have manually sniffed cause node in abnormal state.
  751. // It means that we are re-submitting the page. We will set m_SniffedRecommendation
  752. // to this node, and return.
  753. m_SniffedRecommendation = CNodeStatePair(m_SniffedStates[i].nid(), 1 /*cause node abnormal state*/);
  754. return;
  755. }
  756. NID nid = m_SniffedStates[i].nid();
  757. CString str = m_pTopic->GetNodePropItemStr(nid, H_NODE_CAUSE_SEQUENCE);
  758. try
  759. {
  760. if (str.IsEmpty())
  761. arrnidNoSequence.push_back(nid);
  762. else
  763. {
  764. mapSeqToNID.insert(pair<int, NID>(_ttoi(str), nid));
  765. }
  766. }
  767. catch (exception& x)
  768. {
  769. CString str;
  770. // Note STL exception in event log.
  771. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  772. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  773. SrcLoc.GetSrcFileLineStr(),
  774. CCharConversion::ConvertACharToString(x.what(), str),
  775. _T(""),
  776. EV_GTS_STL_EXCEPTION );
  777. }
  778. }
  779. }
  780. // We want the first in sequence according to H_NODE_CAUSE_SEQUENCE numbering.
  781. // If nothing has a number, we settle for the (arbitrary) first in the array of
  782. // unnumbered Cause nodes.
  783. if (mapSeqToNID.size() > 0)
  784. m_SniffedRecommendation = CNodeStatePair( (mapSeqToNID.begin()->second), 1 /*cause node abnormal state*/);
  785. else if (arrnidNoSequence.size() > 0)
  786. m_SniffedRecommendation = CNodeStatePair( *(arrnidNoSequence.begin()), 1 /*cause node abnormal state*/);
  787. // now remove the matching nid from the incoming arrays
  788. if (m_SniffedRecommendation.nid() != nidNil)
  789. {
  790. for (i = 0; i < m_BasisForInference.size(); i++)
  791. {
  792. if (m_BasisForInference[i].nid() == m_SniffedRecommendation.nid())
  793. {
  794. m_BasisForInference.erase(m_BasisForInference.begin() + i);
  795. break;
  796. }
  797. }
  798. for (i = 0; i < m_SniffedStates.size(); i++)
  799. {
  800. if (m_SniffedStates[i].nid() == m_SniffedRecommendation.nid())
  801. {
  802. m_SniffedStates.erase(m_SniffedStates.begin() + i);
  803. break;
  804. }
  805. }
  806. for (i = 0; i < m_arrnidVisited.size(); i++)
  807. {
  808. if (m_arrnidVisited[i] == m_SniffedRecommendation.nid())
  809. {
  810. m_arrnidVisited.erase(m_arrnidVisited.begin() + i);
  811. break;
  812. }
  813. }
  814. }
  815. }
  816. // return true if every Cause node in the topic is determined to be normal;
  817. // this would imply that there is nothing useful this topic can do for us.
  818. bool CInfer::AllCauseNodesNormal()
  819. {
  820. // for every node in this Belief Network (but taking action only on "cause" nodes)
  821. // see if each of these is known to be Normal
  822. for(int nid = 0; nid < m_pTopic->CNode(); nid++)
  823. {
  824. if (m_pTopic->IsCauseNode(nid))
  825. {
  826. bool bFound=false;
  827. for (CBasisForInference::iterator p= m_SniffedStates.begin();
  828. p != m_SniffedStates.end();
  829. ++p)
  830. {
  831. if (p->nid() == nid)
  832. {
  833. if (p->state() != 0)
  834. // found a Cause node in an abnormal state (or skipped)
  835. return false;
  836. bFound = true;
  837. break;
  838. }
  839. }
  840. if (!bFound)
  841. // found a Cause node for which no state is set
  842. return false;
  843. }
  844. }
  845. return true;
  846. }
  847. // -------------------------------------------------------------
  848. // Writing pieces of the new HTML page. This builds a structure to be used under HTI
  849. // control to represent the recommended node and the (visible or invisible) history table.
  850. // -------------------------------------------------------------
  851. void CInfer::FillInHTMLFragments(CHTMLFragmentsTS &frag)
  852. {
  853. vector<NID>arrnidPresumptiveCause;
  854. // First, a side effect: get the URL for the Online TS Start Over link / pseudo-button
  855. m_strStartOverLink = frag.GetStartOverLink();
  856. // Then on to the main business at hand. In practice (at least as of 11/99)
  857. // bIncludesHistoryTable and bIncludesHiddenHistory are mutually exclusive,
  858. // but this class doesn't need that knowledge.
  859. const bool bIncludesHistoryTable = frag.IncludesHistoryTable();
  860. const bool bIncludesHiddenHistory = frag.IncludesHiddenHistory();
  861. {
  862. // JSM V3.2: convert the net properties in the HTML fragment
  863. // The HTI template may indicate that certain net properties are to be written
  864. // directly into the resulting page. We get a list of these properties and
  865. // fill in a structrue in frag to contain their values.
  866. CString strNetPropName;
  867. for(;frag.IterateNetProp(strNetPropName);)
  868. frag.SetNetProp(strNetPropName,ConvertNetProp(strNetPropName));
  869. }
  870. {
  871. // JM V3.2 to handle sniffing correctly, must do this before history table: sniffing
  872. // on the fly (which happens in AppendCurrentNodeText()) could add to the history.
  873. CString strCurrentNode;
  874. AppendCurrentNodeText(strCurrentNode);
  875. frag.SetCurrentNodeText(strCurrentNode);
  876. }
  877. CString strHiddenHistory;
  878. if (m_nidProblem != nidNil)
  879. {
  880. CString strProblem;
  881. CreateProblemVisitedText(strProblem, m_nidProblem, frag.IncludesHistoryTable());
  882. // OK V3.2 We use hidden field to save the value returned by the "AllowSniffing"
  883. // checkbox (on the problem page) and pass it to each subsequent page.
  884. // We effectively place this before the history table.
  885. if (m_pSniff)
  886. if (m_pSniff->GetAllowAutomaticSniffingPolicy())
  887. AddAllowAutomaticSniffingHiddenField(strProblem);
  888. // Added for V3.2 sniffing
  889. // Not lovely, but this is where we insert sniffed presumptive causes (as hidden
  890. // fields).
  891. // >>> $MAINT Once we integrate with a launcher, this may require
  892. // further thought: what if we sniff presumptive causes before we have an
  893. // identified problem? Where do we put those hidden fields?
  894. for (UINT i=0; i<m_arrnidVisited.size(); i++)
  895. {
  896. NID nid = m_arrnidVisited[i];
  897. int stateSet = SNIFF_FAILURE_RESULT;
  898. {
  899. UINT nSetNodes = m_SniffedStates.size();
  900. for (UINT ii = 0; ii < nSetNodes; ii++)
  901. if (m_SniffedStates[ii].nid() == nid) {
  902. stateSet = m_SniffedStates[ii].state();
  903. break;
  904. }
  905. }
  906. if (m_pTopic->IsCauseNode(nid) && stateSet == 1)
  907. {
  908. // This is a cause node sniffed as abnormal, to be presented eventually
  909. // as a "presumptive" cause. All we put in the History table is hidden
  910. AppendStateText(strProblem, nid, 1, true, false, false, stateSet);
  911. try
  912. {
  913. arrnidPresumptiveCause.push_back(nid);
  914. }
  915. catch (exception& x)
  916. {
  917. CString str;
  918. // Note STL exception in event log.
  919. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  920. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  921. SrcLoc.GetSrcFileLineStr(),
  922. CCharConversion::ConvertACharToString(x.what(), str),
  923. _T(""),
  924. EV_GTS_STL_EXCEPTION );
  925. }
  926. }
  927. }
  928. if (bIncludesHistoryTable)
  929. frag.SetProblemText(strProblem);
  930. if (bIncludesHiddenHistory)
  931. strHiddenHistory = strProblem;
  932. }
  933. UINT nVisitedNodes = m_arrnidVisited.size();
  934. // iVisited incremented for every visited node, iHistory only for a subset:
  935. // if we have a visible History table, iHistory provides an index of nodes visible
  936. // to the end user. If not, iHistory is a harmless irrelevance
  937. for (UINT iVisited=0, iHistory=0; iVisited<nVisitedNodes; iVisited++)
  938. {
  939. NID nid = m_arrnidVisited[iVisited];
  940. int nStates = m_pTopic->GetCountOfStates(nid);
  941. int stateSet = -1;
  942. if (IsSkipped(nid))
  943. {
  944. // "skipped" node.
  945. // instead of ST_UNKNOWN (==102), stateSet uses the number immediately
  946. // past the last valid state of this node. Most nodes have only states
  947. // 0, 1, and 102 so typically stateSet is set = 2, but a multistate
  948. // node can use a different number
  949. stateSet = nStates;
  950. }
  951. else
  952. {
  953. UINT nSetNodes = m_BasisForInference.size();
  954. for (UINT ii = 0; ii < nSetNodes; ii++)
  955. if (m_BasisForInference[ii].nid() == nid) {
  956. stateSet = m_BasisForInference[ii].state();
  957. break;
  958. }
  959. }
  960. // The following test added for V3.2 sniffing
  961. // Weed out cause node sniffed as abnormal, to be presented eventually
  962. // as a "presumptive" cause. Handled above as a hidden field.
  963. if (find(arrnidPresumptiveCause.begin(), arrnidPresumptiveCause.end(), nid)
  964. != arrnidPresumptiveCause.end())
  965. {
  966. // cause node sniffed as abnormal
  967. }
  968. else
  969. {
  970. if (bIncludesHistoryTable)
  971. {
  972. CString strVisitedNode;
  973. AppendVisitedNodeText(strVisitedNode, nid, true);
  974. frag.PushBackVisitedNodeText(strVisitedNode);
  975. }
  976. for (UINT iState=0; iState <= nStates; iState++)
  977. {
  978. if (bIncludesHistoryTable)
  979. {
  980. CString strState;
  981. AppendStateText(strState, nid, iState, iState == stateSet,
  982. iState == nStates, true, stateSet);
  983. // If we are processing last state, and we need to attach
  984. // hidden field for this node as sniffed one
  985. // (if it ts really sniffed)
  986. if (iState == nStates)
  987. AppendHiddenFieldSniffed(strState, nid);
  988. // We need not have empty entry in CHTMLFragment's array,
  989. // describing history table, so by applying "numPresumptiveCauseNodesEncounered"
  990. // we make this array continuous
  991. frag.PushBackStateText(iHistory, strState);
  992. }
  993. if (bIncludesHiddenHistory)
  994. {
  995. AppendStateText(strHiddenHistory, nid, iState, iState == stateSet,
  996. iState == nStates, false, stateSet);
  997. // same as in case of visible history table applies.
  998. if (iState == nStates)
  999. AppendHiddenFieldSniffed(strHiddenHistory, nid);
  1000. }
  1001. }
  1002. if (bIncludesHistoryTable)
  1003. {
  1004. // Check if we need to mark this as visibly sniffed.
  1005. UINT nSniffedNodes = m_SniffedStates.size();
  1006. for (UINT i = 0; i < nSniffedNodes; i++)
  1007. {
  1008. if (m_SniffedStates[i].nid() == nid)
  1009. {
  1010. // mark it visibly as sniffed
  1011. CString strState;
  1012. AppendHistTableSniffedText( strState );
  1013. frag.PushBackStateText(iHistory, strState);
  1014. break;
  1015. }
  1016. }
  1017. }
  1018. iHistory++;
  1019. }
  1020. }
  1021. if (frag.IncludesHiddenHistory())
  1022. frag.SetHiddenHistoryText(strHiddenHistory);
  1023. frag.SetSuccessBool(m_bDone);
  1024. }
  1025. // Append the text for the current (recommended) node to str
  1026. void CInfer::AppendCurrentNodeText(CString & str)
  1027. {
  1028. CString strSave = str;
  1029. if (m_nidProblem == nidNil)
  1030. // show first page (radio-button list of possible problems)
  1031. AppendProblemPage(str);
  1032. else if (m_bDone && !ManuallySniffedNodeExists())
  1033. AppendNIDPage(nidByeNode, str);
  1034. else if ( m_SniffedRecommendation.nid() != nidNil )
  1035. // we already have a recommendation, presumably from a sniffer
  1036. AppendNIDPage(m_SniffedRecommendation.nid(), str);
  1037. else
  1038. {
  1039. // sniff/resniff all, as needed
  1040. if (RUNNING_LOCAL_TS())
  1041. {
  1042. // Before we mess with m_BasisForInference, determine if the only node with a
  1043. // state is the problem node
  1044. // [BC - 20010301] - Added check for size of skipped node count when setting
  1045. // bHaveOnlyProblem here. This catches case where user selects to skip first
  1046. // node presented, when that node is sniffed in abnormal state.
  1047. bool bHaveOnlyProblem = (m_BasisForInference.size() == 1) &&
  1048. (m_arrnidSkipped.size() == 0);
  1049. if (m_pSniff)
  1050. {
  1051. long nExplicitlySetByUser = 0;
  1052. CBasisForInference arrManuallySniffed; // can contain max 1 element;
  1053. // used to prevent resniffing
  1054. // of already sniffed node.
  1055. // We need arrayOrderRestorer in order to make sure that when sniffed
  1056. // nodes are first removed from the array of visited nodes, then restored,
  1057. // we maintain the same sequence in which nodes were visited in the first
  1058. // place. This order is important in our caching strategy and also provides
  1059. // a sense of consistency for the end user.
  1060. CArrayOrderRestorer arrayOrderRestorer(m_arrnidVisited);
  1061. if (ManuallySniffedNodeExists())
  1062. {
  1063. arrManuallySniffed.push_back(m_SniffedStates[m_SniffedStates.size()-1]);
  1064. }
  1065. // Remove all sniffed nodes from m_BasisForInference
  1066. m_BasisForInference -= m_SniffedStates;
  1067. // remove m_SniffedStates from m_arrnidVisited
  1068. m_arrnidVisited -= m_SniffedStates;
  1069. nExplicitlySetByUser = m_arrnidVisited.size();
  1070. if (bHaveOnlyProblem)
  1071. {
  1072. // sniff all since we're in problem page
  1073. m_pSniff->SniffAll(m_SniffedStates);
  1074. }
  1075. else
  1076. {
  1077. CBasisForInference arrSniffed;
  1078. // resniff all except recently sniffed manually (if any)
  1079. arrSniffed = m_SniffedStates;
  1080. arrSniffed -= arrManuallySniffed;
  1081. m_pSniff->Resniff(arrSniffed);
  1082. arrSniffed += arrManuallySniffed;
  1083. m_SniffedStates = arrSniffed;
  1084. }
  1085. // add updated m_SniffedStates to m_arrnidVisited
  1086. m_arrnidVisited += m_SniffedStates;
  1087. arrayOrderRestorer.Restore(nExplicitlySetByUser, m_arrnidVisited);
  1088. // Add all sniffed nodes into m_BasisForInference
  1089. m_BasisForInference += m_SniffedStates;
  1090. if (bHaveOnlyProblem && AllCauseNodesNormal())
  1091. {
  1092. // We just sniffed at startup & we already know all Cause nodes
  1093. // are in their normal states. There is absolutely nothing this
  1094. // troubleshooting topic can do to help this user.
  1095. AppendSniffAllCausesNormalPage(str);
  1096. return;
  1097. }
  1098. }
  1099. // in case that we do not have sniffed recommendation from manual sniffing
  1100. if (m_SniffedRecommendation.nid() == nidNil)
  1101. {
  1102. // Did we get a presumptive cause out of that?
  1103. IdentifyPresumptiveCause();
  1104. }
  1105. if ( m_SniffedRecommendation.nid() != nidNil )
  1106. {
  1107. AppendNIDPage(m_SniffedRecommendation.nid(), str);
  1108. return;
  1109. }
  1110. }
  1111. bool bSniffSucceeded = true;
  1112. while (bSniffSucceeded)
  1113. {
  1114. IST state = -1;
  1115. NID nidNew = nidNil;
  1116. GetRecommendations();
  1117. if (!m_bRecOK)
  1118. {
  1119. str = strSave;
  1120. AppendImpossiblePage(str);
  1121. return;
  1122. }
  1123. else if (m_Recommendations.empty())
  1124. {
  1125. str = strSave;
  1126. AppendNIDPage(nidFailNode, str);
  1127. return;
  1128. }
  1129. else // Have Recommendations
  1130. {
  1131. // Find a recommendation from list of recommendations that is
  1132. // not in the skip list. This is normally the first node in the
  1133. // list.
  1134. int n = m_Recommendations.size();
  1135. for (UINT i=0; i<n; i++)
  1136. {
  1137. if (!IsSkipped(m_Recommendations[i]))
  1138. {
  1139. nidNew = m_Recommendations[i];
  1140. str = strSave;
  1141. AppendNIDPage(nidNew, str);
  1142. break; // out of for loop: just one recommendation is actually
  1143. // reported back to user.
  1144. }
  1145. }
  1146. // It is our first pass, no sniffed node pages
  1147. // were composed earlier in this loop
  1148. if (nidNew == nidNil)
  1149. {
  1150. // We fell though if the entire list of recommendations has been skipped
  1151. // via "ST_UNKNOWN" selection by the user.
  1152. if (m_bRecycleSkippedNode)
  1153. RecycleSkippedNode(); // this can affect m_bRecycleSkippedNode
  1154. if (m_bRecycleSkippedNode)
  1155. {
  1156. // The user got the service node earlier and now wants to review
  1157. // the nodes they marked "Unknown". We already removed the first
  1158. // "Unknown" node from the skip list and put its NID in
  1159. // m_nidRecycled. Now we just do a normal display of the page
  1160. // for that node.
  1161. nidNew = m_nidRecycled;
  1162. str = strSave;
  1163. AppendNIDPage(nidNew, str);
  1164. return;
  1165. }
  1166. else if (!m_arrnidSkipped.empty())
  1167. {
  1168. // We've got "Unknowns", they weren't just in the service page,
  1169. // so give 'em the service page
  1170. str = strSave;
  1171. AppendNIDPage(nidService, str);
  1172. return;
  1173. }
  1174. else
  1175. {
  1176. // no unknowns. Fail. Believed never to arise here, but coded
  1177. // this way for safety.
  1178. str = strSave;
  1179. AppendNIDPage(nidFailNode, str);
  1180. return;
  1181. }
  1182. }
  1183. }
  1184. bSniffSucceeded = false;
  1185. // sniffing on the fly
  1186. if (m_pSniff)
  1187. bSniffSucceeded = m_pSniff->SniffNode(nidNew, &state);
  1188. if (bSniffSucceeded)
  1189. {
  1190. // if it's a cause node and was sniffed as abnormal
  1191. if (m_pTopic->IsCauseNode(nidNew) && state == 1)
  1192. {
  1193. // Display this page as a presumptive cause.
  1194. m_SniffedRecommendation = CNodeStatePair( nidNew, state );
  1195. str = strSave;
  1196. AppendNIDPage(nidNew, str);
  1197. return;
  1198. }
  1199. CNodeStatePair nodestateNew(nidNew, state);
  1200. try
  1201. {
  1202. m_SniffedStates.push_back(nodestateNew);
  1203. m_BasisForInference.push_back(nodestateNew);
  1204. m_arrnidVisited.push_back(nidNew);
  1205. }
  1206. catch (exception& x)
  1207. {
  1208. CString str;
  1209. // Note STL exception in event log.
  1210. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1211. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1212. SrcLoc.GetSrcFileLineStr(),
  1213. CCharConversion::ConvertACharToString(x.what(), str),
  1214. _T(""),
  1215. EV_GTS_STL_EXCEPTION );
  1216. }
  1217. }
  1218. }
  1219. }
  1220. }
  1221. // Write radio buttons describing what was decided by user in a previous node. Part
  1222. // of "the table" (aka "the visited node table" or "table of previous responses").
  1223. //
  1224. // Note that Cause nodes are specially handled. On a cause node:
  1225. // state 0 = "no, this didn't fix it"
  1226. // state 1 = This wasn't OK, so we have a diagnosis. In that case, we
  1227. // wouldn't be displaying these radio buttons.
  1228. // We don't want the user selecting that value from the history table.
  1229. // We append this only if it's been sniffed and must be presented as a presumptive
  1230. // cause, and even then we always append it "hidden"
  1231. // state 2 = "skipped"
  1232. //
  1233. // In other words, on a cause node, the only possibilities we offer to the user through
  1234. // a visible history table are state 0 () and "skip".
  1235. //
  1236. // In the case where a Cause node has been sniffed abnormal, THE CALLING ROUTINE is
  1237. // responsible to call this only for the abnormal state. Otherwise, call for all states.
  1238. //
  1239. // OUTPUT str - string to which we append
  1240. // INPUT nid node of which this is a state
  1241. // INPUT state state number; for ST_UNKNOWN, this is the count of states, not 102
  1242. // INPUT bSet true = this is the current state of this node
  1243. // INPUT bSkipped true = this is the "skipped" state, not a normal node state known to BNTS
  1244. // INPUT bShowHistory true = we are showing a history table, false = history is stored
  1245. // invisibly in the HTML.
  1246. void CInfer::AppendStateText(CString & str, NID nid, UINT state, bool bSet, bool bSkipped,
  1247. bool bShowHistory, int nStateSet)
  1248. {
  1249. // Check if this selection worked.
  1250. // If so only display the "it worked" text in the history table.
  1251. if (m_pTopic->IsCauseNode(nid) && nStateSet == ST_WORKED)
  1252. {
  1253. if (state == 1) // it is presumptive cause ...
  1254. AppendRadioButtonVisited( str, nid, state, true,
  1255. m_pTopic->GetStateName(nid, state), bShowHistory);
  1256. return;
  1257. }
  1258. if (bSkipped)
  1259. {
  1260. CString strUnknownLongName = m_pTopic->GetNodePropItemStr(nid, H_ST_UKN_TXT_STR);
  1261. // The following test is per 11/11/99 email from John Locke
  1262. if (HideState(strUnknownLongName))
  1263. return; // totally omit Unknown from history table: Unknown cannot be
  1264. // selected for this node.
  1265. // Previous calls to AppendStateText have looped through the states known to BNTS;
  1266. // now we handle "skipped", which is a concept BNTS lacks.
  1267. CString strUnknown;
  1268. CreateUnknownButtonText(strUnknown);
  1269. AppendRadioButtonVisited(str, nid, ST_UNKNOWN, bSet, strUnknown, bShowHistory);
  1270. return;
  1271. }
  1272. if (m_pTopic->IsCauseNode(nid) && state == 1) // it is presumptive cause ...
  1273. {
  1274. if (IsInSniffedArray(nid)) //... taken from sniffed array, but NOT current node.
  1275. {
  1276. // We are about to add entry for presumptive cause node.
  1277. // Actually, since this is sniffed node, we need to have two entries:
  1278. // one hidden fiels with node name and one hidden field with node name
  1279. // prefixed by "SNIFFED" prefix.
  1280. if (bSet)
  1281. {
  1282. // "bSet" will always set to true, as sniffed presumptive cause will never
  1283. // be visible.
  1284. AppendRadioButtonVisited(str, nid, state, bSet, m_pTopic->GetStateName(nid, state), false);
  1285. AppendHiddenFieldSniffed(str, nid);
  1286. }
  1287. }
  1288. return;
  1289. }
  1290. AppendRadioButtonVisited(str, nid, state, bSet, m_pTopic->GetStateName(nid, state), bShowHistory);
  1291. return;
  1292. }
  1293. // This is used to get the name of a node that has already been visited (for the
  1294. // history table).
  1295. // INPUT nid - node ID of desired node
  1296. // OUTPUT str - The "full name" of the node is appended to this, something like
  1297. // "Disable IBM AntiVirus" or "Make all paths less than 66 characters"
  1298. // If its value was sniffed, we append the appropriate string to mark it visibly
  1299. // as sniffed (typically, just "SNIFFED").
  1300. // INPUT bShowHistory
  1301. // If !bShowHistory, no appending: no need to show full name in a hidden table.
  1302. // Symbolic name will be written in a hidden field.
  1303. // Note that our CString, unlike MFC's, won't throw an exception on += out of memory
  1304. // RETURNS true if node number exists
  1305. bool CInfer::AppendVisitedNodeText(CString & str, NID nid, bool bShowHistory) const
  1306. {
  1307. if (!bShowHistory)
  1308. return true;
  1309. CString strTemp = m_pTopic->GetNodeFullName(nid);
  1310. if ( !strTemp.IsEmpty() )
  1311. {
  1312. str += strTemp;
  1313. return true;
  1314. }
  1315. else
  1316. return false;
  1317. }
  1318. // -------------------------------------------------------------------
  1319. // Writing to the new HTML page. Representing the recommended node.
  1320. // This is what is often called the page, although it is really only part of
  1321. // the body of the HTML page, along with history.
  1322. // -------------------------------------------------------------------
  1323. // AppendImpossiblePage: Gets the body of text that is
  1324. // displayed when the network is in an unreliable state.
  1325. void CInfer::AppendImpossiblePage(CString & str)
  1326. {
  1327. CString strHeader, strText;
  1328. strHeader = m_pTopic->GetMultilineNetProp(HTK_IMPOSSIBLE_HEADER, _T("<H4> %s </H4>\n"));
  1329. strText = m_pTopic->GetMultilineNetProp(HTK_IMPOSSIBLE_TEXT , _T("%s "));
  1330. if (!strHeader.IsEmpty() && !strText.IsEmpty())
  1331. {
  1332. str = strHeader + strText + _T("<BR>\n<BR>\n");
  1333. }
  1334. else
  1335. {
  1336. strHeader = m_pTopic->GetMultilineNetProp(HX_FAIL_HD_STR , _T("<H4> %s </H4>\n"));
  1337. strText = m_pTopic->GetMultilineNetProp(HX_FAIL_TXT_STR , _T("%s "));
  1338. if (!strHeader.IsEmpty() && !strText.IsEmpty())
  1339. {
  1340. str = strHeader + strText + _T("<BR>\n<BR>\n");
  1341. }
  1342. else
  1343. {
  1344. str = SZ_I_NO_RESULT_PAGE;
  1345. }
  1346. }
  1347. // Make a radio button with name = NODE_IMPOSSIBLE & value = SZ_ST_WORKED
  1348. CString strTemp = m_pTopic->GetNetPropItemStr(HX_IMPOSSIBLE_NORM_STR);
  1349. if (strTemp.IsEmpty()) // fall back on Fail node's property
  1350. strTemp = m_pTopic->GetNetPropItemStr(HX_FAIL_NORM_STR);
  1351. if (!strTemp.IsEmpty())
  1352. {
  1353. if (RUNNING_LOCAL_TS())
  1354. str += "\n<TABLE>";
  1355. AppendRadioButtonCurrentNode(str, NODE_IMPOSSIBLE, SZ_ST_WORKED, strTemp);
  1356. if (RUNNING_LOCAL_TS())
  1357. str += "</TABLE>\n";
  1358. }
  1359. str += _T("<P>");
  1360. AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
  1361. }
  1362. // AppendSniffAllCausesNormalPage: Gets the body of text that is displayed when sniffing
  1363. // on startup detects that all Cause nodes are in their Normal states.
  1364. void CInfer::AppendSniffAllCausesNormalPage(CString & str)
  1365. {
  1366. CString strHeader, strText;
  1367. strHeader = m_pTopic->GetMultilineNetProp(HTK_SNIFF_FAIL_HEADER, _T("<H4> %s </H4>\n"));
  1368. strText = m_pTopic->GetMultilineNetProp(HTK_SNIFF_FAIL_TEXT , _T("%s "));
  1369. if (!strHeader.IsEmpty() && !strText.IsEmpty())
  1370. {
  1371. str = strHeader + strText + _T("<BR>\n<BR>\n");
  1372. }
  1373. else
  1374. {
  1375. strHeader = m_pTopic->GetMultilineNetProp(HX_FAIL_HD_STR , _T("<H4> %s </H4>\n"));
  1376. strText = m_pTopic->GetMultilineNetProp(HX_FAIL_TXT_STR , _T("%s "));
  1377. if (!strHeader.IsEmpty() && !strText.IsEmpty())
  1378. {
  1379. str = strHeader + strText + _T("<BR>\n<BR>\n");
  1380. }
  1381. else
  1382. {
  1383. str = SZ_I_NO_RESULT_PAGE;
  1384. }
  1385. }
  1386. // Make a radio button with name = NODE_FAILALLCAUSESNORMAL & value = SZ_ST_WORKED
  1387. CString strTemp = m_pTopic->GetNetPropItemStr(HX_SNIFF_FAIL_NORM);
  1388. if (strTemp.IsEmpty()) // fall back on Fail node's property
  1389. strTemp = m_pTopic->GetNetPropItemStr(HX_FAIL_NORM_STR);
  1390. if (!strTemp.IsEmpty())
  1391. {
  1392. if (RUNNING_LOCAL_TS())
  1393. str += "\n<TABLE>";
  1394. AppendRadioButtonCurrentNode(str, NODE_FAILALLCAUSESNORMAL, SZ_ST_WORKED, strTemp);
  1395. if (RUNNING_LOCAL_TS())
  1396. str += "</TABLE>\n";
  1397. }
  1398. str += _T("<P>");
  1399. AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
  1400. }
  1401. // OUTPUT str - string to which we are appending to build HTML page we send back.
  1402. // Append (to str) a group of radio buttons, one for each "problem" node in the Belief Network
  1403. void CInfer::AppendProblemPage(CString & str)
  1404. {
  1405. CString strTemp;
  1406. m_nidSelected = nidProblemPage;
  1407. // text to precede list of problems. Introduced 8/98 for version 3.0.
  1408. // space after %s in next line: see note at head of file
  1409. str += m_pTopic->GetMultilineNetProp(H_PROB_PAGE_TXT_STR, _T("%s "));
  1410. // write problem header. This is text written as HTML <H4>.
  1411. strTemp.Format(_T("<H4> %s </H4>\n\n"), m_pTopic->GetNetPropItemStr(H_PROB_HD_STR));
  1412. str += strTemp;
  1413. // Write a comment in the HTML in service of automated test program
  1414. str += _T("<!-- IDH = PROBLEM -->\n");
  1415. //str += "<BR>";
  1416. if (RUNNING_LOCAL_TS())
  1417. str += "\n<TABLE>";
  1418. AppendProblemNodes(str);
  1419. if (RUNNING_LOCAL_TS())
  1420. str += "\n</TABLE>\n";
  1421. if (m_pTopic->UsesSniffer())
  1422. {
  1423. AppendActionButtons (str, k_BtnNext|k_BtnPPSniffing);
  1424. }
  1425. else
  1426. {
  1427. AppendActionButtons (str, k_BtnNext);
  1428. }
  1429. return;
  1430. }
  1431. // Helper routine for AppendProblemPage
  1432. void CInfer::AppendProblemNodes(CString & str)
  1433. {
  1434. vector<NID> arrnidNoSequence;
  1435. multimap<int, NID> mapSeqToNID;
  1436. // for every node in this Belief Network (but taking action only on "problem" nodes)
  1437. // put this nid in arrnidNoSequence if it has no sequence number or mapSeqToNID if it
  1438. // has one.
  1439. for(int nid = 0; nid < m_pTopic->CNode(); nid++)
  1440. {
  1441. if (m_pTopic->IsProblemNode(nid))
  1442. {
  1443. CString strSpecial = m_pTopic->GetNodePropItemStr(nid, H_PROB_SPECIAL);
  1444. // if it's not marked as a "hidden" problem, we'll want it in the problem page
  1445. if (strSpecial.CompareNoCase(_T("hide")) != 0)
  1446. {
  1447. CString str = m_pTopic->GetNodePropItemStr(nid, H_NODE_PROB_SEQUENCE);
  1448. try
  1449. {
  1450. if (str.IsEmpty())
  1451. arrnidNoSequence.push_back(nid);
  1452. else
  1453. mapSeqToNID.insert(pair<int, NID>(_ttoi(str), nid));
  1454. }
  1455. catch (exception& x)
  1456. {
  1457. CString str;
  1458. // Note STL exception in event log.
  1459. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1460. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1461. SrcLoc.GetSrcFileLineStr(),
  1462. CCharConversion::ConvertACharToString(x.what(), str),
  1463. _T(""),
  1464. EV_GTS_STL_EXCEPTION );
  1465. }
  1466. }
  1467. }
  1468. }
  1469. for (multimap<int, NID>::const_iterator ppair=mapSeqToNID.begin();
  1470. ppair != mapSeqToNID.end();
  1471. ppair++)
  1472. {
  1473. // Create a radio button with "ProblemAsk" as its name & this problem
  1474. // as its value
  1475. AppendRadioButtonCurrentNode(
  1476. str,
  1477. NODE_PROBLEM_ASK,
  1478. m_pTopic->GetNodeSymName(ppair->second),
  1479. m_pTopic->GetNodePropItemStr(ppair->second, H_PROB_TXT_STR));
  1480. }
  1481. for (vector<NID>::const_iterator pnid=arrnidNoSequence.begin();
  1482. pnid != arrnidNoSequence.end();
  1483. pnid++)
  1484. {
  1485. // Create a radio button with "ProblemAsk" as its name & this problem
  1486. // as its value
  1487. AppendRadioButtonCurrentNode(
  1488. str,
  1489. NODE_PROBLEM_ASK,
  1490. m_pTopic->GetNodeSymName(*pnid),
  1491. m_pTopic->GetNodePropItemStr(*pnid, H_PROB_TXT_STR));
  1492. }
  1493. }
  1494. // Append this network's "BYE" page to str
  1495. // OUTPUT str - string to append to
  1496. void CInfer::AppendByeMsg(CString & str)
  1497. {
  1498. str += _T("<!-- &quot;BYE&quot; (success) PAGE -->\n");
  1499. // Write a comment in the HTML in service of automated test program
  1500. str += _T("<!-- IDH = IDH_BYE -->\n");
  1501. // Write this troubleshooter's "Bye" header and text
  1502. // space after %s in next 2 lines: see note at head of file
  1503. AppendMultilineNetProp(str, HX_BYE_HD_STR, _T("<H4> %s </H4>\n"));
  1504. AppendMultilineNetProp(str, HX_BYE_TXT_STR, _T("%s "));
  1505. str += _T("<P>\n");
  1506. AppendActionButtons (str, k_BtnBack|k_BtnStartOver);
  1507. return;
  1508. }
  1509. // Append this network's "FAIL" page to str
  1510. // OUTPUT str - string to append to
  1511. void CInfer::AppendFailMsg(CString & str)
  1512. {
  1513. str += _T("<!-- &quot;FAIL&quot; PAGE -->\n");
  1514. // Write a comment in the HTML in service of automated test program
  1515. str += _T("<!-- IDH = IDH_FAIL -->\n");
  1516. // Write this topic's "Fail" header and text
  1517. // space after %s in next 2 lines: see note at head of file
  1518. AppendMultilineNetProp(str, HX_FAIL_HD_STR, _T("<H4> %s </H4>\n"));
  1519. AppendMultilineNetProp(str, HX_FAIL_TXT_STR, _T("%s "));
  1520. str += _T("<BR>\n<BR>\n");
  1521. // Make a radio button with name = NODE_FAIL & value = SZ_ST_WORKED
  1522. CString strTemp = m_pTopic->GetNetPropItemStr(HX_FAIL_NORM_STR);
  1523. if (!strTemp.IsEmpty())
  1524. {
  1525. if (RUNNING_LOCAL_TS())
  1526. str += "\n<TABLE>";
  1527. AppendRadioButtonCurrentNode(str, NODE_FAIL, SZ_ST_WORKED, strTemp);
  1528. if (RUNNING_LOCAL_TS())
  1529. str += "</TABLE>\n";
  1530. }
  1531. AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
  1532. return;
  1533. }
  1534. // Append content of the "service" page to str (Offers 2 possibilities: seek help elsewhere
  1535. // or go back and try something you skipped)
  1536. // OUTPUT str - string to append to
  1537. void CInfer::AppendServiceMsg(CString & str)
  1538. {
  1539. CString strTemp;
  1540. str += _T("<!-- &quot;SERVICE&quot; PAGE -->\n");
  1541. str += _T("<!-- Offers to seek help elsewhere or go back and try something you skipped -->\n");
  1542. // Write a comment in the HTML in service of automated test program
  1543. str += _T("<!-- IDH = SERVICE -->\n");
  1544. // Write this troubleshooter's "Service" header and text
  1545. // space after %s in next 2 lines: see note at head of file
  1546. AppendMultilineNetProp(str, HX_SER_HD_STR, _T("<H4> %s </H4>\n"));
  1547. AppendMultilineNetProp(str, HX_SER_TXT_STR, _T("%s "));
  1548. str += _T("<BR>\n<BR>\n");
  1549. if (RUNNING_LOCAL_TS())
  1550. str += "\n<TABLE>";
  1551. // Make a radio button with name = Service & value = SZ_ST_WORKED
  1552. // Typical text is "I will try to get help elsewhere.";
  1553. strTemp = m_pTopic->GetNetPropItemStr(HX_SER_NORM_STR);
  1554. if (!strTemp.IsEmpty())
  1555. AppendRadioButtonCurrentNode(str, NODE_SERVICE, SZ_ST_WORKED, strTemp);
  1556. // Make a radio button with name = Service & value = SZ_ST_ANY
  1557. // Typical text is "Retry any steps that I have skipped."
  1558. strTemp = m_pTopic->GetNetPropItemStr(HX_SER_AB_STR);
  1559. if (!strTemp.IsEmpty())
  1560. AppendRadioButtonCurrentNode(str, NODE_SERVICE, SZ_ST_ANY, strTemp);
  1561. if (RUNNING_LOCAL_TS())
  1562. str += "</TABLE>\n";
  1563. str += _T("<P>");
  1564. AppendActionButtons (str, k_BtnNext|k_BtnBack|k_BtnStartOver);
  1565. return;
  1566. }
  1567. // Depending on the value of nid, this fn can build
  1568. // - a BYE page
  1569. // - a FAIL page
  1570. // - a SERVICE page
  1571. // - a page for a normal node (fixable/observable, fixable/unobservable, unfixable, or
  1572. // informational).
  1573. // If none of these cases apply, returns with no action taken
  1574. // INPUT nid - ID of a node
  1575. // OUTPUT str - string to append to
  1576. void CInfer::AppendNIDPage(NID nid, CString & str)
  1577. {
  1578. CString strTxt;
  1579. m_nidSelected = nid;
  1580. if (nid == nidByeNode)
  1581. AppendByeMsg(str);
  1582. else if (nid == nidFailNode)
  1583. AppendFailMsg(str);
  1584. else if (nid == nidSniffedAllCausesNormalNode)
  1585. AppendSniffAllCausesNormalPage(str);
  1586. else if (nid == nidService)
  1587. AppendServiceMsg(str);
  1588. else if (m_pTopic->IsValidNID(nid))
  1589. {
  1590. bool bShowManualSniffingButton = false;
  1591. if (m_pSniff)
  1592. if (nid != m_SniffedRecommendation.nid())
  1593. // we're NOT showing sniffed node.
  1594. bShowManualSniffingButton = m_pSniff->GetSniffController()->AllowManualSniffing(nid);
  1595. // Write a comment in the HTML in service of automated test program
  1596. str += _T("<!-- IDH = ");
  1597. str += m_pTopic->GetNodeSymName(nid);
  1598. str += _T(" -->\n");
  1599. // Write this node's header & text
  1600. // space after %s in next several lines: see note at head of file
  1601. AppendMultilineNodeProp(str, nid, H_NODE_HD_STR, _T("<H4> %s </H4>\n"));
  1602. if (bShowManualSniffingButton)
  1603. AppendMultilineNodeProp(str, nid, H_NODE_MANUAL_SNIFF_TEXT, _T("%s "));
  1604. if (m_SniffedRecommendation.nid() == nid)
  1605. {
  1606. CString tmp;
  1607. AppendMultilineNodeProp(tmp, nid, H_NODE_DCT_STR, _T("%s "));
  1608. if (tmp.IsEmpty())
  1609. AppendMultilineNodeProp(str, nid, H_NODE_TXT_STR, _T("%s "));
  1610. else
  1611. str += tmp;
  1612. }
  1613. else
  1614. {
  1615. AppendMultilineNodeProp(str, nid, H_NODE_TXT_STR, _T("%s "));
  1616. }
  1617. str += _T("\n<BR>\n<BR>\n");
  1618. // Write appropriate radio buttons depending on what kind of node it is.
  1619. if (m_pTopic->IsCauseNode(nid) || m_pTopic->IsInformationalNode(nid))
  1620. AppendCurrentRadioButtons(nid, str);
  1621. AppendActionButtons (
  1622. str,
  1623. k_BtnNext|k_BtnBack|k_BtnStartOver|(bShowManualSniffingButton ? k_BtnManualSniffing : 0),
  1624. nid);
  1625. }
  1626. // else nothing we can do with this
  1627. return;
  1628. }
  1629. // -------------------------------------------------------------------
  1630. // BES
  1631. // -------------------------------------------------------------------
  1632. // Historically:
  1633. // Returns true if we are supposed to show the full BES page (& let the user edit the
  1634. // search string) vs. extracting the search string & starting the search without
  1635. // any possible user intervention
  1636. // However, we no longer offer that option as of 981021.
  1637. bool CInfer::ShowFullBES()
  1638. {
  1639. return false;
  1640. }
  1641. // returns true in the circumstances where we wish to show a Back End Search
  1642. bool CInfer::TimeForBES()
  1643. {
  1644. return (m_pTopic->HasBES() && m_bUseBackEndRedirection);
  1645. }
  1646. // If it is time to do a Back End Search redirection, append the "redirection" string
  1647. // to str and return true
  1648. // Otherwise, return false
  1649. // str should represent the header of an HTML page.
  1650. // For browsers which support redirection, this is how we overide service node (or fail node)
  1651. // when BES is present
  1652. bool CInfer::AppendBESRedirection(CString & str)
  1653. {
  1654. if (m_pTopic->HasBES() && TimeForBES() && !ShowFullBES() && !m_strEncodedForm.IsEmpty())
  1655. {
  1656. str += _T("Location: ");
  1657. str += m_strEncodedForm;
  1658. str += _T("\r\n");
  1659. return( true );
  1660. }
  1661. return false;
  1662. }
  1663. // Append HTML representing BES to OUTPUT str and build m_strEncodedForm,
  1664. // This is a distinct new algorithm in Ver 3.0, replacing the old "word list" approach.
  1665. void CInfer::OutputBackend(CString & str)
  1666. {
  1667. vector<CString>arrstrSearch;
  1668. int nNodesInBasis = m_BasisForInference.size();
  1669. for (int i = 0; i<nNodesInBasis; i++)
  1670. {
  1671. NID nid = m_BasisForInference[i].nid();
  1672. IST state = m_BasisForInference[i].state();
  1673. CString strSearchState;
  1674. // First account for binary nodes w/ special property names
  1675. if (state == 0)
  1676. strSearchState = m_pTopic->GetNodePropItemStr(nid, H_NODE_NORM_SRCH_STR);
  1677. else if (state == 1)
  1678. strSearchState = m_pTopic->GetNodePropItemStr(nid, H_NODE_AB_SRCH_STR);
  1679. else
  1680. strSearchState = _T("");
  1681. if (strSearchState.IsEmpty())
  1682. // multistate node
  1683. strSearchState = m_pTopic->GetNodePropItemStr(nid, MUL_ST_SRCH_STR, state);
  1684. if (! strSearchState.IsEmpty())
  1685. {
  1686. try
  1687. {
  1688. arrstrSearch.push_back(strSearchState);
  1689. }
  1690. catch (exception& x)
  1691. {
  1692. CString str;
  1693. // Note STL exception in event log.
  1694. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1695. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1696. SrcLoc.GetSrcFileLineStr(),
  1697. CCharConversion::ConvertACharToString(x.what(), str),
  1698. _T(""),
  1699. EV_GTS_STL_EXCEPTION );
  1700. }
  1701. }
  1702. }
  1703. // Build the full BES page
  1704. CString strRaw;
  1705. m_pTopic->GenerateBES(arrstrSearch, m_strEncodedForm, strRaw);
  1706. str += strRaw;
  1707. }
  1708. // -------------------------------------------------------------
  1709. // Logging
  1710. // -------------------------------------------------------------
  1711. // Return NID of page ultimately selected. If no such page, returns nidNil.
  1712. NID CInfer::NIDSelected() const
  1713. {
  1714. return m_nidSelected;
  1715. }
  1716. // -------------------------------------------------------------
  1717. // Effectively, a method on m_arrnidSkipped
  1718. // -------------------------------------------------------------
  1719. // INPUT nid
  1720. // RETURNS true if nid is node in the "skip list" (ST_UNKNOWN, "Try something else").
  1721. bool CInfer::IsSkipped(NID nid) const
  1722. {
  1723. vector<NID>::const_iterator itBegin= m_arrnidSkipped.begin();
  1724. vector<NID>::const_iterator itEnd= m_arrnidSkipped.end();
  1725. return (find(itBegin, itEnd, nid) != itEnd);
  1726. }
  1727. // -------------------------------------------------------------
  1728. // Buttons
  1729. // -------------------------------------------------------------
  1730. // appends only <INPUT TYPE=...> clause
  1731. void CInfer::AppendNextButton(CString & str) const
  1732. {
  1733. str += SZ_INPUT_TAG_NEXT; // _T("<INPUT tag=next TYPE=SUBMIT VALUE=\"")
  1734. AppendNextButtonText(str);
  1735. str += _T("\">");
  1736. }
  1737. // For local TS, appends only <INPUT TYPE=...> clause
  1738. // For Online TS, must build a pseudo button.
  1739. void CInfer::AppendStartOverButton(CString & str) const
  1740. {
  1741. if (RUNNING_LOCAL_TS())
  1742. {
  1743. str += SZ_INPUT_TAG_STARTOVER; // _T("<INPUT tag=startover TYPE=BUTTON VALUE=\"")
  1744. AppendStartOverButtonText(str);
  1745. str += _T("\" onClick=\"starter()\">");
  1746. }
  1747. else
  1748. {
  1749. // Added for V3.2
  1750. CString strLabel; // visible label for pseudo button
  1751. AppendStartOverButtonText(strLabel);
  1752. AppendLinkAsButton(str, m_strStartOverLink, strLabel);
  1753. }
  1754. }
  1755. // appends only <INPUT TYPE=...> clause
  1756. void CInfer::AppendBackButton(CString & str) const
  1757. {
  1758. if (RUNNING_LOCAL_TS())
  1759. {
  1760. str += SZ_INPUT_TAG_BACK; // _T("<INPUT tag=back TYPE=BUTTON VALUE=\"")
  1761. AppendBackButtonText(str);
  1762. str += _T("\" onClick=\"generated_previous()\">");
  1763. }
  1764. }
  1765. // AppendManualSniffButton will generate script something like this, but this
  1766. // comment is not being carefully maintained, so see actual code for details.
  1767. /////////////////////////////////////////////////////////////////////////////
  1768. // function sniffManually() { //
  1769. // var stateSniffed = parent.t3.PerformSniffingJS("NodeName", "", "");//
  1770. // //
  1771. // if(stateSniffed == -1) { //
  1772. // stateSniffed = parent.t3.PerformSniffingVB("NodeName", "", "");//
  1773. // } //
  1774. // //
  1775. // if(stateSniffed == -1) { //
  1776. // alert("Could not sniff this node"); //
  1777. // } else { //
  1778. // if(stateSniffed > NumOfStates) { //
  1779. // alert("Could not sniff this node"); //
  1780. // } else { //
  1781. // /////////////////////////////////////////////////////// //
  1782. // IF IS CAUSE NODE: //
  1783. // if (stateSniffed == 1) //
  1784. // document.all.Sniffed_NodeName.value = 101; //
  1785. // else //
  1786. // document.all.Sniffed_NodeName.value = stateSniffed; //
  1787. // /////////////////////////////////////////////////////// //
  1788. // IF IS NOT CAUSE NODE: //
  1789. // document.all.Sniffed_NodeName.value = stateSniffed; //
  1790. // /////////////////////////////////////////////////////// //
  1791. // document.all.NodeState[stateSniffed].checked = true; //
  1792. // document.ButtonForm.onsubmit(); //
  1793. // } //
  1794. // } //
  1795. // } //
  1796. /////////////////////////////////////////////////////////////////////////////
  1797. void CInfer::AppendManualSniffButton(CString & str, NID nid) const
  1798. {
  1799. if (RUNNING_LOCAL_TS())
  1800. {
  1801. CString strNodeName;
  1802. CString strTmp;
  1803. SymbolicFromNID(strNodeName, nid);
  1804. bool bIsCause = m_pTopic->IsCauseNode(nid);
  1805. str += _T(
  1806. "\n\n<script language=\"JavaScript\">\n"
  1807. "function sniffManually() {\n"
  1808. " var stateSniffed = parent.t3.PerformSniffingJavaScript(\"");
  1809. str += strNodeName;
  1810. str += _T(
  1811. "\", \"\", \"\");\n");
  1812. str += _T(
  1813. " if(stateSniffed == -1) {\n"
  1814. " stateSniffed = parent.t3.PerformSniffingVBScript(\"");
  1815. str += strNodeName;
  1816. str += _T(
  1817. "\", \"\", \"\");\n"
  1818. "}\n");
  1819. str += _T(
  1820. " if(stateSniffed == -1) {\n"
  1821. " alert(\"");
  1822. AppendSniffFailedText(str);
  1823. str += _T(
  1824. "\");\n"
  1825. " } else {\n"
  1826. " if(stateSniffed >");
  1827. CString strStates;
  1828. strStates.Format(_T("%d"), m_pTopic->GetCountOfStates(nid) -1);
  1829. str += strStates;
  1830. str += _T(
  1831. ") {\n"
  1832. " alert(\"");
  1833. AppendSniffFailedText(str);
  1834. str += _T(
  1835. "\");\n"
  1836. " } else {\n");
  1837. if (bIsCause)
  1838. {
  1839. str += _T(
  1840. " if (stateSniffed == 1)\n"
  1841. " document.all.");
  1842. str += C_SNIFFTAG;
  1843. str += strNodeName;
  1844. str += _T(".value = ");
  1845. str += SZ_ST_WORKED;
  1846. str += _T(";\n");
  1847. str += _T(
  1848. " else\n");
  1849. }
  1850. str += _T(
  1851. " document.all.");
  1852. str += C_SNIFFTAG;
  1853. str += strNodeName;
  1854. str += _T(".value = stateSniffed;\n");
  1855. str += _T(
  1856. " document.all.");
  1857. str += C_LAST_SNIFFED_MANUALLY;
  1858. str += _T(".value = ");
  1859. str += SZ_ST_SNIFFED_MANUALLY_TRUE;
  1860. str += _T(";\n");
  1861. str += _T(
  1862. " document.all.");
  1863. str += strNodeName;
  1864. str += _T(
  1865. "[stateSniffed].checked = true;\n");
  1866. str += _T(
  1867. " document.ButtonForm.onsubmit();\n");
  1868. str += _T(
  1869. " }\n"
  1870. " }\n"
  1871. "}\n"
  1872. "</script>\n\n");
  1873. str += _T(
  1874. "<INPUT tag=sniff TYPE=BUTTON VALUE=\"");
  1875. AppendManualSniffButtonText(str);
  1876. str += _T(
  1877. "\" onClick=\"sniffManually()\">\n");
  1878. str += _T(
  1879. "<INPUT type=\"HIDDEN\" name=\"");
  1880. str += C_SNIFFTAG;
  1881. str += strNodeName;
  1882. str += _T("\" value=\"");
  1883. strTmp.Format(_T("%d"), SNIFF_FAILURE_RESULT);
  1884. str += strTmp;
  1885. str += _T("\">\n");
  1886. str += _T(
  1887. "<INPUT type=\"HIDDEN\" name=\"");
  1888. str += C_LAST_SNIFFED_MANUALLY;
  1889. str += _T("\" value=\"");
  1890. str += SZ_ST_SNIFFED_MANUALLY_FALSE;
  1891. str += _T("\">\n");
  1892. }
  1893. }
  1894. // appends only <INPUT TYPE=...> clause
  1895. void CInfer::AppendPPSnifferButton(CString & str) const
  1896. {
  1897. str += SZ_INPUT_TAG_SNIFFER; // _T("<INPUT tag=sniffer TYPE=BUTTON VALUE=\"")
  1898. AppendPPSnifferButtonText(str);
  1899. str += _T("\" onClick=\"runtest()\">");
  1900. }
  1901. void CInfer::AppendActionButtons(CString & str, ActionButtonSet btns, NID nid /*=-1*/) const
  1902. {
  1903. // Online TS's Start Over "button" is actually a link, and will implicitly
  1904. // start a new line unless we do something about it.
  1905. bool bGenerateTable = (!RUNNING_LOCAL_TS() && (btns & k_BtnStartOver));
  1906. if (bGenerateTable)
  1907. str += _T("<TABLE><tr><td>");
  1908. if (btns & k_BtnNext)
  1909. {
  1910. AppendNextButton(str);
  1911. str += _T("\n");
  1912. }
  1913. if (btns & k_BtnBack)
  1914. {
  1915. AppendBackButton(str);
  1916. str += _T("\n");
  1917. }
  1918. if (bGenerateTable)
  1919. str += _T("</td><td>");
  1920. if (btns & k_BtnStartOver)
  1921. {
  1922. AppendStartOverButton(str);
  1923. str += _T("\n");
  1924. }
  1925. if (bGenerateTable)
  1926. str += _T("</td><td>");
  1927. if (btns & k_BtnPPSniffing)
  1928. {
  1929. AppendPPSnifferButton(str);
  1930. str += _T("\n");
  1931. }
  1932. if ((btns & k_BtnManualSniffing) && nid != -1)
  1933. {
  1934. AppendManualSniffButton(str, nid);
  1935. str += _T("\n");
  1936. }
  1937. if (bGenerateTable)
  1938. str += _T("</td></tr></TABLE>");
  1939. str += _T("<BR><BR>");
  1940. }
  1941. // -------------------------------------------------------------
  1942. // MISCELLANY
  1943. // -------------------------------------------------------------
  1944. // RETURN true for cause (vs. informational or problem) node.
  1945. // Note that a cause may be either a fixable node or an "unfixable" node which
  1946. // "can be fixed with infinite effort"
  1947. /* static */ bool CInfer::IsCause (ESTDLBL lbl)
  1948. {
  1949. return (lbl == ESTDLBL_fixobs || lbl == ESTDLBL_fixunobs || lbl == ESTDLBL_unfix);
  1950. }
  1951. // This code can take a previously skipped node and bring it back again as a recommendation.
  1952. // It is relevant only if the user received the service node in the previous call
  1953. // to the DLL and now wants to see if there is "Anything Else I Can Try".
  1954. //
  1955. // This code will remove the first node from the skip list so that it may be delivered to
  1956. // the user again.
  1957. //
  1958. // Of course, m_arrnidSkipped, m_arrnidVisited must be filled in before this is called.
  1959. //
  1960. void CInfer::RecycleSkippedNode()
  1961. {
  1962. // Only should take effect once per instance of this object, because peels the first
  1963. // entry off of m_arrnidSkipped. We guarantee that with the following:
  1964. if (m_bRecyclingInitialized)
  1965. return;
  1966. m_bRecyclingInitialized = true;
  1967. // Only relevant if the query asks for a previously skipped node brought back again
  1968. // as a recommendation.
  1969. if (!m_bRecycleSkippedNode)
  1970. return;
  1971. // This is a safety check to bail out if there are no skipped nodes.
  1972. // This would be a bogus query, because the Service Node should only have been
  1973. // offered if there were skipped recommendations to try.
  1974. if (m_arrnidSkipped.empty())
  1975. {
  1976. m_bRecycleSkippedNode = false;
  1977. return;
  1978. }
  1979. // OK,now down to business.
  1980. // Get a value for m_nidRecycled from the first item skipped
  1981. m_nidRecycled = m_arrnidSkipped.front();
  1982. // Remove skipped item from skip table
  1983. m_arrnidSkipped.erase(m_arrnidSkipped.begin());
  1984. // Fix table of nodes that will be placed into the output table
  1985. // to not include the first node skipped
  1986. vector<NID>::const_iterator itnidBegin = m_arrnidVisited.begin();
  1987. vector<NID>::const_iterator itnidEnd = m_arrnidVisited.end();
  1988. vector<NID>::const_iterator itnidAnythingElse = find(itnidBegin, itnidEnd, m_nidRecycled);
  1989. if (itnidAnythingElse != itnidEnd)
  1990. m_arrnidVisited.erase( const_cast<vector<NID>::iterator>(itnidAnythingElse) );
  1991. }
  1992. bool CInfer::ManuallySniffedNodeExists() const
  1993. {
  1994. // If last element in m_BasisForInference is sniffed,
  1995. // it means, that this element was set by manual sniffing
  1996. // function.
  1997. if (m_BasisForInference.size() && m_SniffedStates.size())
  1998. return m_bLastSniffedManually;
  1999. return false;
  2000. }
  2001. bool CInfer::IsManuallySniffedNode(NID nid) const
  2002. {
  2003. if (ManuallySniffedNodeExists())
  2004. return nid == m_SniffedStates[m_SniffedStates.size()-1].nid();
  2005. return false;
  2006. }
  2007. void CInfer::SetLastSniffedManually(bool set)
  2008. {
  2009. m_bLastSniffedManually = set;
  2010. }
  2011. // -------------------------------------------------------------------
  2012. // CInfer::CArrayOrderRestorer implementation
  2013. // -------------------------------------------------------------------
  2014. //
  2015. // CInfer::CArrayOrderRestorer exists so that after re-sniffing, we can restore an array
  2016. // of visited nodes to its original order, as saved in m_arrInitial.
  2017. //
  2018. // INPUT: nBaseLength = number of elements in fixed locations at head of array, which will
  2019. // never be moved (typically nodes explicitly set by user rather than sniffed).
  2020. // INPUT/OUTPUT: arrToRestore = array to restore: input dictates content of output, but
  2021. // (beyond nBaseLength) does not dictate the order. Order comes from m_arrInitial.
  2022. // OUTPUT: arrToRestore = array with restored order
  2023. // RETURN: true if successful
  2024. bool CInfer::CArrayOrderRestorer::Restore(long nBaseLength, vector<NID>& arrToRestore)
  2025. {
  2026. if (nBaseLength > arrToRestore.size())
  2027. return false;
  2028. long i;
  2029. vector<NID>::iterator i_base;
  2030. vector<NID>::iterator i_additional;
  2031. vector<NID> arrBase;
  2032. vector<NID> arrAdditional;
  2033. try
  2034. {
  2035. for (i = 0; i < nBaseLength; i++)
  2036. arrBase.push_back(arrToRestore[i]);
  2037. for (i = nBaseLength; i < arrToRestore.size(); i++)
  2038. arrAdditional.push_back(arrToRestore[i]);
  2039. }
  2040. catch (exception& x)
  2041. {
  2042. CString str;
  2043. // Note STL exception in event log.
  2044. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  2045. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  2046. SrcLoc.GetSrcFileLineStr(),
  2047. CCharConversion::ConvertACharToString(x.what(), str),
  2048. _T(""),
  2049. EV_GTS_STL_EXCEPTION );
  2050. }
  2051. arrToRestore.clear();
  2052. for (i = 0, i_base = arrBase.begin();
  2053. i < m_arrInitial.size();
  2054. i++)
  2055. {
  2056. if (arrBase.end() != find(arrBase.begin(), arrBase.end(), m_arrInitial[i]))
  2057. {
  2058. if (i_base != arrBase.end())
  2059. i_base++;
  2060. }
  2061. else if (arrAdditional.end() != (i_additional = find(arrAdditional.begin(), arrAdditional.end(), m_arrInitial[i])))
  2062. {
  2063. i_base = arrBase.insert(i_base, m_arrInitial[i]);
  2064. i_base++;
  2065. arrAdditional.erase(i_additional);
  2066. }
  2067. }
  2068. arrToRestore = arrBase;
  2069. try
  2070. {
  2071. for (i = 0; i < arrAdditional.size(); i++)
  2072. arrToRestore.push_back(arrAdditional[i]);
  2073. }
  2074. catch (exception& x)
  2075. {
  2076. CString str;
  2077. // Note STL exception in event log.
  2078. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  2079. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  2080. SrcLoc.GetSrcFileLineStr(),
  2081. CCharConversion::ConvertACharToString(x.what(), str),
  2082. _T(""),
  2083. EV_GTS_STL_EXCEPTION );
  2084. }
  2085. return true;
  2086. }