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"
24 #define USER_CONSOLE_PREF_PATH \
25 std::string(__ENV__("SERVICE_DATA_PATH")) + "/ConsolePreferences/"
26 #define USERS_PREFERENCES_FILETYPE "pref"
28 #define QUIET_CFG_FILE \
29 std::string(__ENV__("USER_DATA")) + \
30 "/MessageFacilityConfigurations/" \
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|")
38 #define __MF_SUBJECT__ "Console"
41 ConsoleSupervisor::ConsoleSupervisor(xdaq::ApplicationStub* stub)
42 : CoreSupervisorBase(stub), writePointer_(0), messageCount_(0)
44 __SUP_COUT__ <<
"Constructor started." << __E__;
46 INIT_MF(
"ConsoleSupervisor");
49 mkdir(((std::string)USER_CONSOLE_PREF_PATH).c_str(), 0755);
53 __SUP_COUT__ <<
"Constructor complete." << __E__;
57 ConsoleSupervisor::~ConsoleSupervisor(
void) { destroy(); }
59 void ConsoleSupervisor::init(
void)
64 ConsoleSupervisor::messageFacilityReceiverWorkLoop(cs);
71 void ConsoleSupervisor::destroy(
void)
82 __COUT__ << std::endl;
84 std::string configFile = QUIET_CFG_FILE;
85 FILE* fp = fopen(configFile.c_str(),
"r");
88 __SS__ <<
"File with port info could not be loaded: " << QUIET_CFG_FILE
90 __COUT__ <<
"\n" << ss.str();
97 sscanf(tmp,
"%*s %d", &myport);
101 sscanf(tmp,
"%*s %s", myip);
104 ReceiverSocket rsock(myip, myport);
113 std::lock_guard<std::mutex> lock(cs->messageMutex_);
119 __SS__ <<
"FATAL Console error. Could not initialize socket on port "
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."
130 __SS__ <<
"FATAL Console error. Could not initialize socket on port " << myport
131 <<
". Perhaps it is already in use? Exiting Console receive loop."
133 __COUT__ << ss.str();
135 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_ERROR + ss.str(),
136 cs->messageCount_++);
138 if(++cs->writePointer_ == cs->messages_.size())
139 cs->writePointer_ = 0;
146 int heartbeatCount = 0;
147 int selfGeneratedMessageCount = 0;
149 std::map<unsigned int, unsigned int>
150 sourceLastSequenceID;
153 long long newSourceId;
154 unsigned int newSequenceId;
161 buffer, 1 , 0 ,
false ) !=
166 __COUT__ <<
"Console has first message." << std::endl;
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;
185 if(selfGeneratedMessageCount)
186 --selfGeneratedMessageCount;
194 std::lock_guard<std::mutex> lock(cs->messageMutex_);
196 cs->messages_[cs->writePointer_].set(buffer, cs->messageCount_++);
199 newSourceId = cs->messages_[cs->writePointer_].getSourceIDAsNumber();
200 newSequenceId = cs->messages_[cs->writePointer_].getSequenceIDAsNumber();
205 if(newSourceId != -1 &&
206 sourceLastSequenceID.find(newSourceId) !=
207 sourceLastSequenceID.end() &&
208 ((newSequenceId == 0 && sourceLastSequenceID[newSourceId] !=
211 sourceLastSequenceID[newSourceId] + 1))
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();
220 if(++cs->writePointer_ == cs->messages_.size())
221 cs->writePointer_ = 0;
224 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_WARNING + ss.str(),
225 cs->messageCount_++);
229 sourceLastSequenceID[newSourceId] = newSequenceId;
231 if(++cs->writePointer_ == cs->messages_.size())
232 cs->writePointer_ = 0;
245 (heartbeatCount < 60 * 5 &&
246 heartbeatCount % 60 == 59))
248 ++selfGeneratedMessageCount;
249 __MOUT__ <<
"Console is alive and waiting... (if no messages, next "
250 "heartbeat is in approximately two minutes)"
253 else if(heartbeatCount % (60 * 30) == 59)
255 ++selfGeneratedMessageCount;
256 __MOUT__ <<
"Console is alive and waiting a long time... (if no "
257 "messages, next heartbeat is in approximately one hour)"
268 if(i == 120 || selfGeneratedMessageCount == 5)
271 __COUTV__(selfGeneratedMessageCount);
272 __COUT__ <<
"No messages received at Console Supervisor. Exiting Console "
273 "messageFacilityReceiverWorkLoop"
282 void ConsoleSupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
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>";
294 void ConsoleSupervisor::forceSupervisorPropertyValues()
296 CorePropertySupervisorBase::setSupervisorProperty(
297 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
307 void ConsoleSupervisor::request(
const std::string& requestType,
309 HttpXmlDocument& xmlOut,
310 const WebUsers::RequestUserInfo& userInfo)
322 if(requestType ==
"GetConsoleMsgs")
325 std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn,
"lcount");
326 std::string lastUpdateIndexStr = CgiDataUtilities::postData(cgiIn,
"lindex");
328 if(lastUpdateCountStr ==
"" || lastUpdateIndexStr ==
"")
330 __SUP_COUT_ERR__ <<
"Invalid Parameters! lastUpdateCount="
331 << lastUpdateCountStr
332 <<
", lastUpdateIndex=" << lastUpdateIndexStr << std::endl;
333 xmlOut.addTextElementToData(
"Error",
334 "Error - Invalid parameters for GetConsoleMsgs.");
338 clock_t lastUpdateCount;
339 sscanf(lastUpdateCountStr.c_str(),
"%ld", &lastUpdateCount);
341 unsigned int lastUpdateIndex;
342 sscanf(lastUpdateIndexStr.c_str(),
"%u", &lastUpdateIndex);
346 insertMessageRefresh(&xmlOut, lastUpdateCount, lastUpdateIndex);
348 else if(requestType ==
"SaveUserPreferences")
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");
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;
363 if(userInfo.username_ ==
"")
365 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_
367 xmlOut.addTextElementToData(
"Error",
368 "Error - InvauserInfo.username_user found.");
372 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." +
373 (std::string)USERS_PREFERENCES_FILETYPE;
375 __SUP_COUT__ <<
"Save preferences: " << fn << std::endl;
376 FILE* fp = fopen(fn.c_str(),
"w");
380 __THROW__(ss.str() +
"Could not open file: " + fn);
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);
389 else if(requestType ==
"LoadUserPreferences")
391 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
393 unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers;
395 if(userInfo.username_ ==
"")
397 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_
399 xmlOut.addTextElementToData(
"Error",
"Error - Invalid user found.");
403 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." +
404 (std::string)USERS_PREFERENCES_FILETYPE;
406 __SUP_COUT__ <<
"Load preferences: " << fn << std::endl;
408 FILE* fp = fopen(fn.c_str(),
"r");
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");
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);
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;
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);
446 __SUP_SS__ <<
"requestType Request, " << requestType <<
", not recognized."
475 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
476 const time_t lastUpdateCount,
477 const unsigned int lastUpdateIndex)
482 if(lastUpdateIndex > messages_.size() && lastUpdateIndex != (
unsigned int)-1)
484 __SS__ <<
"Invalid lastUpdateIndex: " << lastUpdateIndex
485 <<
" messagesArray size = " << messages_.size() << std::endl;
491 std::lock_guard<std::mutex> lock(messageMutex_);
494 refreshReadPointer_ = (writePointer_ + messages_.size() - 1) % messages_.size();
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_);
501 if(!messages_[refreshReadPointer_].getTime())
506 if(lastUpdateIndex != (
unsigned int)-1 &&
508 messages_[lastUpdateIndex].getCount() == lastUpdateCount)
509 refreshReadPointer_ = (lastUpdateIndex + 1) % messages_.size();
510 else if(messages_[writePointer_]
517 xmlOut->addTextElementToData(
"message_overflow",
"1");
518 __SUP_COUT__ <<
"Overflow was detected!" << std::endl;
519 refreshReadPointer_ = (writePointer_ + 1) % messages_.size();
523 refreshReadPointer_ = 0;
532 refreshParent_ = xmlOut->addTextElementToData(
"messages",
"");
534 bool requestOutOfSync =
false;
535 std::string requestOutOfSyncMsg;
538 refreshReadPointer_ != writePointer_;
539 refreshReadPointer_ = (refreshReadPointer_ + 1) % messages_.size())
541 if(messages_[refreshReadPointer_].getCount() < lastUpdateCount)
543 if(!requestOutOfSync)
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();
557 for(refreshIndex_ = 0;
558 refreshIndex_ < messages_[refreshReadPointer_].fields.size();
560 xmlOut->addTextElementToParent(
562 messages_[refreshReadPointer_].fields[refreshIndex_].fieldName,
563 messages_[refreshReadPointer_].getField(refreshIndex_),
567 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getTime());
568 xmlOut->addTextElementToParent(
"message_Time", refreshTempStr_, refreshParent_);
570 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getCount());
571 xmlOut->addTextElementToParent(
"message_Count", refreshTempStr_, refreshParent_);
575 __SUP_COUT__ << requestOutOfSyncMsg;