diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index cb1e7032a6d81d900c7f13c8bd116b66e71035aa..a885038c2694fd1ca56f12e7f51e3c5eff7382f6 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -5,6 +5,8 @@ cmake_minimum_required(VERSION 3.3 FATAL_ERROR) # locations on all platforms. include(GNUInstallDirs) +find_package(fmt REQUIRED) +find_package(spdlog REQUIRED) find_package( DD4hep REQUIRED COMPONENTS DDCore DDG4 ) find_package(ROOT REQUIRED COMPONENTS Geom GenVector MathCore) @@ -75,6 +77,27 @@ target_compile_features(${exe_name} ) target_link_libraries(${exe_name} PUBLIC DD4hep::DDCore DD4hep::DDG4 ROOT::Core ROOT::Hist ROOT::GenVector ROOT::Eve ROOT::Gpad stdc++fs ROOT::Imt) + +set(exe_name dd_web_display) +add_executable(${exe_name} src/${exe_name}.cxx) +#target_link_libraries(${exe_name} +# PUBLIC DD4hep::DDG4 +# PUBLIC DD4hep::DDCore +# ) + +target_include_directories(${exe_name} PUBLIC + PRIVATE include ) +target_compile_features(${exe_name} + PUBLIC cxx_auto_type + PUBLIC cxx_trailing_return_types + PRIVATE cxx_variadic_templates + #PRIVATE cxx_std_17 + ) +target_link_libraries(${exe_name} + PUBLIC DD4hep::DDCore DD4hep::DDG4 ROOT::Core ROOT::Hist ROOT::GenVector ROOT::Eve ROOT::Gpad ROOT::Imt ROOT::RHTTP ROOT::Net stdc++fs fmt::fmt spdlog::spdlog) + install(TARGETS ${exe_name} EXPORT NPDetTargets RUNTIME DESTINATION bin ) + + diff --git a/src/tools/src/dd_web_display.cxx b/src/tools/src/dd_web_display.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f324e5e1876e14172b78257c996ed2a47b7d52d6 --- /dev/null +++ b/src/tools/src/dd_web_display.cxx @@ -0,0 +1,490 @@ +#include "Math/Vector3D.h" +#include "Math/Vector4D.h" +#include "Math/VectorUtil.h" +#include "TCanvas.h" +#include "TLegend.h" +#include "TMath.h" +#include "TRandom3.h" +#include "TFile.h" +#include "TH1F.h" +#include "TH2F.h" +#include "TH1D.h" +#include "TH2D.h" +#include "TTree.h" +#include "TF1.h" +#include "TSystem.h" +#include "TH1F.h" +#include "TH2F.h" +#include "TH3F.h" +#include "TCanvas.h" +#include "THttpServer.h" + +#include <csignal> +#include <stdexcept> +#include <cstring> +#include <vector> +#include <map> +#include <memory> +#include <functional> + +#include "TMessage.h" +#include "TMonitor.h" +#include "TServerSocket.h" +#include "TSocket.h" +#include "TFile.h" + +#include <vector> +#include <tuple> +#include <algorithm> +#include <iterator> +#include <thread> +#include <experimental/filesystem> +namespace fs = std::experimental::filesystem; + +#include "DD4hep/Detector.h" +#include "DD4hep/Printout.h" +#include "DDG4/Geant4Data.h" +#include "DDRec/CellIDPositionConverter.h" +#include "DDRec/SurfaceManager.h" +#include "DDRec/Surface.h" +#include "DD4hep/DD4hepUnits.h" + +#include "TApplication.h" +#include "TMultiGraph.h" +#include "TGraph.h" + +#include "TGeoManager.h" +#include "TGeoNode.h" +#include "TGeoVolume.h" +#include "TEveManager.h" +#include "TEveGeoNode.h" +#include "TEveGeoShapeExtract.h" +#include "TEveGeoShape.h" +#include "TEveBrowser.h" +#include "TSystem.h" +#include "ROOT/TFuture.hxx" + +#include "Math/DisplacementVector3D.h" + +#include <iostream> +#include <string> +#include <chrono> + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/basic_file_sink.h" + +volatile sig_atomic_t sig_caught = 0; + +void handle_sig(int signum) { + /* in case we registered this handler for multiple signals */ + if (signum == SIGINT) { + sig_caught = 1; + } + if (signum == SIGTERM) { + sig_caught = 2; + } + if (signum == SIGABRT) { + sig_caught = 3; + } +} + +#include "clipp.h" +using namespace clipp; +using std::string; +//______________________________________________________________________________ + +enum class mode { none, help, list, part }; + +struct settings { + bool help = false; + bool success = false; + std::string compact_file = ""; + std::string infile = ""; + std::string outfile = "detector_geometry"; + std::string p_name = ""; + int part_level = -1; + bool level_set = false; + int geo_level = -1; + bool list_all = false; + mode selected = mode::list; + int color = 1; + double alpha = 1; + std::map<std::string,int> part_name_levels; + std::map<std::string,int> part_name_colors; + std::map<std::string,double> part_name_alphas; + int http_port = 8090; + string http_host = "127.0.0.1"; + string in_out_file = ""; +}; +//______________________________________________________________________________ + +void run_http_server(const settings& s); +//void run_part_mode(const settings& s); +//______________________________________________________________________________ + +template<typename T> +void print_usage(T cli, const char* argv0 ) +{ + //used default formatting + std::cout << "Usage:\n" << usage_lines(cli, argv0) + << "\nOptions:\n" << documentation(cli) << '\n'; +} +//______________________________________________________________________________ + +template<typename T> +void print_man_page(T cli, const char* argv0 ){ + //all formatting options (with their default values) + auto fmt = clipp::doc_formatting{} + .start_column(8) //column where usage lines and documentation starts + .doc_column(20) //parameter docstring start col + .indent_size(4) //indent of documentation lines for children of a documented group + .line_spacing(0) //number of empty lines after single documentation lines + .paragraph_spacing(1) //number of empty lines before and after paragraphs + .flag_separator(", ") //between flags of the same parameter + .param_separator(" ") //between parameters + .group_separator(" ") //between groups (in usage) + .alternative_param_separator("|") //between alternative flags + .alternative_group_separator(" | ") //between alternative groups + .surround_group("(", ")") //surround groups with these + .surround_alternatives("(", ")") //surround group of alternatives with these + .surround_alternative_flags("", "") //surround alternative flags with these + .surround_joinable("(", ")") //surround group of joinable flags with these + .surround_optional("[", "]") //surround optional parameters with these + .surround_repeat("", "..."); //surround repeatable parameters with these + //.surround_value("<", ">") //surround values with these + //.empty_label("") //used if parameter has no flags and no label + //.max_alternative_flags_in_usage(1) //max. # of flags per parameter in usage + //.max_alternative_flags_in_doc(2) //max. # of flags per parameter in detailed documentation + //.split_alternatives(true) //split usage into several lines for large alternatives + //.alternatives_min_split_size(3) //min. # of parameters for separate usage line + //.merge_alternative_flags_with_common_prefix(false) //-ab(cdxy|xy) instead of -abcdxy|-abxy + //.merge_joinable_flags_with_common_prefix(true); //-abc instead of -a -b -c + + auto mp = make_man_page(cli, argv0, fmt); + mp.prepend_section("DESCRIPTION", "Geometry tool for converting compact files to STEP (cad) files."); + mp.append_section("EXAMPLES", " $ npdet_to_teve list compact.xml"); + std::cout << mp << "\n"; +} +//______________________________________________________________________________ + +settings cmdline_settings(int argc, char* argv[]) +{ + settings s; + auto partMode = "part mode:" % repeatable( + command("part").set(s.selected,mode::part) % "Select only the first level nodes by name", + repeatable( + required("-l","--level").set(s.level_set) & integer("level",s.part_level) % "Maximum level navigated to for part", + option("-c","--color") & integer("color",s.color), + option("-a","--alpha") & number("alpha",s.alpha), + value("name")([&](const std::string& p) + { + s.p_name = p; + if(!s.level_set) { s.part_level = -1; } + s.level_set = false; + std::cout << "s.color " << s.color << "\n"; + std::cout << "s.alpha " << s.alpha << "\n"; + s.part_name_levels[p] = s.part_level; + s.part_name_colors[p] = s.color; + s.part_name_alphas[p] = s.alpha; + }) % "Part/Node name (must be child of top node)" + ) + ); + + auto lastOpt = " options:" % ( + option("-h", "--help").set(s.selected, mode::help) % "show help", + option("-g","--global_level") & integer("level",s.geo_level), + option("-o","--output") & value("out",s.outfile), + value("file",s.infile).if_missing([]{ + std::cout << "You need to provide an input xml filename as the last argument!\n"; + } ) % "input xml file" + ); + + + auto server_cli = + ((option("-p", "--port") & value("http_port", s.http_port)) % + "port to which the http serve attaches. Default: 8090 ", + (option("-H", "--host") & value("http_host", s.http_host)) % + "Http server host name or IP address. Default: 127.0.0.1", + (option("-f", "--file") & value("io_file", s.in_out_file)) % + "File used to initialize and save plots. Default: top_folder.root"); + + std::string wrong; + auto cli = ( + command("help").set(s.selected, mode::help) | (server_cli, lastOpt), + any_other(wrong) + ); + + assert( cli.flags_are_prefix_free() ); + + auto res = parse(argc, argv, cli); + + //if( res.any_error() ) { + // s.success = false; + // std::cout << make_man_page(cli, argv[0]).prepend_section("error: ", + // " The best thing since sliced bread."); + // return s; + //} + s.success = true; + + if(s.selected == mode::help) { + print_man_page<decltype(cli)>(cli,argv[0]); + }; + return s; +} +//______________________________________________________________________________ + + +int main (int argc, char *argv[]) { + + settings s = cmdline_settings(argc,argv); + if( !s.success ) { + return 1; + } + + if(s.selected == mode::help) { + return 0; + } + + // ------------------------ + // CLI Checks + if( !fs::exists(fs::path(s.infile)) ) { + std::cerr << "file, " << s.infile << ", does not exist\n"; + return 1; + } + auto has_suffix = [&](const std::string &str, const std::string &suffix) { + return str.size() >= suffix.size() && + str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; + }; + if( !has_suffix(s.outfile,".root") ) { + s.outfile += ".root"; + } + //for(const auto& [part_name, part_level] : s.part_name_levels ) { + // std::cout << " SOME Part : " << part_name << ", level = " << part_level <<"\n"; + //} + + // --------------------------------------------- + // Run modes + // + //switch(s.selected) { + // case mode::list: + // run_list_mode(s); + // break; + // case mode::part: + // run_part_mode(s); + // break; + // default: + // break; + //} + run_http_server(s); + + return 0; +} +//______________________________________________________________________________ + + +void run_http_server(const settings& s) +{ + dd4hep::setPrintLevel(dd4hep::WARNING); + gErrorIgnoreLevel = kWarning;// kPrint, kInfo, kWarning, + + void (*prev_handler)(int); + prev_handler = signal(SIGINT, handle_sig); + + // ------------------------- + // Get the DD4hep instance + dd4hep::Detector& detector = dd4hep::Detector::getInstance(); + detector.fromCompact(s.infile); + + + auto serv = new THttpServer((std::string("http:") + s.http_host + ":" + + std::to_string(s.http_port) + + std::string("?top=geometry&thrds=1;rw;noglobal")) + .c_str()); + + spdlog::info("Creating display server at http://{}:{}",s.http_host,s.http_port); + if( !(serv->IsAnyEngine()) ) { + spdlog::error("Failed to start http server."); + std::exit(-1); + } + //_server->SetDefaultPage("online.htm"); + //_server->SetDefaultPage("draw.htm"); + serv->SetCors(); + + serv->Register("/",detector.manager().GetTopNode()); + + // Loop until an interrupt (ctrl-c) is issued + while (1) { + serv->ProcessRequests(); + if (sig_caught) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + } + + signal(SIGINT, prev_handler); + + //std::cout << gGeoManager->GetPath() << "\n"; + + //for(const auto& [p,l] : s.part_name_levels) { + // bool dir = gGeoManager->cd(p.c_str()); + // if (!dir) { + // std::cerr << p << " not found!\n"; + // continue; + // } + // TGeoNode *node = gGeoManager->GetCurrentNode(); + // int level = gGeoManager->GetLevel(); + // if( level > l ){ + // std::cout << "\n" << p << " found at level " << level << " but above selected level of " << l << ")\n"; + // continue; + // } + // std::cout << "\n"; + // std::cout << "Subnodes for \"" << p << "\" (level = " << level << ")\n"; + // if(node->GetNdaughters() == 0) { + // continue; + // } + // TObjArrayIter node_array_iter(node->GetVolume()->GetNodes()); + // TGeoNode* a_node = nullptr; + // while( (a_node = dynamic_cast<TGeoNode*>(node_array_iter.Next())) ) { + // std::cout << p << "/" << a_node->GetName() << "\n"; + // } + + // //TGeoNode* currentNode = nullptr; + // //auto res = new TEveGeoNode(node); + // //TGeoIterator nextNode( node->GetVolume() ); + // //nextNode.SetType(1); + // //while( (currentNode = nextNode()) ) { + // // auto nlevel = nextNode.GetLevel(); + // // if( nlevel > l-level ) { + // // break; + // // } + // // //auto daughter = new TEveGeoNode( currentNode ); + // // //res->AddElement(daughter); + // // currentNode->ls(); + // //} + //} +} +//______________________________________________________________________________ + +//void run_part_mode(const settings& s) +//{ +// int root_argc = 0; +// char *root_argv[1] = {"npdet_to_teve"}; +// //argv[0] = "npdet_fields"; +// +// gErrorIgnoreLevel = kWarning;// kPrint, kInfo, kWarning, +// TApplication app("tapp", &root_argc, root_argv); +// +// // Get the DD4hep instance +// dd4hep::setPrintLevel(dd4hep::WARNING); +// dd4hep::Detector& detector = dd4hep::Detector::getInstance(); +// detector.fromCompact(s.infile); +// +// TEveManager::Create();//true,""); +// +// TEveGeoTopNode* eve_top_n = new TEveGeoTopNode(&(detector.manager()),detector.manager().GetTopNode()); +// detector.manager().GetTopNode()->SetVisibility(kFALSE); +// eve_top_n->SetVisLevel(0); +// std::cout << "Path is " << gGeoManager->GetPath() << "\n"; +// +// for(const auto& [p,l] : s.part_name_levels) { +// bool dir = gGeoManager->cd(p.c_str()); +// if (!dir) { +// std::cerr << p << " not found!\n"; +// return; +// } +// TGeoNode *node = gGeoManager->GetCurrentNode(); +// if(!node){ +// std::cerr << " bad node\n"; +// return; +// } +// int ilevel = gGeoManager->GetLevel(); +// if( ilevel > l ){ +// std::cout << p << " found at level " << ilevel << " but above selected level of " << l << "\n"; +// //return; +// } +// +// int istate = gGeoManager->GetCurrentNodeId(); +// std::string ipath = gGeoManager->GetPath(); +// +// auto pcolor = s.part_name_colors.find(p)->second; +// auto ptransparency = 100.0*(1.0 - s.part_name_alphas.find(p)->second); +// +// std::cout << " color " << pcolor << "\n"; +// std::cout << " ptransparency " << ptransparency << "\n"; +// +// node->GetVolume()->SetLineColor(pcolor); +// node->GetVolume()->SetFillColor(pcolor); +// node->GetVolume()->SetTransparency(ptransparency); +// node->GetVolume()->SetTransparency(ptransparency); +// +// +// TEveGeoNode* res = new TEveGeoNode(node); +// std::map<int,TEveGeoNode*> eve_nodes; +// eve_nodes[0] = res; +// +// TGeoIterator nextNode( node->GetVolume() ); +// int path_index = 0; +// TGeoNode* currentNode = nullptr; +// +// while( (currentNode = nextNode()) ) { +// nextNode.SetType(0); +// // Not iterator level starts at 1 +// auto nlevel = nextNode.GetLevel(); +// if( nlevel > l ) { +// continue; +// } +// //if( path_index == nlevel) { +// // if(pcolor != s.part_name_colors.end() ){ +// currentNode->GetVolume()->SetLineColor(pcolor); +// currentNode->GetVolume()->SetFillColor(pcolor); +// currentNode->GetVolume()->SetTransparency(ptransparency); +// currentNode->GetVolume()->SetTransparency(ptransparency); +// // } +// TEveGeoNode* daughter = new TEveGeoNode( currentNode ); +// eve_nodes[nlevel] = daughter; +// eve_nodes[nlevel-1]->AddElement(daughter); +// //} +// //std::cout << nlevel << "\n"; +// //std::cout << gGeoManager->PushPath() << "\n"; +// +// //res->AddElement(daughter); +// currentNode->ls(); +// } +// eve_top_n->AddElement(res); +// } +// +// eve_top_n->SaveExtract(s.outfile.c_str(), "extract", kTRUE); +// +// //-------------------------------------------- +// // Now Quit ... it is harder than it should be. +// app.Run(kFALSE); +// TEveManager::Terminate(); +// app.Terminate(0); +// +// //std::cout << "derp1 \n"; +// //ROOT::EnableImplicitMT(2); +// ////TEveManager::Terminate(); +// //auto wi0 = ROOT::Experimental::Async([&](){app.Run(kFALSE); return std::string("done1");}); +// //auto wi1 = ROOT::Experimental::Async([&]() { +// // //using namespace std::chrono_literals; +// // std::cout << "Hello waiter" << std::endl; +// // gEve->GetBrowser()->CloseWindow(); +// // gSystem->ProcessEvents() ; +// // std::chrono::seconds sec(2); +// // std::this_thread::sleep_for(sec); +// // std::cout << "Hello waiter" << std::endl; +// // app.Terminate(0); +// // std::cout << " " << wi0.get() << std::endl; +// // return std::string("done 2"); +// //}); +// ////app.Run(kTRUE); +// //std::cout << " " << wi1.get() << std::endl; +// ////app.Terminate(0); +// //// Pass kFALSE if you want application to terminate by itself. +// //// Then you just need "return 0;" below (to avoid compiler warnings). +// //// Optionally shutdown eve here (not really needed): +// //// TEveManager::Terminate(); +// //std::cout << "derp \n"; +//} +