otsdaq_utilities  v2_04_00
ConsoleSupervisor.cc
1 #include "otsdaq-utilities/Console/ConsoleSupervisor.h"
2 #include <xdaq/NamespaceURI.h>
3 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
4 #include "otsdaq-core/Macros/CoutMacros.h"
5 #include "otsdaq-core/MessageFacility/MessageFacility.h"
6 #include "otsdaq-core/NetworkUtilities/ReceiverSocket.h"
7 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
8 
9 #include <dirent.h> //for DIR
10 #include <sys/stat.h> //for mkdir
11 #include <fstream>
12 #include <iostream>
13 #include <string>
14 #include <thread> //for std::thread
15 
16 using namespace ots;
17 
18 // UDP Message Format:
19 // UDPMESSAGE|TIMESTAMP|SEQNUM|HOSTNAME|HOSTADDR|SEVERITY|CATEGORY|APPLICATION|PID|ITERATION|MODULE|(FILE|LINE)|MESSAGE
20 // FILE and LINE are only printed for s67+
21 
22 XDAQ_INSTANTIATOR_IMPL(ConsoleSupervisor)
23 
24 #define USER_CONSOLE_PREF_PATH \
25  std::string(__ENV__("SERVICE_DATA_PATH")) + "/ConsolePreferences/"
26 #define USERS_PREFERENCES_FILETYPE "pref"
27 
28 #define QUIET_CFG_FILE \
29  std::string(__ENV__("USER_DATA")) + \
30  "/MessageFacilityConfigurations/" \
31  "QuietForwarder.cfg"
32 
33 #define CONSOLE_SPECIAL_ERROR std::string("||0|||Error|Console|-1||ConsoleSupervisor|")
34 #define CONSOLE_SPECIAL_WARNING \
35  std::string("||0|||Warning|Console|-1||ConsoleSupervisor|")
36 
37 #undef __MF_SUBJECT__
38 #define __MF_SUBJECT__ "Console"
39 
40 //========================================================================================================================
41 ConsoleSupervisor::ConsoleSupervisor(xdaq::ApplicationStub* stub)
42  : CoreSupervisorBase(stub), writePointer_(0), messageCount_(0)
43 {
44  __SUP_COUT__ << "Constructor started." << __E__;
45 
46  INIT_MF("ConsoleSupervisor");
47 
48  // attempt to make directory structure (just in case)
49  mkdir(((std::string)USER_CONSOLE_PREF_PATH).c_str(), 0755);
50 
51  init();
52 
53  __SUP_COUT__ << "Constructor complete." << __E__;
54 }
55 
56 //========================================================================================================================
57 ConsoleSupervisor::~ConsoleSupervisor(void) { destroy(); }
58 //========================================================================================================================
59 void ConsoleSupervisor::init(void)
60 {
61  // start mf msg listener
62  std::thread(
63  [](ConsoleSupervisor* cs) {
64  ConsoleSupervisor::messageFacilityReceiverWorkLoop(cs);
65  },
66  this)
67  .detach();
68 } // end init()
69 
70 //========================================================================================================================
71 void ConsoleSupervisor::destroy(void)
72 {
73  // called by destructor
74 } // end destroy()
75 
76 //========================================================================================================================
77 // messageFacilityReceiverWorkLoop ~~
78 // Thread for printing Message Facility messages without decorations
79 // Note: Uses std::mutex to avoid conflict with reading thread.
80 void ConsoleSupervisor::messageFacilityReceiverWorkLoop(ConsoleSupervisor* cs)
81 {
82  __COUT__ << std::endl;
83 
84  std::string configFile = QUIET_CFG_FILE;
85  FILE* fp = fopen(configFile.c_str(), "r");
86  if(!fp)
87  {
88  __SS__ << "File with port info could not be loaded: " << QUIET_CFG_FILE
89  << std::endl;
90  __COUT__ << "\n" << ss.str();
91  __SS_THROW__;
92  }
93  char tmp[100];
94  fgets(tmp, 100, fp); // receive port (ignore)
95  fgets(tmp, 100, fp); // destination port *** used here ***
96  int myport;
97  sscanf(tmp, "%*s %d", &myport);
98 
99  fgets(tmp, 100, fp); // destination ip *** used here ***
100  char myip[100];
101  sscanf(tmp, "%*s %s", myip);
102  fclose(fp);
103 
104  ReceiverSocket rsock(myip, myport); // Take Port from Configuration
105  try
106  {
107  rsock.initialize();
108  }
109  catch(...)
110  {
111  // lockout the messages array for the remainder of the scope
112  // this guarantees the reading thread can safely access the messages
113  std::lock_guard<std::mutex> lock(cs->messageMutex_);
114 
115  // NOTE: if we do not want this to be fatal, do not throw here, just print out
116 
117  if(1) // generate special message and throw for failed socket
118  {
119  __SS__ << "FATAL Console error. Could not initialize socket on port "
120  << myport
121  << ". Perhaps the port is already in use? Check for multiple stale "
122  "instances of otsdaq processes, or notify admins."
123  << " Multiple instances of otsdaq on the same node should be "
124  "possible, but port numbers must be unique."
125  << std::endl;
126  __SS_THROW__;
127  }
128 
129  // generate special message to indicate failed socket
130  __SS__ << "FATAL Console error. Could not initialize socket on port " << myport
131  << ". Perhaps it is already in use? Exiting Console receive loop."
132  << std::endl;
133  __COUT__ << ss.str();
134 
135  cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_ERROR + ss.str(),
136  cs->messageCount_++);
137 
138  if(++cs->writePointer_ == cs->messages_.size()) // handle wrap-around
139  cs->writePointer_ = 0;
140 
141  return;
142  }
143 
144  std::string buffer;
145  int i = 0;
146  int heartbeatCount = 0;
147  int selfGeneratedMessageCount = 0;
148 
149  std::map<unsigned int, unsigned int>
150  sourceLastSequenceID; // map from sourceID to
151  // lastSequenceID to
152  // identify missed messages
153  long long newSourceId;
154  unsigned int newSequenceId;
155 
156  while(1)
157  {
158  // if receive succeeds display message
159 
160  if(rsock.receive(
161  buffer, 1 /*timeoutSeconds*/, 0 /*timeoutUSeconds*/, false /*verbose*/) !=
162  -1)
163  {
164  if(i != 200)
165  {
166  __COUT__ << "Console has first message." << std::endl;
167  i = 200; // mark so things are good for all time. (this indicates things
168  // are configured to be sent here)
169 
170  __MOUT__ << "DEBUG messages look like this." << std::endl;
171  __MOUT_INFO__ << "INFO messages look like this." << std::endl;
172  __MOUT_WARN__ << "WARNING messages look like this." << std::endl;
173  __MOUT_ERR__ << "ERROR messages look like this." << std::endl;
174 
175  // //to debug special packets
176  // __SS__ << "???";
177  // cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_ERROR
178  //+ ss.str(),
179  // cs->messageCount_++);
180  //
181  // if(++cs->writePointer_ == cs->messages_.size()) //handle
182  // wrap-around cs->writePointer_ = 0;
183  }
184 
185  if(selfGeneratedMessageCount)
186  --selfGeneratedMessageCount; // decrement internal message count
187  else // reset heartbeat if external messages are coming through
188  heartbeatCount = 0;
189 
190  //__COUT__ << buffer << std::endl;
191 
192  // lockout the messages array for the remainder of the scope
193  // this guarantees the reading thread can safely access the messages
194  std::lock_guard<std::mutex> lock(cs->messageMutex_);
195 
196  cs->messages_[cs->writePointer_].set(buffer, cs->messageCount_++);
197 
198  // check if sequence ID is out of order
199  newSourceId = cs->messages_[cs->writePointer_].getSourceIDAsNumber();
200  newSequenceId = cs->messages_[cs->writePointer_].getSequenceIDAsNumber();
201 
202  //__COUT__ << "newSourceId: " << newSourceId << std::endl;
203  //__COUT__ << "newSequenceId: " << newSequenceId << std::endl;
204 
205  if(newSourceId != -1 &&
206  sourceLastSequenceID.find(newSourceId) !=
207  sourceLastSequenceID.end() && // ensure not first packet received
208  ((newSequenceId == 0 && sourceLastSequenceID[newSourceId] !=
209  (unsigned int)-1) || // wrap around case
210  newSequenceId !=
211  sourceLastSequenceID[newSourceId] + 1)) // normal sequence case
212  {
213  // missed some messages!
214  __SS__ << "Missed packets from "
215  << cs->messages_[cs->writePointer_].getSource()
216  << "! Sequence IDs " << sourceLastSequenceID[newSourceId] << " to "
217  << newSequenceId << "." << std::endl;
218  std::cout << ss.str();
219 
220  if(++cs->writePointer_ == cs->messages_.size()) // handle wrap-around
221  cs->writePointer_ = 0;
222 
223  // generate special message to indicate missed packets
224  cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_WARNING + ss.str(),
225  cs->messageCount_++);
226  }
227 
228  // save the new last sequence ID
229  sourceLastSequenceID[newSourceId] = newSequenceId;
230 
231  if(++cs->writePointer_ == cs->messages_.size()) // handle wrap-around
232  cs->writePointer_ = 0;
233  }
234  else
235  {
236  if(i < 120) // if nothing received for 120 seconds, then something is wrong
237  // with Console configuration
238  ++i;
239 
240  sleep(1); // sleep one second, if timeout
241 
242  // every 60 heartbeatCount (2 seconds each = 1 sleep and 1 timeout) print a
243  // heartbeat message
244  if(i != 200 || // show first message, if not already a message
245  (heartbeatCount < 60 * 5 &&
246  heartbeatCount % 60 == 59)) // every ~2 min for first 5 messages
247  {
248  ++selfGeneratedMessageCount; // increment internal message count
249  __MOUT__ << "Console is alive and waiting... (if no messages, next "
250  "heartbeat is in approximately two minutes)"
251  << std::endl;
252  }
253  else if(heartbeatCount % (60 * 30) == 59) // approx every hour
254  {
255  ++selfGeneratedMessageCount; // increment internal message count
256  __MOUT__ << "Console is alive and waiting a long time... (if no "
257  "messages, next heartbeat is in approximately one hour)"
258  << std::endl;
259  }
260 
261  ++heartbeatCount;
262  }
263 
264  // if nothing received for 2 minutes seconds, then something is wrong with Console
265  // configuration after 5 seconds there is a self-send. Which will at least
266  // confirm configuration. OR if 5 generated messages and never cleared.. then
267  // the forwarding is not working.
268  if(i == 120 || selfGeneratedMessageCount == 5)
269  {
270  __COUTV__(i);
271  __COUTV__(selfGeneratedMessageCount);
272  __COUT__ << "No messages received at Console Supervisor. Exiting Console "
273  "messageFacilityReceiverWorkLoop"
274  << std::endl;
275  break; // assume something wrong, and break loop
276  }
277  }
278 
279 } // end messageFacilityReceiverWorkLoop()
280 
281 //========================================================================================================================
282 void ConsoleSupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
283 {
284  __SUP_COUT__ << "ApplicationDescriptor LID="
285  << getApplicationDescriptor()->getLocalId() << std::endl;
286  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
287  "src='/WebPath/html/Console.html?urn="
288  << getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
289 } // end defaultPage()
290 
291 //========================================================================================================================
292 // forceSupervisorPropertyValues
293 // override to force supervisor property values (and ignore user settings)
294 void ConsoleSupervisor::forceSupervisorPropertyValues()
295 {
296  CorePropertySupervisorBase::setSupervisorProperty(
297  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
298  "GetConsoleMsgs");
299  // CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NeedUsernameRequestTypes,
300  // "SaveUserPreferences | LoadUserPreferences");
301 } // end forceSupervisorPropertyValues()
302 
303 //========================================================================================================================
304 // Request
305 // Handles Web Interface requests to Console supervisor.
306 // Does not refresh cookie for automatic update checks.
307 void ConsoleSupervisor::request(const std::string& requestType,
308  cgicc::Cgicc& cgiIn,
309  HttpXmlDocument& xmlOut,
310  const WebUsers::RequestUserInfo& userInfo)
311 {
312  //__SUP_COUT__ << "requestType " << requestType << std::endl;
313 
314  // Commands:
315  // GetConsoleMsgs
316  // SaveUserPreferences
317  // LoadUserPreferences
318 
319  // Note: to report to logbook admin status use
320  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,refreshTempStr_);
321 
322  if(requestType == "GetConsoleMsgs")
323  {
324  // lindex of -1 means first time and user just gets update lcount and lindex
325  std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn, "lcount");
326  std::string lastUpdateIndexStr = CgiDataUtilities::postData(cgiIn, "lindex");
327 
328  if(lastUpdateCountStr == "" || lastUpdateIndexStr == "")
329  {
330  __SUP_COUT_ERR__ << "Invalid Parameters! lastUpdateCount="
331  << lastUpdateCountStr
332  << ", lastUpdateIndex=" << lastUpdateIndexStr << std::endl;
333  xmlOut.addTextElementToData("Error",
334  "Error - Invalid parameters for GetConsoleMsgs.");
335  return;
336  }
337 
338  clock_t lastUpdateCount;
339  sscanf(lastUpdateCountStr.c_str(), "%ld", &lastUpdateCount);
340 
341  unsigned int lastUpdateIndex;
342  sscanf(lastUpdateIndexStr.c_str(), "%u", &lastUpdateIndex);
343  // __SUP_COUT__ << "lastUpdateCount=" << lastUpdateCount <<
344  // ", lastUpdateIndex=" << lastUpdateIndex << std::endl;
345 
346  insertMessageRefresh(&xmlOut, lastUpdateCount, lastUpdateIndex);
347  }
348  else if(requestType == "SaveUserPreferences")
349  {
350  int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn, "colorIndex");
351  int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn, "showSideBar");
352  int noWrap = CgiDataUtilities::postDataAsInt(cgiIn, "noWrap");
353  int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn, "messageOnly");
354  int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn, "hideLineNumers");
355 
356  __SUP_COUT__ << "requestType " << requestType << std::endl;
357  __SUP_COUT__ << "colorIndex: " << colorIndex << std::endl;
358  __SUP_COUT__ << "showSideBar: " << showSideBar << std::endl;
359  __SUP_COUT__ << "noWrap: " << noWrap << std::endl;
360  __SUP_COUT__ << "messageOnly: " << messageOnly << std::endl;
361  __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << std::endl;
362 
363  if(userInfo.username_ == "") // should never happen?
364  {
365  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
366  << std::endl;
367  xmlOut.addTextElementToData("Error",
368  "Error - InvauserInfo.username_user found.");
369  return;
370  }
371 
372  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
373  (std::string)USERS_PREFERENCES_FILETYPE;
374 
375  __SUP_COUT__ << "Save preferences: " << fn << std::endl;
376  FILE* fp = fopen(fn.c_str(), "w");
377  if(!fp)
378  {
379  __SS__;
380  __THROW__(ss.str() + "Could not open file: " + fn);
381  }
382  fprintf(fp, "colorIndex %d\n", colorIndex);
383  fprintf(fp, "showSideBar %d\n", showSideBar);
384  fprintf(fp, "noWrap %d\n", noWrap);
385  fprintf(fp, "messageOnly %d\n", messageOnly);
386  fprintf(fp, "hideLineNumers %d\n", hideLineNumers);
387  fclose(fp);
388  }
389  else if(requestType == "LoadUserPreferences")
390  {
391  __SUP_COUT__ << "requestType " << requestType << std::endl;
392 
393  unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers;
394 
395  if(userInfo.username_ == "") // should never happen?
396  {
397  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
398  << std::endl;
399  xmlOut.addTextElementToData("Error", "Error - Invalid user found.");
400  return;
401  }
402 
403  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
404  (std::string)USERS_PREFERENCES_FILETYPE;
405 
406  __SUP_COUT__ << "Load preferences: " << fn << std::endl;
407 
408  FILE* fp = fopen(fn.c_str(), "r");
409  if(!fp)
410  {
411  // return defaults
412  __SUP_COUT__ << "Returning defaults." << std::endl;
413  xmlOut.addTextElementToData("colorIndex", "0");
414  xmlOut.addTextElementToData("showSideBar", "0");
415  xmlOut.addTextElementToData("noWrap", "1");
416  xmlOut.addTextElementToData("messageOnly", "0");
417  xmlOut.addTextElementToData("hideLineNumers", "1");
418  return;
419  }
420  fscanf(fp, "%*s %u", &colorIndex);
421  fscanf(fp, "%*s %u", &showSideBar);
422  fscanf(fp, "%*s %u", &noWrap);
423  fscanf(fp, "%*s %u", &messageOnly);
424  fscanf(fp, "%*s %u", &hideLineNumers);
425  fclose(fp);
426  __SUP_COUT__ << "colorIndex: " << colorIndex << std::endl;
427  __SUP_COUT__ << "showSideBar: " << showSideBar << std::endl;
428  __SUP_COUT__ << "noWrap: " << noWrap << std::endl;
429  __SUP_COUT__ << "messageOnly: " << messageOnly << std::endl;
430  __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << std::endl;
431 
432  char tmpStr[20];
433  sprintf(tmpStr, "%u", colorIndex);
434  xmlOut.addTextElementToData("colorIndex", tmpStr);
435  sprintf(tmpStr, "%u", showSideBar);
436  xmlOut.addTextElementToData("showSideBar", tmpStr);
437  sprintf(tmpStr, "%u", noWrap);
438  xmlOut.addTextElementToData("noWrap", tmpStr);
439  sprintf(tmpStr, "%u", messageOnly);
440  xmlOut.addTextElementToData("messageOnly", tmpStr);
441  sprintf(tmpStr, "%u", hideLineNumers);
442  xmlOut.addTextElementToData("hideLineNumers", tmpStr);
443  }
444  else
445  {
446  __SUP_SS__ << "requestType Request, " << requestType << ", not recognized."
447  << __E__;
448  __SUP_SS_THROW__;
449  }
450 } // end request()
451 
452 //========================================================================================================================
453 // ConsoleSupervisor::insertMessageRefresh()
454 // if lastUpdateClock is current, return nothing
455 // else return new messages
456 // (note: lastUpdateIndex==(unsigned int)-1 first time and returns as much as possible//
457 // nothing but lastUpdateClock)
458 //
459 // format of xml:
460 //
461 // <last_update_count/>
462 // <last_update_index/>
463 // <messages>
464 // <message_FIELDNAME*/>
465 //"Level"
466 //"Label"
467 //"Source"
468 //"Msg"
469 //"Time"
470 //"Count"
471 // </messages>
472 //
473 // NOTE: Uses std::mutex to avoid conflict with writing thread. (this is the reading
474 // thread)
475 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
476  const time_t lastUpdateCount,
477  const unsigned int lastUpdateIndex)
478 {
479  //__SUP_COUT__ << std::endl;
480 
481  // validate lastUpdateIndex
482  if(lastUpdateIndex > messages_.size() && lastUpdateIndex != (unsigned int)-1)
483  {
484  __SS__ << "Invalid lastUpdateIndex: " << lastUpdateIndex
485  << " messagesArray size = " << messages_.size() << std::endl;
486  __SS_THROW__;
487  }
488 
489  // lockout the messages array for the remainder of the scope
490  // this guarantees the reading thread can safely access the messages
491  std::lock_guard<std::mutex> lock(messageMutex_);
492 
493  // newest available read pointer is defined as always one behind writePointer_
494  refreshReadPointer_ = (writePointer_ + messages_.size() - 1) % messages_.size();
495 
496  sprintf(refreshTempStr_, "%lu", messages_[refreshReadPointer_].getCount());
497  xmlOut->addTextElementToData("last_update_count", refreshTempStr_);
498  sprintf(refreshTempStr_, "%u", refreshReadPointer_);
499  xmlOut->addTextElementToData("last_update_index", refreshTempStr_);
500 
501  if(!messages_[refreshReadPointer_].getTime()) // if no data, then no data
502  return;
503 
504  // else, send all messages since last_update_count, from
505  // last_update_index(refreshReadPointer_) on
506  if(lastUpdateIndex != (unsigned int)-1 && // if not first time, and index-count are
507  // valid then start at next message
508  messages_[lastUpdateIndex].getCount() == lastUpdateCount)
509  refreshReadPointer_ = (lastUpdateIndex + 1) % messages_.size();
510  else if(messages_[writePointer_]
511  .getTime()) // check that writePointer_ message has
512  // been initialized, therefore has wrapped
513  // around at least once already
514  {
515  // This means we have had many messages and that some were missed since last
516  // update (give as many messages as we can!)
517  xmlOut->addTextElementToData("message_overflow", "1");
518  __SUP_COUT__ << "Overflow was detected!" << std::endl;
519  refreshReadPointer_ = (writePointer_ + 1) % messages_.size();
520  }
521  else // user does not have valid index, and writePointer_ has not wrapped around, so
522  // give all new messages
523  refreshReadPointer_ = 0;
524 
525  // __SUP_COUT__ << "refreshReadPointer_: " << refreshReadPointer_ << std::endl;
526  // __SUP_COUT__ << "lastUpdateCount: " << lastUpdateCount << std::endl;
527  // __SUP_COUT__ << "writePointer_: " << writePointer_ << std::endl;
528 
529  // return anything from refreshReadPointer_ to writePointer_
530  // all should have a clock greater than lastUpdateClock
531 
532  refreshParent_ = xmlOut->addTextElementToData("messages", "");
533 
534  bool requestOutOfSync = false;
535  std::string requestOutOfSyncMsg;
536  // output oldest to new (from refreshReadPointer_ to writePointer_-1, inclusive)
537  for(/*refreshReadPointer_=<first index to read>*/;
538  refreshReadPointer_ != writePointer_;
539  refreshReadPointer_ = (refreshReadPointer_ + 1) % messages_.size())
540  {
541  if(messages_[refreshReadPointer_].getCount() < lastUpdateCount)
542  {
543  if(!requestOutOfSync) // record out of sync message once only
544  {
545  requestOutOfSync = true;
546  __SS__ << "Request is out of sync! Message count should be more recent "
547  "than update clock! "
548  << messages_[refreshReadPointer_].getCount() << " < "
549  << lastUpdateCount << std::endl;
550  requestOutOfSyncMsg = ss.str();
551  }
552  // assume these messages are new (due to a system restart)
553  // continue;
554  }
555 
556  // for all fields, give value
557  for(refreshIndex_ = 0;
558  refreshIndex_ < messages_[refreshReadPointer_].fields.size();
559  ++refreshIndex_)
560  xmlOut->addTextElementToParent(
561  "message_" +
562  messages_[refreshReadPointer_].fields[refreshIndex_].fieldName,
563  messages_[refreshReadPointer_].getField(refreshIndex_),
564  refreshParent_);
565 
566  // give timestamp also
567  sprintf(refreshTempStr_, "%lu", messages_[refreshReadPointer_].getTime());
568  xmlOut->addTextElementToParent("message_Time", refreshTempStr_, refreshParent_);
569  // give clock also
570  sprintf(refreshTempStr_, "%lu", messages_[refreshReadPointer_].getCount());
571  xmlOut->addTextElementToParent("message_Count", refreshTempStr_, refreshParent_);
572  }
573 
574  if(requestOutOfSync) // if request was out of sync, show message
575  __SUP_COUT__ << requestOutOfSyncMsg;
576 }