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(getenv("SERVICE_DATA_PATH")) + "/ConsolePreferences/"
26 #define USERS_PREFERENCES_FILETYPE "pref"
28 #define QUIET_CFG_FILE \
29 std::string(getenv("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;
195 std::lock_guard<std::mutex> lock(cs->messageMutex_);
197 cs->messages_[cs->writePointer_].set(buffer, cs->messageCount_++);
200 newSourceId = cs->messages_[cs->writePointer_].getSourceIDAsNumber();
201 newSequenceId = cs->messages_[cs->writePointer_].getSequenceIDAsNumber();
206 if(newSourceId != -1 &&
207 sourceLastSequenceID.find(newSourceId) !=
208 sourceLastSequenceID.end() &&
209 ((newSequenceId == 0 && sourceLastSequenceID[newSourceId] !=
212 sourceLastSequenceID[newSourceId] + 1))
215 __SS__ <<
"Missed packets from "
216 << cs->messages_[cs->writePointer_].getSource()
217 <<
"! Sequence IDs " << sourceLastSequenceID[newSourceId] <<
" to "
218 << newSequenceId <<
"." << std::endl;
219 std::cout << ss.str();
221 if(++cs->writePointer_ == cs->messages_.size())
222 cs->writePointer_ = 0;
225 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_WARNING + ss.str(),
226 cs->messageCount_++);
230 sourceLastSequenceID[newSourceId] = newSequenceId;
232 if(++cs->writePointer_ == cs->messages_.size())
233 cs->writePointer_ = 0;
246 (heartbeatCount < 60 * 5 &&
247 heartbeatCount % 60 == 59))
249 ++selfGeneratedMessageCount;
250 __MOUT__ <<
"Console is alive and waiting... (if no messages, next "
251 "heartbeat is in approximately two minutes)"
254 else if(heartbeatCount % (60 * 30) == 59)
256 ++selfGeneratedMessageCount;
257 __MOUT__ <<
"Console is alive and waiting a long time... (if no "
258 "messages, next heartbeat is in approximately one hour)"
269 if(i == 120 || selfGeneratedMessageCount == 5)
271 __COUT__ <<
"No messages received at Console Supervisor. Exiting Console "
272 "messageFacilityReceiverWorkLoop"
281 void ConsoleSupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
283 __SUP_COUT__ <<
"ApplicationDescriptor LID="
284 << getApplicationDescriptor()->getLocalId() << std::endl;
285 *out <<
"<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
286 "src='/WebPath/html/Console.html?urn="
287 << getApplicationDescriptor()->getLocalId() <<
"'></frameset></html>";
293 void ConsoleSupervisor::forceSupervisorPropertyValues()
295 CorePropertySupervisorBase::setSupervisorProperty(
296 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
306 void ConsoleSupervisor::request(
const std::string& requestType,
308 HttpXmlDocument& xmlOut,
309 const WebUsers::RequestUserInfo& userInfo)
321 if(requestType ==
"GetConsoleMsgs")
324 std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn,
"lcount");
325 std::string lastUpdateIndexStr = CgiDataUtilities::postData(cgiIn,
"lindex");
327 if(lastUpdateCountStr ==
"" || lastUpdateIndexStr ==
"")
329 __SUP_COUT_ERR__ <<
"Invalid Parameters! lastUpdateCount="
330 << lastUpdateCountStr
331 <<
", lastUpdateIndex=" << lastUpdateIndexStr << std::endl;
332 xmlOut.addTextElementToData(
"Error",
333 "Error - Invalid parameters for GetConsoleMsgs.");
337 clock_t lastUpdateCount;
338 sscanf(lastUpdateCountStr.c_str(),
"%ld", &lastUpdateCount);
340 unsigned int lastUpdateIndex;
341 sscanf(lastUpdateIndexStr.c_str(),
"%u", &lastUpdateIndex);
345 insertMessageRefresh(&xmlOut, lastUpdateCount, lastUpdateIndex);
347 else if(requestType ==
"SaveUserPreferences")
349 int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn,
"colorIndex");
350 int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn,
"showSideBar");
351 int noWrap = CgiDataUtilities::postDataAsInt(cgiIn,
"noWrap");
352 int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn,
"messageOnly");
353 int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn,
"hideLineNumers");
355 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
356 __SUP_COUT__ <<
"colorIndex: " << colorIndex << std::endl;
357 __SUP_COUT__ <<
"showSideBar: " << showSideBar << std::endl;
358 __SUP_COUT__ <<
"noWrap: " << noWrap << std::endl;
359 __SUP_COUT__ <<
"messageOnly: " << messageOnly << std::endl;
360 __SUP_COUT__ <<
"hideLineNumers: " << hideLineNumers << std::endl;
362 if(userInfo.username_ ==
"")
364 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_
366 xmlOut.addTextElementToData(
"Error",
367 "Error - InvauserInfo.username_user found.");
371 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." +
372 (std::string)USERS_PREFERENCES_FILETYPE;
374 __SUP_COUT__ <<
"Save preferences: " << fn << std::endl;
375 FILE* fp = fopen(fn.c_str(),
"w");
379 __THROW__(ss.str() +
"Could not open file: " + fn);
381 fprintf(fp,
"colorIndex %d\n", colorIndex);
382 fprintf(fp,
"showSideBar %d\n", showSideBar);
383 fprintf(fp,
"noWrap %d\n", noWrap);
384 fprintf(fp,
"messageOnly %d\n", messageOnly);
385 fprintf(fp,
"hideLineNumers %d\n", hideLineNumers);
388 else if(requestType ==
"LoadUserPreferences")
390 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
392 unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers;
394 if(userInfo.username_ ==
"")
396 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_
398 xmlOut.addTextElementToData(
"Error",
"Error - Invalid user found.");
402 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." +
403 (std::string)USERS_PREFERENCES_FILETYPE;
405 __SUP_COUT__ <<
"Load preferences: " << fn << std::endl;
407 FILE* fp = fopen(fn.c_str(),
"r");
411 __SUP_COUT__ <<
"Returning defaults." << std::endl;
412 xmlOut.addTextElementToData(
"colorIndex",
"0");
413 xmlOut.addTextElementToData(
"showSideBar",
"0");
414 xmlOut.addTextElementToData(
"noWrap",
"1");
415 xmlOut.addTextElementToData(
"messageOnly",
"0");
416 xmlOut.addTextElementToData(
"hideLineNumers",
"1");
419 fscanf(fp,
"%*s %u", &colorIndex);
420 fscanf(fp,
"%*s %u", &showSideBar);
421 fscanf(fp,
"%*s %u", &noWrap);
422 fscanf(fp,
"%*s %u", &messageOnly);
423 fscanf(fp,
"%*s %u", &hideLineNumers);
425 __SUP_COUT__ <<
"colorIndex: " << colorIndex << std::endl;
426 __SUP_COUT__ <<
"showSideBar: " << showSideBar << std::endl;
427 __SUP_COUT__ <<
"noWrap: " << noWrap << std::endl;
428 __SUP_COUT__ <<
"messageOnly: " << messageOnly << std::endl;
429 __SUP_COUT__ <<
"hideLineNumers: " << hideLineNumers << std::endl;
432 sprintf(tmpStr,
"%u", colorIndex);
433 xmlOut.addTextElementToData(
"colorIndex", tmpStr);
434 sprintf(tmpStr,
"%u", showSideBar);
435 xmlOut.addTextElementToData(
"showSideBar", tmpStr);
436 sprintf(tmpStr,
"%u", noWrap);
437 xmlOut.addTextElementToData(
"noWrap", tmpStr);
438 sprintf(tmpStr,
"%u", messageOnly);
439 xmlOut.addTextElementToData(
"messageOnly", tmpStr);
440 sprintf(tmpStr,
"%u", hideLineNumers);
441 xmlOut.addTextElementToData(
"hideLineNumers", tmpStr);
445 __SUP_SS__ <<
"requestType Request, " << requestType <<
", not recognized."
474 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
475 const time_t lastUpdateCount,
476 const unsigned int lastUpdateIndex)
481 if(lastUpdateIndex > messages_.size() && lastUpdateIndex != (
unsigned int)-1)
483 __SS__ <<
"Invalid lastUpdateIndex: " << lastUpdateIndex
484 <<
" messagesArray size = " << messages_.size() << std::endl;
490 std::lock_guard<std::mutex> lock(messageMutex_);
493 refreshReadPointer_ = (writePointer_ + messages_.size() - 1) % messages_.size();
495 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getCount());
496 xmlOut->addTextElementToData(
"last_update_count", refreshTempStr_);
497 sprintf(refreshTempStr_,
"%u", refreshReadPointer_);
498 xmlOut->addTextElementToData(
"last_update_index", refreshTempStr_);
500 if(!messages_[refreshReadPointer_].getTime())
505 if(lastUpdateIndex != (
unsigned int)-1 &&
507 messages_[lastUpdateIndex].getCount() == lastUpdateCount)
508 refreshReadPointer_ = (lastUpdateIndex + 1) % messages_.size();
509 else if(messages_[writePointer_]
516 xmlOut->addTextElementToData(
"message_overflow",
"1");
517 __SUP_COUT__ <<
"Overflow was detected!" << std::endl;
518 refreshReadPointer_ = (writePointer_ + 1) % messages_.size();
522 refreshReadPointer_ = 0;
531 refreshParent_ = xmlOut->addTextElementToData(
"messages",
"");
533 bool requestOutOfSync =
false;
534 std::string requestOutOfSyncMsg;
537 refreshReadPointer_ != writePointer_;
538 refreshReadPointer_ = (refreshReadPointer_ + 1) % messages_.size())
540 if(messages_[refreshReadPointer_].getCount() < lastUpdateCount)
542 if(!requestOutOfSync)
544 requestOutOfSync =
true;
545 __SS__ <<
"Request is out of sync! Message count should be more recent "
546 "than update clock! "
547 << messages_[refreshReadPointer_].getCount() <<
" < "
548 << lastUpdateCount << std::endl;
549 requestOutOfSyncMsg = ss.str();
556 for(refreshIndex_ = 0;
557 refreshIndex_ < messages_[refreshReadPointer_].fields.size();
559 xmlOut->addTextElementToParent(
561 messages_[refreshReadPointer_].fields[refreshIndex_].fieldName,
562 messages_[refreshReadPointer_].getField(refreshIndex_),
566 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getTime());
567 xmlOut->addTextElementToParent(
"message_Time", refreshTempStr_, refreshParent_);
569 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getCount());
570 xmlOut->addTextElementToParent(
"message_Count", refreshTempStr_, refreshParent_);
574 __SUP_COUT__ << requestOutOfSyncMsg;