#include "ROOT/RWebWindow.hxx" #include "TBufferJSON.h" #include #include std::shared_ptr window; int num_clients = 1; bool window_terminated = false; bool call_show = true; bool batch_mode = false; int current_counter = 0; auto start_tm = std::chrono::high_resolution_clock::now(); auto firstmsg_tm = start_tm; auto stop_tm = start_tm; std::string round_trip = ""; void ProcessData(unsigned connid, const std::string &arg) { if (arg.find("PING:") == 0) { window->Send(connid, arg); } else if (arg == "first") { // first message to provide config firstmsg_tm = std::chrono::high_resolution_clock::now(); std::vector clients; // for new clients request new connection URL // use relative path ".." while connection will be requested from the window itself for (int n = 1; n < num_clients; ++n) clients.emplace_back(".." + window->GetUrl(false)); window->Send(connid, std::string("CLIENTS:") + TBufferJSON::ToJSON(&clients).Data()); } else if (arg.find("SHOW:") == 0) { std::string msg = arg.substr(5); if (!batch_mode) std::cout << msg << std::endl; if (msg.find("Cnt:") == 0) { int counter = std::stoi(msg.substr(4)); if (counter > 0) current_counter = counter; } auto p = msg.find("round-trip:"); if (p > 0) round_trip = msg.substr(p); } else if (arg == "halt") { // terminate ROOT window_terminated = true; window->TerminateROOT(); } } //////////////////////////////////////////////////////// /// @param nclients - number of clients /// @param test_mode /// 0 - default config, no special threads /// 1 - reduce http server timer /// 2 - create special thread in THttpServer and use it /// 3 - also create special thread for RWebWindow /// 4 - directly use civetweb threads (only for experts) /// 10 - force longpoll socket with default config enum TestModes { modeDefault = 0, // default configuration modeMinimalTimer = 1, // reduce THttpServer timer modeHttpThread = 2, // create and use THttpServer thread to handle window functionality modeHttpWindowThread = 3, // with THttpServer thread also create thread for the window modeCivetThread = 4 // directly use threads if civetweb, dangerous }; enum MajorModes { majorDefault = 0, // default test suite, using websockets majorLongpoll = 1 // force longpoll sockets }; void ping(int nclients = 1, int test_mode = 0) { num_clients = nclients; batch_mode = gROOT->IsBatch(); // verify values if (test_mode < 0) test_mode = 0; int major_mode = test_mode / 10; test_mode = test_mode % 10; if (test_mode > modeCivetThread) test_mode = modeDefault; if (num_clients < 1) num_clients = 1; else if (num_clients > 1000) num_clients = 1000; // let force usage of longpoll engine instead of plain websocket if (major_mode == majorLongpoll) gEnv->SetValue("WebGui.WSLongpoll", "yes"); if (num_clients > 5) gEnv->SetValue("WebGui.HttpThreads", num_clients + 5); // allocate special thread for THttpServer, it will be automatically used by web window if ((test_mode == modeHttpThread) || (test_mode == modeHttpWindowThread)) gEnv->SetValue("WebGui.HttpThrd", "yes"); // let reduce reaction time of THttpServer if (test_mode == modeMinimalTimer) gEnv->SetValue("WebGui.HttpTimer", 1); // let allocate special thread which will be used to perform data sending via websocket // should reduce consumption of webwindow thread when big data are send // gEnv->SetValue("WebGui.SenderThrds", "yes"); // create window window = ROOT::RWebWindow::Create(); // configure number of clients are allowed to connect ROOT::RWebWindowsManager::SetSingleConnMode(false); window->SetConnLimit(num_clients); // configure default html page // either HTML code can be specified or just name of file after 'file:' prefix // Detect file location to specify full path to the HTML file std::string fname = __FILE__; auto pos = fname.find("ping.cxx"); if (pos > 0) fname.resize(pos); else fname = gROOT->GetTutorialsDir() + std::string("/visualisation/webgui/ping/"); fname.append("ping.html"); window->SetDefaultPage("file:" + fname); // configure window geometry window->SetGeometry(300, 500); // this set call-back, invoked when message received from client // also at this moment thread id is configured which supposed to be used to handle requests window->SetDataCallBack(ProcessData); // allow to use server thread, which responsible for requests processing if (test_mode == modeCivetThread) window->UseServerThreads(); if (test_mode == modeHttpWindowThread) window->StartThread(); // instead of showing window one can create URL and type it in any browser window if (call_show) window->Show(batch_mode ? "headless" : ""); else std::cout << "Window url is: " << window->GetUrl(true) << std::endl; // provide blocking method to let run if (batch_mode) { const int run_limit = 200; const double fullrun_time = 100., startup_time = 70.; start_tm = firstmsg_tm = std::chrono::high_resolution_clock::now(); window->WaitFor([=](double tm) { return (current_counter >= run_limit) || (tm > fullrun_time) || ((current_counter == 0) && (tm > startup_time)) ? 1 : 0; }); stop_tm = std::chrono::high_resolution_clock::now(); auto startuptime_int = std::chrono::duration_cast(firstmsg_tm - start_tm); auto runttime_int = std::chrono::duration_cast(stop_tm - firstmsg_tm); if (current_counter >= run_limit) std::cout << "PING-PONG TEST COMPLETED " << round_trip; else std::cout << "PING-PONG TEST FAIL cnt:" << current_counter; std::cout << " startup: " << startuptime_int.count() << "ms" << " run: " << runttime_int.count() << "ms" << std::endl; } }