1 #include "otsdaq-utilities/Console/ConsoleSupervisor.h"
2 #include "otsdaq-core/MessageFacility/MessageFacility.h"
3 #include "otsdaq-core/Macros/CoutMacros.h"
4 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
5 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
6 #include "otsdaq-core/NetworkUtilities/ReceiverSocket.h"
7 #include <xdaq/NamespaceURI.h>
25 #define USER_CONSOLE_PREF_PATH std::string(getenv("SERVICE_DATA_PATH")) + "/ConsolePreferences/"
26 #define USERS_PREFERENCES_FILETYPE "pref"
28 #define QUIET_CFG_FILE std::string(getenv("USER_DATA")) + "/MessageFacilityConfigurations/QuietForwarder.cfg"
30 #define CONSOLE_SPECIAL_ERROR std::string("||0|||Error|Console|-1||ConsoleSupervisor|")
31 #define CONSOLE_SPECIAL_WARNING std::string("||0|||Warning|Console|-1||ConsoleSupervisor|")
34 #define __MF_SUBJECT__ "Console"
38 ConsoleSupervisor::ConsoleSupervisor(xdaq::ApplicationStub* stub)
39 : CoreSupervisorBase (stub)
43 __SUP_COUT__ <<
"Constructor started." << __E__;
45 INIT_MF(
"ConsoleSupervisor");
48 mkdir(((std::string)USER_CONSOLE_PREF_PATH).c_str(), 0755);
52 __SUP_COUT__ <<
"Constructor complete." << __E__;
56 ConsoleSupervisor::~ConsoleSupervisor(
void)
61 void ConsoleSupervisor::init(
void)
64 std::thread([](
ConsoleSupervisor *cs){ ConsoleSupervisor::messageFacilityReceiverWorkLoop(cs); },
this).detach();
68 void ConsoleSupervisor::destroy(
void)
79 __COUT__ << std::endl;
81 std::string configFile = QUIET_CFG_FILE;
82 FILE *fp = fopen(configFile.c_str(),
"r");
85 __SS__ <<
"File with port info could not be loaded: " <<
86 QUIET_CFG_FILE << std::endl;
87 __COUT__ <<
"\n" << ss.str();
94 sscanf(tmp,
"%*s %d",&myport);
98 sscanf(tmp,
"%*s %s",myip);
101 ReceiverSocket rsock(myip,myport);
110 std::lock_guard<std::mutex> lock(cs->messageMutex_);
116 __SS__ <<
"FATAL Console error. Could not initialize socket on port " <<
117 myport <<
". Perhaps the port is already in use? Check for multiple stale instances of otsdaq processes, or notify admins." <<
118 " Multiple instances of otsdaq on the same node should be possible, but port numbers must be unique." << std::endl;
123 __SS__ <<
"FATAL Console error. Could not initialize socket on port " <<
124 myport <<
". Perhaps it is already in use? Exiting Console receive loop." << std::endl;
125 __COUT__ << ss.str();
128 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_ERROR +
130 cs->messageCount_++);
133 if(++cs->writePointer_ == cs->messages_.size())
134 cs->writePointer_ = 0;
141 int heartbeatCount = 0;
142 int selfGeneratedMessageCount = 0;
144 std::map<unsigned int, unsigned int> sourceLastSequenceID;
145 long long newSourceId;
146 unsigned int newSequenceId;
152 if(rsock.receive(buffer,1 ,0,
157 __COUT__ <<
"Console has first message." << std::endl;
160 __MOUT__ <<
"DEBUG messages look like this." << std::endl;
161 __MOUT_INFO__ <<
"INFO messages look like this." << std::endl;
162 __MOUT_WARN__ <<
"WARNING messages look like this." << std::endl;
163 __MOUT_ERR__ <<
"ERROR messages look like this." << std::endl;
177 if(selfGeneratedMessageCount)
178 --selfGeneratedMessageCount;
186 std::lock_guard<std::mutex> lock(cs->messageMutex_);
188 cs->messages_[cs->writePointer_].set(buffer,cs->messageCount_++);
192 newSourceId = cs->messages_[cs->writePointer_].getSourceIDAsNumber();
193 newSequenceId = cs->messages_[cs->writePointer_].getSequenceIDAsNumber();
198 if(newSourceId != -1 && sourceLastSequenceID.find(newSourceId) !=
199 sourceLastSequenceID.end() &&
200 ((newSequenceId == 0 &&
201 sourceLastSequenceID[newSourceId] != (
unsigned int)-1) ||
202 newSequenceId != sourceLastSequenceID[newSourceId] + 1))
205 __SS__ <<
"Missed packets from " <<
206 cs->messages_[cs->writePointer_].getSource() <<
"! Sequence IDs " <<
207 sourceLastSequenceID[newSourceId] <<
208 " to " << newSequenceId <<
"." << std::endl;
209 std::cout << ss.str();
211 if(++cs->writePointer_ == cs->messages_.size())
212 cs->writePointer_ = 0;
215 cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_WARNING +
217 cs->messageCount_++);
221 sourceLastSequenceID[newSourceId] = newSequenceId;
223 if(++cs->writePointer_ == cs->messages_.size())
224 cs->writePointer_ = 0;
236 (heartbeatCount < 60*5 && heartbeatCount%60 == 59))
238 ++selfGeneratedMessageCount;
239 __MOUT__ <<
"Console is alive and waiting... (if no messages, next heartbeat is in approximately two minutes)" << std::endl;
241 else if(heartbeatCount%(60*30) == 59)
243 ++selfGeneratedMessageCount;
244 __MOUT__ <<
"Console is alive and waiting a long time... (if no messages, next heartbeat is in approximately one hour)" << std::endl;
253 if(i==120 || selfGeneratedMessageCount == 5)
255 __COUT__ <<
"No messages received at Console Supervisor. Exiting Console messageFacilityReceiverWorkLoop" << std::endl;
263 void ConsoleSupervisor::defaultPage(xgi::Input * in, xgi::Output * out )
265 __SUP_COUT__ <<
"ApplicationDescriptor LID=" << getApplicationDescriptor()->getLocalId() << std::endl;
266 *out <<
"<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame src='/WebPath/html/Console.html?urn=" <<
267 getApplicationDescriptor()->getLocalId() <<
"'></frameset></html>";
273 void ConsoleSupervisor::forceSupervisorPropertyValues()
275 CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
285 void ConsoleSupervisor::request(
const std::string& requestType, cgicc::Cgicc& cgiIn,
286 HttpXmlDocument& xmlOut,
const WebUsers::RequestUserInfo& userInfo)
298 if(requestType ==
"GetConsoleMsgs")
301 std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn,
"lcount");
302 std::string lastUpdateIndexStr = CgiDataUtilities::postData(cgiIn,
"lindex");
304 if(lastUpdateCountStr ==
"" || lastUpdateIndexStr ==
"")
306 __SUP_COUT_ERR__ <<
"Invalid Parameters! lastUpdateCount=" << lastUpdateCountStr <<
307 ", lastUpdateIndex=" << lastUpdateIndexStr << std::endl;
308 xmlOut.addTextElementToData(
"Error",
"Error - Invalid parameters for GetConsoleMsgs.");
312 clock_t lastUpdateCount;
313 sscanf(lastUpdateCountStr.c_str(),
"%ld",&lastUpdateCount);
315 unsigned int lastUpdateIndex;
316 sscanf(lastUpdateIndexStr.c_str(),
"%u",&lastUpdateIndex);
320 insertMessageRefresh(&xmlOut,lastUpdateCount,lastUpdateIndex);
322 else if(requestType ==
"SaveUserPreferences")
324 int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn,
"colorIndex");
325 int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn,
"showSideBar");
326 int noWrap = CgiDataUtilities::postDataAsInt(cgiIn,
"noWrap");
327 int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn,
"messageOnly");
328 int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn,
"hideLineNumers");
330 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
331 __SUP_COUT__ <<
"colorIndex: " << colorIndex << std::endl;
332 __SUP_COUT__ <<
"showSideBar: " << showSideBar << std::endl;
333 __SUP_COUT__ <<
"noWrap: " << noWrap << std::endl;
334 __SUP_COUT__ <<
"messageOnly: " << messageOnly << std::endl;
335 __SUP_COUT__ <<
"hideLineNumers: " << hideLineNumers << std::endl;
337 if(userInfo.username_ ==
"")
339 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_ << std::endl;
340 xmlOut.addTextElementToData(
"Error",
"Error - InvauserInfo.username_user found.");
344 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." + (std::string)USERS_PREFERENCES_FILETYPE;
346 __SUP_COUT__ <<
"Save preferences: " << fn << std::endl;
347 FILE *fp = fopen(fn.c_str(),
"w");
349 {__SS__;__THROW__(ss.str()+
"Could not open file: " + fn);}
350 fprintf(fp,
"colorIndex %d\n",colorIndex);
351 fprintf(fp,
"showSideBar %d\n",showSideBar);
352 fprintf(fp,
"noWrap %d\n",noWrap);
353 fprintf(fp,
"messageOnly %d\n",messageOnly);
354 fprintf(fp,
"hideLineNumers %d\n",hideLineNumers);
357 else if(requestType ==
"LoadUserPreferences")
359 __SUP_COUT__ <<
"requestType " << requestType << std::endl;
361 unsigned int colorIndex,showSideBar,noWrap,messageOnly,hideLineNumers;
363 if(userInfo.username_ ==
"")
365 __SUP_COUT_ERR__ <<
"Invalid user found! user=" << userInfo.username_ << std::endl;
366 xmlOut.addTextElementToData(
"Error",
"Error - Invalid user found.");
370 std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ +
"." + (std::string)USERS_PREFERENCES_FILETYPE;
372 __SUP_COUT__ <<
"Load preferences: " << fn << std::endl;
374 FILE *fp = fopen(fn.c_str(),
"r");
378 __SUP_COUT__ <<
"Returning defaults." << std::endl;
379 xmlOut.addTextElementToData(
"colorIndex",
"0");
380 xmlOut.addTextElementToData(
"showSideBar",
"0");
381 xmlOut.addTextElementToData(
"noWrap",
"1");
382 xmlOut.addTextElementToData(
"messageOnly",
"0");
383 xmlOut.addTextElementToData(
"hideLineNumers",
"1");
386 fscanf(fp,
"%*s %u",&colorIndex);
387 fscanf(fp,
"%*s %u",&showSideBar);
388 fscanf(fp,
"%*s %u",&noWrap);
389 fscanf(fp,
"%*s %u",&messageOnly);
390 fscanf(fp,
"%*s %u",&hideLineNumers);
392 __SUP_COUT__ <<
"colorIndex: " << colorIndex << std::endl;
393 __SUP_COUT__ <<
"showSideBar: " << showSideBar << std::endl;
394 __SUP_COUT__ <<
"noWrap: " << noWrap << std::endl;
395 __SUP_COUT__ <<
"messageOnly: " << messageOnly << std::endl;
396 __SUP_COUT__ <<
"hideLineNumers: " << hideLineNumers << std::endl;
399 sprintf(tmpStr,
"%u",colorIndex);
400 xmlOut.addTextElementToData(
"colorIndex",tmpStr);
401 sprintf(tmpStr,
"%u",showSideBar);
402 xmlOut.addTextElementToData(
"showSideBar",tmpStr);
403 sprintf(tmpStr,
"%u",noWrap);
404 xmlOut.addTextElementToData(
"noWrap",tmpStr);
405 sprintf(tmpStr,
"%u",messageOnly);
406 xmlOut.addTextElementToData(
"messageOnly",tmpStr);
407 sprintf(tmpStr,
"%u",hideLineNumers);
408 xmlOut.addTextElementToData(
"hideLineNumers",tmpStr);
435 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument *xmlOut,
436 const time_t lastUpdateCount,
const unsigned int lastUpdateIndex)
441 if(lastUpdateIndex > messages_.size() &&
442 lastUpdateIndex != (
unsigned int)-1)
444 __SS__ <<
"Invalid lastUpdateIndex: " << lastUpdateIndex <<
445 " messagesArray size = " << messages_.size() << std::endl;
451 std::lock_guard<std::mutex> lock(messageMutex_);
454 refreshReadPointer_ = (writePointer_ + messages_.size() - 1) % messages_.size();
456 sprintf(refreshTempStr_,
"%lu",messages_[refreshReadPointer_].getCount());
457 xmlOut->addTextElementToData(
"last_update_count",refreshTempStr_);
458 sprintf(refreshTempStr_,
"%u",refreshReadPointer_);
459 xmlOut->addTextElementToData(
"last_update_index",refreshTempStr_);
461 if(!messages_[refreshReadPointer_].getTime())
465 if(lastUpdateIndex != (
unsigned int)-1 &&
466 messages_[lastUpdateIndex].getCount() == lastUpdateCount)
467 refreshReadPointer_ = (lastUpdateIndex+1) % messages_.size();
468 else if(messages_[writePointer_].getTime())
472 xmlOut->addTextElementToData(
"message_overflow",
"1");
473 __SUP_COUT__ <<
"Overflow was detected!" << std::endl;
474 refreshReadPointer_ = (writePointer_+1) % messages_.size();
477 refreshReadPointer_ = 0;
486 refreshParent_ = xmlOut->addTextElementToData(
"messages",
"");
488 bool requestOutOfSync =
false;
489 std::string requestOutOfSyncMsg;
492 refreshReadPointer_ != writePointer_;
493 refreshReadPointer_ = (refreshReadPointer_+1) % messages_.size())
495 if(messages_[refreshReadPointer_].getCount() < lastUpdateCount)
497 if(!requestOutOfSync)
499 requestOutOfSync =
true;
500 __SS__ <<
"Request is out of sync! Message count should be more recent than update clock! " <<
501 messages_[refreshReadPointer_].getCount() <<
" < " <<
502 lastUpdateCount << std::endl;
503 requestOutOfSyncMsg = ss.str();
510 for(refreshIndex_=0; refreshIndex_ < messages_[refreshReadPointer_].fields.size();++refreshIndex_)
511 xmlOut->addTextElementToParent(
"message_" +
512 messages_[refreshReadPointer_].fields[refreshIndex_].fieldName,
513 messages_[refreshReadPointer_].getField(refreshIndex_), refreshParent_);
516 sprintf(refreshTempStr_,
"%lu",messages_[refreshReadPointer_].getTime());
517 xmlOut->addTextElementToParent(
"message_Time",
518 refreshTempStr_, refreshParent_);
520 sprintf(refreshTempStr_,
"%lu",messages_[refreshReadPointer_].getCount());
521 xmlOut->addTextElementToParent(
"message_Count",
522 refreshTempStr_, refreshParent_);
526 __SUP_COUT__ << requestOutOfSyncMsg;