Add infos into target window
[ryzomcore.git] / ryzom / server / src / admin_modules / aes_client_module.cpp
blob23024247a56461ae42a1f4c4be091dff0209a529
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "stdpch.h"
19 #include "nel/misc/singleton.h"
20 #include "nel/net/module.h"
21 #include "nel/net/module_builder_parts.h"
22 #include "nel/net/unified_network.h"
23 #include "nel/net/service.h"
25 #include "admin_modules_itf.h"
27 using namespace std;
28 using namespace NLMISC;
29 using namespace NLNET;
31 void aesclient_forceLink() {}
33 namespace ADMIN
35 class CAdminExecutorServiceClient
36 : /*public CManualSingleton<CAdminExecutorService>,*/
37 public CEmptyModuleServiceBehav<CEmptyModuleCommBehav<CEmptySocketBehav<CModuleBase> > >,
38 public CAdminExecutorServiceClientSkel
41 enum
43 // Maximum time without sending report string (a kind of 'keep alive')
44 MAX_DELAY_BETWEEN_REPORT = 30, // 30 seconds
47 /// Flag to inform AES that we don't want to be affected by shard orders
48 bool _DontUseShardOrders;
50 /// Admin executor service module
51 TModuleProxyPtr _AdminExecutorService;
53 /// Date of last state reporting to AES
54 uint32 _LastStateReport;
56 /// Last date of status string update
57 uint32 _LastStatusStringReport;
58 /// Last status string sent (to avoid double send)
59 string _LastSentStatus;
61 /// The service alias (must be an unique name)
62 string _ServiceAlias;
64 /// A cache of the value because reading it is very slow
65 uint32 _ProcessUsedMemory;
67 struct TGraphSample
69 // The date of the sample (in second)
70 uint32 TimeStamp;
71 // The date of the sample (in ms)
72 TTime HighRezTimeStamp;
73 // sample value
74 double SampleValue;
76 struct TGraphVarInfo
78 /// Name of the graphed var
79 string VarName;
80 /** Mean time between two sample in ms
81 * (in fact, if will be the min period)
82 * Set it to 1 to have a sample at each tick
83 * If the period is set less than 1000 ms,
84 * then the var is considered 'high rez'.
85 * Otherwise, the period is rounded at the
86 * nearest integer second.
87 * For 'high rez' var, the service buffer
88 * the relative timestamp in ms at each
89 * tick loop and send update every seconds
90 * to the AES service.
91 * In addition, HighRez var are also sent
92 * every second as normal sample.
94 uint32 MeanSamplePeriod;
96 /// Date of last sample (low rez)
97 uint32 LastSampleTimeStamp;
98 /// Date of last sample (high rez)
99 TTime LastHighRezTimeStamp;
101 /// The vector of buffered samples
102 vector<TGraphSample> Samples;
104 TGraphVarInfo()
105 : MeanSamplePeriod(1000),
106 LastSampleTimeStamp(0),
107 LastHighRezTimeStamp(0)
112 /// The list of variable to graph (build from service config file var 'GraphVars')
113 vector<TGraphVarInfo> _GraphVars;
115 /// Date of last graph
117 public:
119 CAdminExecutorServiceClient()
120 : _DontUseShardOrders(false),
121 _LastStateReport(0),
122 _LastStatusStringReport(0),
123 _ProcessUsedMemory(0)
125 CAdminExecutorServiceClientSkel::init(this);
129 std::string makeServiceAlias()
131 string serviceAlias = IService::getInstance()->getServiceAliasName();
132 if (serviceAlias.empty())
134 serviceAlias = IService::getInstance()->getHostName()+"."+IService::getInstance()->getServiceUnifiedName();
136 return serviceAlias;
139 string getModuleManifest() const
141 uint32 pid = getpid ();
143 string serviceAlias = _ServiceAlias;
145 CSString manifest;
147 manifest << "LongName=" << IService::getInstance()->getServiceLongName()
148 << " ShortName=" << IService::getInstance()->getServiceShortName()
149 << " AliasName=" << serviceAlias
150 << " PID=" << pid
151 << " DontUseShardOrders=" << _DontUseShardOrders;
153 return manifest;
156 bool initModule(const TParsedCommandLine &pcl)
158 if (!CModuleBase::initModule(pcl))
159 return false;
161 // try to read the config file
162 IService *service = IService::getInstance();
163 if (service == NULL)
165 nlwarning("Failed to get the IService singleton instance");
166 return false;
169 CConfigFile::CVar *gv = service->ConfigFile.getVarPtr("GraphVars");
170 if (gv)
172 _GraphVars.clear();
173 for (uint i =0; i<gv->size()/2; ++i)
175 TGraphVarInfo gvi;
177 gvi.VarName = gv->asString(i*2);
178 gvi.MeanSamplePeriod = max(1, gv->asInt((i*2)+1));
180 _GraphVars.push_back(gvi);
184 // precompute the service name
185 _ServiceAlias = makeServiceAlias();
187 // loop for an optional 'dontUseShardOrders' flag in init params
188 const TParsedCommandLine *duso = pcl.getParam("dontUseShardOrders");
189 if (duso != NULL)
190 _DontUseShardOrders = (duso->ParamValue == "true" || duso->ParamName == "1");
192 return true;
196 void onModuleUp(IModuleProxy *proxy)
198 if (proxy->getModuleClassName() == "AdminExecutorService")
200 nldebug("CAdminExecutorServiceClient : admin executor service up as '%s'", proxy->getModuleName().c_str());
201 // we found the manager of AES
202 if (_AdminExecutorService != NULL)
204 nlwarning("CAdminExecutorServiceClient : admin executor service already known as '%s', replacing with new one", _AdminExecutorService->getModuleName().c_str());
206 _AdminExecutorService = proxy;
208 // // send basic service info to AES
209 // CAdminExecutorServiceProxy aes(proxy);
211 // uint32 pid = getpid ();
213 // string serviceAlias = IService::getInstance()->getServiceAliasName();
214 // if (serviceAlias.empty())
215 // serviceAlias = getModuleFullyQualifiedName();
217 // aes.serviceConnected(this,
218 // IService::getInstance()->getServiceLongName(),
219 // IService::getInstance()->getServiceShortName(),
220 // serviceAlias,
221 // pid);
222 // for resend of the current status to the new AES
223 _LastSentStatus = "";
224 sendServiceStatus();
228 void onModuleDown(IModuleProxy *proxy)
230 if (proxy == _AdminExecutorService)
232 nldebug("CAdminExecutorServiceClient : admin executor service '%s' is down", proxy->getModuleName().c_str());
234 _AdminExecutorService = NULL;
238 void onModuleUpdate()
240 H_AUTO(CAdminExecutorServiceClient_onModuleUpdate);
242 uint32 now = CTime::getSecondsSince1970();
243 TTime timer = CTime::getLocalTime();
245 // update every HR variables
246 for (uint i=0; i<_GraphVars.size(); ++i)
248 if (_GraphVars[i].MeanSamplePeriod < 1000)
250 // this is a HR var
251 TGraphVarInfo &gvi = _GraphVars[i];
252 if (gvi.LastHighRezTimeStamp + gvi.MeanSamplePeriod < timer)
254 // it's time to get a sample
255 // create a sample
256 gvi.Samples.push_back(TGraphSample());
257 TGraphSample &gs = gvi.Samples.back();
258 // inialise it
259 gs.TimeStamp = now;
260 gs.HighRezTimeStamp = timer;
261 IVariable *var = dynamic_cast<IVariable*>(ICommand::getCommand(gvi.VarName));
262 if (var != NULL)
263 NLMISC::fromString(var->toString(), gs.SampleValue);
268 if (_LastStateReport != now)
271 if ((now & 0xf) == 0)
273 // every 16 seconds because very slow
275 // FIXME: This is too slow
276 IVariable *var = dynamic_cast<IVariable*>(ICommand::getCommand("ProcessUsedMemory"));
277 if (var != NULL)
278 NLMISC::fromString(var->toString(), _ProcessUsedMemory);
280 _ProcessUsedMemory = 0;
283 // at least one second as passed, check for updates to send to
284 // AES
286 TGraphDatas graphDatas;
287 graphDatas.setCurrentTime(now);
289 THighRezDatas highRezDatas;
290 highRezDatas.setServiceAlias(_ServiceAlias);
291 highRezDatas.setCurrentTime(now);
293 vector<TGraphData> &datas = graphDatas.getDatas();
295 for (uint i=0; i<_GraphVars.size(); ++i)
297 if (_GraphVars[i].LastSampleTimeStamp+(_GraphVars[i].MeanSamplePeriod/1000) < now)
299 TGraphVarInfo &gvi = _GraphVars[i];
300 // it's time to send update for this var
301 // create a new sample entry
302 datas.push_back(TGraphData());
303 // and fill it
304 TGraphData &gd = datas.back();
305 gd.setServiceAlias(_ServiceAlias);
306 gd.setVarName(gvi.VarName);
307 gd.setSamplePeriod(max(uint32(1), uint32(gvi.MeanSamplePeriod/1000)));
308 if (gvi.Samples.empty())
310 // no sample collected yet, just ask a new one
311 IVariable *var = dynamic_cast<IVariable*>(ICommand::getCommand(gvi.VarName));
312 if (var != NULL)
314 float val;
315 NLMISC::fromString(var->toString(), val);
316 gd.setValue(val);
319 else
321 // we have some sample collected, just use the last one
322 gd.setValue(gvi.Samples.back().SampleValue);
325 // if it's a high rez sampler, send the complete buffer
326 if (gvi.MeanSamplePeriod < 1000 && _AdminExecutorService != NULL)
328 // build the message
329 highRezDatas.setVarName(gvi.VarName);
330 highRezDatas.getDatas().clear();
332 for (uint j=0; j<gvi.Samples.size(); ++j)
334 highRezDatas.getDatas().push_back(THighRezData());
335 THighRezData &hrd = highRezDatas.getDatas().back();
336 hrd.setSampleTick(gvi.Samples[j].HighRezTimeStamp);
337 hrd.setValue(gvi.Samples[j].SampleValue);
340 if (!highRezDatas.getDatas().empty() && _AdminExecutorService != NULL)
342 // send the high rez data
343 CAdminExecutorServiceProxy aes(_AdminExecutorService);
344 aes.highRezGraphUpdate(this, highRezDatas);
347 // we don't send normal update for high rez sampler
348 datas.pop_back();
351 // update the time stamp
352 gvi.LastSampleTimeStamp = now;
353 // clear the buffer
354 gvi.Samples.clear();
358 // if we have some data to send, send them
359 if (!datas.empty() && _AdminExecutorService != NULL)
361 CAdminExecutorServiceProxy aes(_AdminExecutorService);
362 aes.graphUpdate(this, graphDatas);
365 // update the last report date
366 _LastStateReport = now;
369 // send an update of the status (if needed)
370 sendServiceStatus();
373 void sendServiceStatus()
375 CSString status;
376 uint32 now = NLMISC::CTime::getSecondsSince1970();
378 status << "\tServiceAlias=" << _ServiceAlias;
380 // build the status string
381 IVariable *var = dynamic_cast<IVariable*>(ICommand::getCommand("State"));
382 if (var != NULL)
383 status << "\tState=" <<var->toString();
385 var = dynamic_cast<IVariable*>(ICommand::getCommand("UserSpeedLoop"));
386 if (var != NULL)
387 status << "\tUserSpeedLoop=" <<var->toString();
389 var = dynamic_cast<IVariable*>(ICommand::getCommand("TickSpeedLoop"));
390 if (var != NULL)
391 status << "\tTickSpeedLoop=" <<var->toString();
393 if (_ProcessUsedMemory != 0)
394 status << "\tProcessUsedMemory=" <<_ProcessUsedMemory;
396 var = dynamic_cast<IVariable*>(ICommand::getCommand("Uptime"));
397 if (var != NULL)
398 status << "\tUpTime=" <<var->toString();
400 var = dynamic_cast<IVariable*>(ICommand::getCommand("NbPlayers"));
401 if (var != NULL)
402 status << "\tNbPlayers=" <<var->toString();
404 var = dynamic_cast<IVariable*>(ICommand::getCommand("NbEntities"));
405 if (var != NULL)
406 status << "\tNbEntities=" <<var->toString();
408 var = dynamic_cast<IVariable*>(ICommand::getCommand("LocalEntities"));
409 if (var != NULL)
410 status << "\tLocalEntities=" <<var->toString();
412 uint32 shardId = IService::getInstance()->getShardId();
413 status << "\tShardId=" <<toString(shardId);
415 // add any service specific info
416 if (IService::isServiceInitialized())
418 status << "\t" << IService::getInstance()->getServiceStatusString();
421 if ((status != _LastSentStatus || (now - _LastStatusStringReport) > MAX_DELAY_BETWEEN_REPORT)
422 && _AdminExecutorService != NULL)
424 CAdminExecutorServiceProxy aes(_AdminExecutorService);
425 aes.serviceStatusUpdate(this, status);
427 _LastSentStatus = status;
428 _LastStatusStringReport = now;
432 ///////////////////////////////////////////////////////////////
433 // implementation from Admin executor service client
434 ///////////////////////////////////////////////////////////////
436 // execute a command and return the result.
437 virtual void serviceCmd(NLNET::IModuleProxy *sender, uint32 commandId, const std::string &command)
439 // create a displayer to gather the output of the command
440 class CStringDisplayer: public IDisplayer
442 public:
443 virtual void doDisplay( const CLog::TDisplayInfo& args, const char *message)
445 _Data += message;
448 std::string _Data;
450 CStringDisplayer stringDisplayer;
451 IService::getInstance()->CommandLog.addDisplayer(&stringDisplayer);
453 // retrieve the command from the input message and execute it
454 nlinfo ("ADMIN: Executing command from network : '%s'", command.c_str());
455 ICommand::execute (command, IService::getInstance()->CommandLog);
457 // unhook our displayer as it's work is now done
458 IService::getInstance()->CommandLog.removeDisplayer(&stringDisplayer);
460 string serviceAlias = IService::getInstance()->getServiceAliasName();
461 if (serviceAlias.empty())
462 serviceAlias = getModuleFullyQualifiedName();
464 // return the result to AES
465 CAdminExecutorServiceProxy aes(sender);
466 aes.commandResult(this, commandId, serviceAlias, stringDisplayer._Data);
469 // execute a command without result
470 virtual void serviceCmdNoReturn(NLNET::IModuleProxy *sender, const std::string &command)
472 // retrieve the command from the input message and execute it
473 nlinfo ("ADMIN: Executing command from network : '%s'", command.c_str());
474 ICommand::execute (command, IService::getInstance()->CommandLog);
478 NLNET_REGISTER_MODULE_FACTORY(CAdminExecutorServiceClient, "AdminExecutorServiceClient");
481 } // namespace ADMIN