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)
80 void ConsoleSupervisor::messageFacilityReceiverWorkLoop(
ConsoleSupervisor* cs)
try
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;
246 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)
272 __COUTV__(selfGeneratedMessageCount);
273 __COUT__ <<
"No messages received at Console Supervisor. Exiting Console "
274 "messageFacilityReceiverWorkLoop"
281 catch(
const std::runtime_error& e)
283 __COUT_ERR__ <<
"Error caught at Console Supervisor thread: " << e.what() << __E__;
287 __COUT_ERR__ <<
"Unknown error caught at Console Supervisor thread." << __E__;
291 void ConsoleSupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
293 __SUP_COUT__ <<
"ApplicationDescriptor LID="
294 << getApplicationDescriptor()->getLocalId() << std::endl;
295 *out <<
"<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
296 "src='/WebPath/html/Console.html?urn="
297 << getApplicationDescriptor()->getLocalId() <<
"'></frameset></html>";
303 void ConsoleSupervisor::forceSupervisorPropertyValues()
305 CorePropertySupervisorBase::setSupervisorProperty(
306 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
316 void ConsoleSupervisor::request(
const std::string& requestType,
318 HttpXmlDocument& xmlOut,
319 const WebUsers::RequestUserInfo& userInfo)
331 if(requestType ==
"GetConsoleMsgs")
334 std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn,
"lcount");
335 std::string lastUpdateIndexStr = CgiDataUtilities::postData(cgiIn,
"lindex");
337 if(lastUpdateCountStr ==
"" || lastUpdateIndexStr ==
"")
339 __SUP_COUT_ERR__ <<
"Invalid Parameters! lastUpdateCount="
340 << lastUpdateCountStr
341 <<
", lastUpdateIndex=" << lastUpdateIndexStr << std::endl;
342 xmlOut.addTextElementToData(
"Error",
343 "Error - Invalid parameters for GetConsoleMsgs.");
347 clock_t lastUpdateCount;
348 sscanf(lastUpdateCountStr.c_str(),
"%ld", &lastUpdateCount);
350 unsigned int lastUpdateIndex;
351 sscanf(lastUpdateIndexStr.c_str(),
"%u", &lastUpdateIndex);
355 insertMessageRefresh(&xmlOut, lastUpdateCount, lastUpdateIndex);
357 else if(requestType ==
"SaveUserPreferences")
359 int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn,
"colorIndex");
360 int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn,
"showSideBar");
361 int noWrap = CgiDataUtilities::postDataAsInt(cgiIn,
"noWrap");
362 int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn,
"messageOnly");
363 int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn,
"hideLineNumers");
365 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
366 __SUP_COUT__ <<
"colorIndex: " << colorIndex << std::endl;
367 __SUP_COUT__ <<
"showSideBar: " << showSideBar << std::endl;
368 __SUP_COUT__ <<
"noWrap: " << noWrap << std::endl;
369 __SUP_COUT__ <<
"messageOnly: " << messageOnly << std::endl;
370 __SUP_COUT__ <<
"hideLineNumers: " << hideLineNumers << std::endl;
372 if(userInfo.username_ ==
"")
374 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_
376 xmlOut.addTextElementToData(
"Error",
377 "Error - InvauserInfo.username_user found.");
381 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." +
382 (std::string)USERS_PREFERENCES_FILETYPE;
384 __SUP_COUT__ <<
"Save preferences: " << fn << std::endl;
385 FILE* fp = fopen(fn.c_str(),
"w");
389 __THROW__(ss.str() +
"Could not open file: " + fn);
391 fprintf(fp,
"colorIndex %d\n", colorIndex);
392 fprintf(fp,
"showSideBar %d\n", showSideBar);
393 fprintf(fp,
"noWrap %d\n", noWrap);
394 fprintf(fp,
"messageOnly %d\n", messageOnly);
395 fprintf(fp,
"hideLineNumers %d\n", hideLineNumers);
398 else if(requestType ==
"LoadUserPreferences")
400 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
402 unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers;
404 if(userInfo.username_ ==
"")
406 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_
408 xmlOut.addTextElementToData(
"Error",
"Error - Invalid user found.");
412 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." +
413 (std::string)USERS_PREFERENCES_FILETYPE;
415 __SUP_COUT__ <<
"Load preferences: " << fn << std::endl;
417 FILE* fp = fopen(fn.c_str(),
"r");
421 __SUP_COUT__ <<
"Returning defaults." << std::endl;
422 xmlOut.addTextElementToData(
"colorIndex",
"0");
423 xmlOut.addTextElementToData(
"showSideBar",
"0");
424 xmlOut.addTextElementToData(
"noWrap",
"1");
425 xmlOut.addTextElementToData(
"messageOnly",
"0");
426 xmlOut.addTextElementToData(
"hideLineNumers",
"1");
429 fscanf(fp,
"%*s %u", &colorIndex);
430 fscanf(fp,
"%*s %u", &showSideBar);
431 fscanf(fp,
"%*s %u", &noWrap);
432 fscanf(fp,
"%*s %u", &messageOnly);
433 fscanf(fp,
"%*s %u", &hideLineNumers);
435 __SUP_COUT__ <<
"colorIndex: " << colorIndex << std::endl;
436 __SUP_COUT__ <<
"showSideBar: " << showSideBar << std::endl;
437 __SUP_COUT__ <<
"noWrap: " << noWrap << std::endl;
438 __SUP_COUT__ <<
"messageOnly: " << messageOnly << std::endl;
439 __SUP_COUT__ <<
"hideLineNumers: " << hideLineNumers << std::endl;
442 sprintf(tmpStr,
"%u", colorIndex);
443 xmlOut.addTextElementToData(
"colorIndex", tmpStr);
444 sprintf(tmpStr,
"%u", showSideBar);
445 xmlOut.addTextElementToData(
"showSideBar", tmpStr);
446 sprintf(tmpStr,
"%u", noWrap);
447 xmlOut.addTextElementToData(
"noWrap", tmpStr);
448 sprintf(tmpStr,
"%u", messageOnly);
449 xmlOut.addTextElementToData(
"messageOnly", tmpStr);
450 sprintf(tmpStr,
"%u", hideLineNumers);
451 xmlOut.addTextElementToData(
"hideLineNumers", tmpStr);
455 __SUP_SS__ <<
"requestType Request, " << requestType <<
", not recognized."
484 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
485 const time_t lastUpdateCount,
486 const unsigned int lastUpdateIndex)
491 if(lastUpdateIndex > messages_.size() && lastUpdateIndex != (
unsigned int)-1)
493 __SS__ <<
"Invalid lastUpdateIndex: " << lastUpdateIndex
494 <<
" messagesArray size = " << messages_.size() << std::endl;
500 std::lock_guard<std::mutex> lock(messageMutex_);
503 refreshReadPointer_ = (writePointer_ + messages_.size() - 1) % messages_.size();
505 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getCount());
506 xmlOut->addTextElementToData(
"last_update_count", refreshTempStr_);
507 sprintf(refreshTempStr_,
"%u", refreshReadPointer_);
508 xmlOut->addTextElementToData(
"last_update_index", refreshTempStr_);
510 if(!messages_[refreshReadPointer_].getTime())
515 if(lastUpdateIndex != (
unsigned int)-1 &&
517 messages_[lastUpdateIndex].getCount() == lastUpdateCount)
518 refreshReadPointer_ = (lastUpdateIndex + 1) % messages_.size();
519 else if(messages_[writePointer_]
526 xmlOut->addTextElementToData(
"message_overflow",
"1");
527 __SUP_COUT__ <<
"Overflow was detected!" << std::endl;
528 refreshReadPointer_ = (writePointer_ + 1) % messages_.size();
532 refreshReadPointer_ = 0;
541 refreshParent_ = xmlOut->addTextElementToData(
"messages",
"");
543 bool requestOutOfSync =
false;
544 std::string requestOutOfSyncMsg;
547 refreshReadPointer_ != writePointer_;
548 refreshReadPointer_ = (refreshReadPointer_ + 1) % messages_.size())
550 if(messages_[refreshReadPointer_].getCount() < lastUpdateCount)
552 if(!requestOutOfSync)
554 requestOutOfSync =
true;
555 __SS__ <<
"Request is out of sync! Message count should be more recent "
556 "than update clock! "
557 << messages_[refreshReadPointer_].getCount() <<
" < "
558 << lastUpdateCount << std::endl;
559 requestOutOfSyncMsg = ss.str();
566 for(refreshIndex_ = 0;
567 refreshIndex_ < messages_[refreshReadPointer_].fields.size();
569 xmlOut->addTextElementToParent(
571 messages_[refreshReadPointer_].fields[refreshIndex_].fieldName,
572 messages_[refreshReadPointer_].getField(refreshIndex_),
576 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getTime());
577 xmlOut->addTextElementToParent(
"message_Time", refreshTempStr_, refreshParent_);
579 sprintf(refreshTempStr_,
"%lu", messages_[refreshReadPointer_].getCount());
580 xmlOut->addTextElementToParent(
"message_Count", refreshTempStr_, refreshParent_);
584 __SUP_COUT__ << requestOutOfSyncMsg;