tdaq-develop-2025-02-12
ChatSupervisor.cc
1 #include "otsdaq-utilities/Chat/ChatSupervisor.h"
2 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
3 #include "otsdaq/Macros/CoutMacros.h"
4 #include "otsdaq/MessageFacility/MessageFacility.h"
5 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
6 
7 #include <xdaq/NamespaceURI.h>
8 
9 #include <iostream>
10 
11 using namespace ots;
12 
13 #undef __MF_SUBJECT__
14 #define __MF_SUBJECT__ "Chat"
15 
16 XDAQ_INSTANTIATOR_IMPL(ChatSupervisor)
17 
18 //==============================================================================
19 ChatSupervisor::ChatSupervisor(xdaq::ApplicationStub* stub) : CoreSupervisorBase(stub)
20 {
21  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
22 
23  ChatLastUpdateIndex = 1; // skip 0
24 }
25 
26 //==============================================================================
27 ChatSupervisor::~ChatSupervisor(void) { destroy(); }
28 
29 //==============================================================================
30 void ChatSupervisor::destroy(void)
31 {
32  // called by destructor
33 }
34 
35 //==============================================================================
36 void ChatSupervisor::defaultPage(xgi::Input* /* cgiIn */, xgi::Output* out)
37 {
38  out->getHTTPResponseHeader().addHeader("Access-Control-Allow-Origin", "*");
39  out->getHTTPResponseHeader().addHeader("Pragma", "no-cache");
40 
41  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
42  "src='/WebPath/html/Chat.html?urn="
43  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
44 } //end defaultPage()
45 
46 //==============================================================================
50 {
51  CorePropertySupervisorBase::setSupervisorProperty(
52  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
53  "RefreshChat");
54 }
55 
56 //==============================================================================
60 void ChatSupervisor::request(const std::string& requestType,
61  cgicc::Cgicc& cgiIn,
62  HttpXmlDocument& xmlOut,
63  const WebUsers::RequestUserInfo& /*userInfo*/)
64 {
65  __COUTVS__(40, requestType);
66 
67  // Commands:
68  // RefreshChat
69  // RefreshUsers
70  // SendChat
71 
72  cleanupExpiredChats();
73 
74  if(requestType == "RefreshChat")
75  {
76  std::string lastUpdateIndexString =
77  CgiDataUtilities::postData(cgiIn, "lastUpdateIndex");
78  std::string user = CgiDataUtilities::postData(cgiIn, "user");
79  uint64_t lastUpdateIndex;
80  sscanf(lastUpdateIndexString.c_str(), "%lu", &lastUpdateIndex);
81 
82  insertChatRefresh(&xmlOut, lastUpdateIndex, user);
83  }
84  else if(requestType == "RefreshUsers")
85  {
86  insertActiveUsers(&xmlOut);
87  }
88  else if(requestType == "SendChat")
89  {
90  std::string chat = CgiDataUtilities::postData(cgiIn, "chat");
91  std::string user = CgiDataUtilities::postData(cgiIn, "user");
92 
93  escapeChat(chat);
94 
95  newChat(chat, user);
96  }
97  else if(requestType == "PageUser")
98  {
99  std::string topage = CgiDataUtilities::postData(cgiIn, "topage");
100  unsigned int topageId = CgiDataUtilities::postDataAsInt(cgiIn, "topageId");
101  std::string user = CgiDataUtilities::postData(cgiIn, "user");
102 
103  __COUT__ << "Paging = " << topage.substr(0, 10)
104  << "... from user = " << user.substr(0, 10) << std::endl;
105 
106  __COUTV__(topageId);
107 
108  theRemoteWebUsers_.sendSystemMessage(topage,
109  user + " is paging you to come chat.");
110  }
111  else
112  __COUT__ << "requestType request not recognized." << std::endl;
113 
114 } // end request()
115 
116 //==============================================================================
120 void ChatSupervisor::escapeChat(std::string& /*chat*/)
121 {
122  // char reserved[] = {'"','\'','&','<','>'};
123  // std::string replace[] = {"&#34;","&#39;","&#38;","&#60;","&#62;"};
124  // for(uint64_t i=0;i<chat.size();++i)
125  // for(uint64_t j=0;j<chat.size();++j)
126  // if(chat[i] ==
127 } // end escapeChat()
128 
129 //==============================================================================
131 void ChatSupervisor::insertActiveUsers(HttpXmlDocument* xmlOut)
132 {
133  xmlOut->addTextElementToData("active_users", theRemoteWebUsers_.getActiveUserList());
134 } // end insertActiveUsers()
135 
136 //==============================================================================
143 void ChatSupervisor::insertChatRefresh(HttpXmlDocument* xmlOut,
144  uint64_t lastUpdateIndex,
145  const std::string& user)
146 {
147  newUser(user);
148 
149  if(!isLastUpdateIndexStale(lastUpdateIndex))
150  return; // if lastUpdateIndex is current, return nothing
151 
152  // return new update index, full chat user list, and new chats!
153 
154  char tempStr[50];
155  sprintf(tempStr, "%lu", ChatLastUpdateIndex);
156  xmlOut->addTextElementToData("last_update_index", tempStr);
157 
158  // get all users
159  xmlOut->addTextElementToData("chat_users", "");
160  for(uint64_t i = 0; i < ChatUsers_.size(); ++i)
161  xmlOut->addTextElementToParent("chat_user", ChatUsers_[i], "chat_users");
162 
163  //if lastUpdateIndex == 0, first request, so give give full history!
164 
165  // get all accounts
166  xmlOut->addTextElementToData("chat_history", "");
167  for(uint64_t i = 0; i < ChatHistoryEntry_.size(); ++i) // output oldest to new
168  {
169  __COUTT__ << "Chat[" << i << "]: " << ChatHistoryIndex_[i] << " vs "
170  << lastUpdateIndex << __E__;
171  if(isChatOld(ChatHistoryIndex_[i], lastUpdateIndex))
172  continue;
173 
174  xmlOut->addTextElementToParent(
175  "chat_entry", ChatHistoryEntry_[i], "chat_history");
176  xmlOut->addTextElementToParent(
177  "chat_author", ChatHistoryAuthor_[i], "chat_history");
178  sprintf(tempStr, "%lu", ChatHistoryTime_[i]);
179  xmlOut->addTextElementToParent("chat_time", tempStr, "chat_history");
180  }
181 } // end insertChatRefresh()
182 
183 //==============================================================================
186 void ChatSupervisor::newUser(const std::string& user)
187 {
188  for(uint64_t i = 0; i < ChatUsers_.size(); ++i)
189  if(ChatUsers_[i] == user)
190  {
191  ChatUsersTime_[i] = time(0); // update time
192  return; // do not add new if found
193  }
194 
195  __COUT__ << "New user: " << user << std::endl;
196  // add and increment
197  ChatUsers_.push_back(user);
198  ChatUsersTime_.push_back(time(0));
199  newChat(user + " joined the chat.",
200  "ots"); // add status message to chat, increment update
201 } // end newUser()
202 
203 //==============================================================================
206 void ChatSupervisor::newChat(const std::string& chat, const std::string& user)
207 {
208  ChatHistoryEntry_.push_back(chat);
209  ChatHistoryAuthor_.push_back(user);
210  ChatHistoryTime_.push_back(time(0));
211  ChatHistoryIndex_.push_back(incrementAndGetLastUpdate());
212 }
213 
214 //==============================================================================
217 bool ChatSupervisor::isChatOld(uint64_t chatIndex, uint64_t last)
218 {
219  return (last - chatIndex < (uint64_t(1) << 62));
220 }
221 
222 //==============================================================================
224 bool ChatSupervisor::isLastUpdateIndexStale(uint64_t last)
225 {
226  return ChatLastUpdateIndex != last;
227 }
228 
229 //==============================================================================
231 uint64_t ChatSupervisor::incrementAndGetLastUpdate()
232 {
233  if(!++ChatLastUpdateIndex)
234  ++ChatLastUpdateIndex; // skip 0
235  return ChatLastUpdateIndex;
236 }
237 
238 //==============================================================================
241 void ChatSupervisor::cleanupExpiredChats()
242 {
243  for(uint64_t i = 0; i < ChatHistoryEntry_.size(); ++i)
244  if(i >= CHAT_HISTORY_MAX_ENTRIES ||
245  ChatHistoryTime_[i] + CHAT_HISTORY_EXPIRATION_TIME < time(0)) // expired
246  {
247  removeChatHistoryEntry(i);
248  --i; // rewind loop
249  }
250  else
251  break; // chronological order, so first encountered that is still valid exit
252  // loop
253 
254  for(uint64_t i = 0; i < ChatUsers_.size(); ++i)
255  if(ChatUsersTime_[i] + CHAT_HISTORY_EXPIRATION_TIME < time(0)) // expired
256  {
257  removeChatUserEntry(i);
258  --i; // rewind loop
259  }
260  else
261  break; // chronological order, so first encountered that is still valid exit
262  // loop
263 }
264 
265 //==============================================================================
267 void ChatSupervisor::removeChatHistoryEntry(uint64_t i)
268 {
269  ChatHistoryEntry_.erase(ChatHistoryEntry_.begin() + i);
270  ChatHistoryTime_.erase(ChatHistoryTime_.begin() + i);
271  ChatHistoryAuthor_.erase(ChatHistoryAuthor_.begin() + i);
272  ChatHistoryIndex_.erase(ChatHistoryIndex_.begin() + i);
273 }
274 
275 //==============================================================================
277 void ChatSupervisor::removeChatUserEntry(uint64_t i)
278 {
279  newChat(ChatUsers_[i] + " left the chat.",
280  "ots"); // add status message to chat, increment update
281  ChatUsers_.erase(ChatUsers_.begin() + i);
282  ChatUsersTime_.erase(ChatUsersTime_.begin() + i);
283 }
virtual void request(const std::string &requestType, cgicc::Cgicc &cgiIn, HttpXmlDocument &xmlOut, const WebUsers::RequestUserInfo &userInfo) override
end forceSupervisorPropertyValues()
virtual void forceSupervisorPropertyValues(void) override