All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
WebEVDServer.cxx
Go to the documentation of this file.
1 // Chris Backhouse - bckhouse@fnal.gov
2 
4 
6 
8 
10 
12 
13 #include <string>
14 
15 #include "fhiclcpp/ParameterSet.h"
16 #include "art/Framework/Principal/Handle.h"
17 
18 #include "art/Framework/Principal/Event.h"
19 #include "gallery/Event.h"
20 
27 
29 
30 #include "nusimdata/SimulationBase/MCParticle.h"
31 #include "nusimdata/SimulationBase/MCTruth.h"
32 
34 #include "lardataobj/RawData/raw.h" // Uncompress()
35 
38 
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 //#include <netinet/in.h>
42 #include <netinet/ip.h> /* superset of previous */
43 
44 #include <sys/types.h>
45 #include <fcntl.h>
46 #include <sys/mman.h>
47 #include <sys/stat.h>
48 
49 #include "signal.h"
50 
51 #include "zlib.h"
52 
53 #include <thread>
54 
55 namespace std{
56  bool operator<(const art::InputTag& a, const art::InputTag& b)
57  {
58  return (std::make_tuple(a.label(), a.instance(), a.process()) <
59  std::make_tuple(b.label(), b.instance(), b.process()));
60  }
61 }
62 
63 namespace evd
64 {
65 
66 // ----------------------------------------------------------------------------
67 template<class T> WebEVDServer<T>::WebEVDServer()
68  : fSock(0)
69 {
70 }
71 
72 // ----------------------------------------------------------------------------
73 template<class T> WebEVDServer<T>::~WebEVDServer()
74 {
75  if(fSock) close(fSock);
76 }
77 
78 short swap_byte_order(short x)
79 {
80  char* cx = (char*)&x;
81  std::swap(cx[0], cx[1]);
82  return x;
83 }
84 
85 void write_ok200(int sock,
86  const std::string content = "text/html",
87  bool gzip = false)
88 {
89  std::string str =
90  "HTTP/1.0 200 OK\r\n"
91  "Server: WebEVD/1.0.0\r\n"
92  "Content-Type: "+content+"\r\n";
93 
94  if(gzip) str += "Content-Encoding: gzip\r\n";
95 
96  str += "\r\n";
97 
98  write(sock, &str.front(), str.size());
99 }
100 
101 void write_notfound404(int sock)
102 {
103  const char str[] =
104  "HTTP/1.0 404 Not Found\r\n"
105  "Server: WebEVD/1.0.0\r\n"
106  "Content-Type: text/plain\r\n"
107  "\r\n"
108  "404. Huh?\r\n";
109 
110  write(sock, str, strlen(str));
111 }
112 
113 void write_unimp501(int sock)
114 {
115  const char str[] =
116  "HTTP/1.0 501 Not Implemented\r\n"
117  "Server: WebEVD/1.0.0\r\n"
118  "Content-Type: text/plain\r\n"
119  "\r\n"
120  "I don't know how to do that\r\n";
121 
122  write(sock, str, strlen(str));
123 }
124 
125 std::string read_all(int sock)
126 {
127  std::string ret;
128 
129  std::vector<char> buf(1024*1024);
130  while(true){
131  const int nread = read(sock, &buf.front(), buf.size());
132  if(nread == 0) return ret;
133  ret.insert(ret.end(), buf.begin(), buf.begin()+nread);
134  // Only handle GETs, so no need to wait for payload (for which we'd need to
135  // check Content-Length too).
136  if(ret.find("\r\n\r\n") != std::string::npos) return ret;
137  }
138 }
139 
140 EResult err(const char* call)
141 {
142  std::cout << call << "() error " << errno << " = " << strerror(errno) << std::endl;
143  return kERROR;
144  // return errno;
145 }
146 
147 Result HandleCommand(std::string cmd, int sock)
148 {
149  EResult code = kERROR;
150  int run = -1, subrun = -1, evt = -1;
151  bool traces = false;
152 
153  if(cmd == "/QUIT") code = kQUIT;
154  if(cmd == "/NEXT") code = kNEXT;
155  if(cmd == "/PREV") code = kPREV;
156  if(cmd == "/NEXT_TRACES"){ code = kNEXT; traces = true;}
157  if(cmd == "/PREV_TRACES"){ code = kPREV; traces = true;}
158 
159  if(cmd.find("/seek/") == 0 ||
160  cmd.find("/seek_traces/") == 0){
161  if(cmd.find("/seek_traces/") == 0) traces = true;
162 
163  code = kSEEK;
164  char* ctx;
165  strtok_r(cmd.data(), "/", &ctx); // consumes the "seek" text
166  run = atoi(strtok_r(0, "/", &ctx));
167  subrun = atoi(strtok_r(0, "/", &ctx));
168  evt = atoi(strtok_r(0, "/", &ctx));
169  // if this goes wrong we get zeros, which seems a reasonable fallback
170  }
171 
172  write_ok200(sock, "text/html", false);
173 
174  const int delay = (code == kQUIT) ? 2000 : 0;
175  const std::string txt = (code == kQUIT) ? "Goodbye!" : "Please wait...";
176  const std::string next = traces ? "/traces.html" : "/";
177 
178  // The script tag to set the style is a pretty egregious layering violation,
179  // but doing more seems overkill for a simple interstitial page.
180  const std::string msg = TString::Format("<!DOCTYPE html><html><head><meta charset=\"utf-8\"><script>setTimeout(function(){window.location.replace('%s');}, %d);</script></head><body><script>if(window.sessionStorage.theme != 'lighttheme'){document.body.style.backgroundColor='black';document.body.style.color='white';}</script><h1>%s</h1></body></html>", next.c_str(), delay, txt.c_str()).Data();
181 
182  write(sock, msg.c_str(), msg.size());
183  close(sock);
184 
185  if(code == kSEEK){
186  return Result(kSEEK, run, subrun, evt);
187  }
188  else{
189  return code;
190  }
191 }
192 
193 // ----------------------------------------------------------------------------
194 std::string FindWebDir()
195 {
196  std::string webdir;
197 
198  // For development purposes we prefer to serve the files from the source
199  // directory, which allows them to be live-edited with just a refresh of the
200  // browser window to see them.
201  if(getenv("MRB_SOURCE")) cet::search_path("MRB_SOURCE").find_file("webevd/webevd/WebEVD/web/", webdir);
202  // Otherwise, serve the files from where they get installed
203  if(webdir.empty() && getenv("PRODUCTS") && getenv("WEBEVD_VERSION")) cet::search_path("PRODUCTS").find_file("webevd/"+std::string(getenv("WEBEVD_VERSION"))+"/webevd/", webdir);
204 
205  if(webdir.empty()){
206  std::cout << "Unable to find webevd files under $MRB_SOURCE or $PRODUCTS" << std::endl;
207  abort();
208  }
209 
210  return webdir;
211 }
212 
213 class ILazy
214 {
215 public:
216  virtual void Serialize(JSONFormatter& json) = 0;
217  virtual PNGArena& GetArena() = 0;
218 };
219 
220 // ----------------------------------------------------------------------------
221 void _HandleGetPNG(std::string doc, int sock, ILazy* digs, ILazy* wires)
222 {
223  const std::string mime = "image/png";
224 
225  // Parse the filename
226  char* ctx;
227 
228  const char* pName = strtok_r(&doc.front(), "_", &ctx);
229  const char* pIdx = strtok_r(0, "_", &ctx);
230  const char* pDim = strtok_r(0, ".", &ctx);
231 
232  if(!pName || !pIdx || !pDim){
233  write_notfound404(sock);
234  close(sock);
235  return;
236  }
237 
238  const std::string name(pName);
239  const int idx = atoi(pIdx);
240  const int dim = atoi(pDim);
241 
242  PNGArena* arena = 0;
243  if(name == "/dig") arena = &digs->GetArena();
244  if(name == "/wire") arena = &wires->GetArena();
245 
246  if(!arena || idx >= int(arena->data.size()) || dim > PNGArena::kArenaSize){
247  write_notfound404(sock);
248  close(sock);
249  return;
250  }
251 
252  write_ok200(sock, mime, false);
253  FILE* f = fdopen(sock, "wb");
254  arena->WritePNGBytes(f, idx, dim);
255  fclose(f);
256 }
257 
258 // ----------------------------------------------------------------------------
259 void gzip_buffer(unsigned char* src,
260  int length,
261  std::vector<unsigned char>& dest,
262  int level)
263 {
264  // C++20 will allow to use designated initializers here
265  z_stream strm;
266  strm.zalloc = Z_NULL;
267  strm.zfree = Z_NULL;
268  strm.opaque = Z_NULL;
269 
270  strm.next_in = src;
271  strm.avail_in = length;
272 
273  // The 16 here is the secret sauce to get gzip header and trailer for some
274  // reason...
275  deflateInit2(&strm, level, Z_DEFLATED, 15 | 16, 9, Z_DEFAULT_STRATEGY);
276 
277  // If we allocate a big enough buffer we can deflate in one pass
278  dest.resize(deflateBound(&strm, length));
279 
280  strm.next_out = dest.data();
281  strm.avail_out = dest.size();
282 
283  deflate(&strm, Z_FINISH);
284 
285  dest.resize(dest.size() - strm.avail_out);
286 
287  deflateEnd(&strm);
288 }
289 
290 // ----------------------------------------------------------------------------
291 void write_compressed_buffer(unsigned char* src,
292  int length,
293  int sock,
294  int level,
295  const std::string& name)
296 {
297  std::vector<unsigned char> dest;
298  gzip_buffer(src, length, dest, level);
299 
300  std::cout << "Writing " << length << " bytes (compressed to " << dest.size() << ") for " << name << "\n" << std::endl;
301 
302  write(sock, dest.data(), dest.size());
303 }
304 
305 // ----------------------------------------------------------------------------
306 void write_compressed_file(const std::string& loc, int fd_out, int level)
307 {
308  int fd_in = open(loc.c_str(), O_RDONLY);
309 
310  // Map in the whole file
311  struct stat st;
312  fstat(fd_in, &st);
313  unsigned char* src = (unsigned char*)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd_in, 0);
314 
315  write_compressed_buffer(src, st.st_size, fd_out, level, loc);
316 
317  munmap(src, st.st_size);
318 
319  close(fd_in);
320 }
321 
322 // ----------------------------------------------------------------------------
323 bool endswith(const std::string& s, const std::string& suffix)
324 {
325  return s.rfind(suffix)+suffix.size() == s.size();
326 }
327 
328 // ----------------------------------------------------------------------------
329 JSONFormatter& operator<<(JSONFormatter& json, const art::InputTag& t)
330 {
331  json << "\"" << t.label();
332  if(!t.instance().empty()) json << ":" << t.instance();
333  if(!t.process().empty()) json << ":" << t.process();
334  json << "\"";
335  return json;
336 }
337 
338 // ----------------------------------------------------------------------------
340 {
341  json << "\"" << std::string(id) << "\"";
342  return json;
343 }
344 
345 
346 // ----------------------------------------------------------------------------
348 {
349  return json << "\"" << std::string(plane) << "\"";
350 }
351 
352 // ----------------------------------------------------------------------------
354 {
355  return json << "{\"wire\": " << geo::WireID(hit.WireID()).Wire
356  << ", \"tick\": " << hit.PeakTime()
357  << ", \"rms\": " << hit.RMS()
358  << ", \"peakamp\": " << hit.PeakAmplitude() << "}";
359 }
360 
361 // ----------------------------------------------------------------------------
363 {
364  return json << TVector3(vtx.position().x(),
365  vtx.position().y(),
366  vtx.position().z());
367 }
368 
369 // ----------------------------------------------------------------------------
370 JSONFormatter& operator<<(JSONFormatter& json, const simb::MCTruth& mct)
371 {
372  // Don't show MCTruth for cosmic rays, which can be extremely
373  // lengthy. Ideally we should exclude them from the list entirely, but this
374  // requires less change to the structure of the code.
375  if(mct.Origin() == simb::kCosmicRay) return json << "\"\"";
376 
377  return json << "\"" << MCTruthShortText(mct) << "\"";
378 }
379 
380 // ----------------------------------------------------------------------------
382 {
383  return json << TVector3(pt.X(), pt.Y(), pt.Z());
384 }
385 
386 // ----------------------------------------------------------------------------
388 {
389  return json << "{\"pdg\": " << edep.PdgCode() << ", "
390  << "\"start\": " << edep.Start() << ", "
391  << "\"end\": " << edep.End() << ", "
392  << "\"edep\": " << edep.Energy()
393  << "}";
394 }
395 
396 // ----------------------------------------------------------------------------
398 {
399  return json << TVector3(sp.XYZ());
400 }
401 
402 // ----------------------------------------------------------------------------
404 {
405  std::vector<TVector3> pts;
406 
407  const recob::TrackTrajectory& traj = track.Trajectory();
408  for(unsigned int j = traj.FirstValidPoint(); j <= traj.LastValidPoint(); ++j){
409  if(!traj.HasValidPoint(j)) continue;
410  const geo::Point_t pt = traj.LocationAtPoint(j);
411  pts.emplace_back(pt.X(), pt.Y(), pt.Z());
412  }
413 
414  return json << "{ \"positions\": " << pts << " }";
415 }
416 
417 // ----------------------------------------------------------------------------
418 JSONFormatter& operator<<(JSONFormatter& json, const simb::MCParticle& part)
419 {
420  const int apdg = abs(part.PdgCode());
421  if(apdg == 12 || apdg == 14 || apdg == 16) return json << "{ \"pdg\": " << apdg << ", \"positions\": [] }"; // decay neutrinos
422  std::vector<TVector3> pts;
423  for(unsigned int j = 0; j < part.NumberTrajectoryPoints(); ++j){
424  pts.emplace_back(part.Vx(j), part.Vy(j), part.Vz(j));
425  }
426 
427  return json << "{ \"pdg\": " << apdg << ", \"positions\": " << pts << " }";
428 }
429 
430 // ----------------------------------------------------------------------------
432 {
433  json << std::map<std::string, double>{
434  {"tcenter", flash.Time()},
435  {"twidth", flash.TimeWidth()},
436  {"ycenter", flash.YCenter()},
437  {"ywidth", flash.YWidth()},
438  {"zcenter", flash.ZCenter()},
439  {"zwidth", flash.ZWidth()},
440  {"totpe", flash.TotalPE()}
441  };
442 
443  return json;
444 }
445 
446 // ----------------------------------------------------------------------------
448 {
449  const TVector3 r0(cryo.MinX(), cryo.MinY(), cryo.MinZ());
450  const TVector3 r1(cryo.MaxX(), cryo.MaxY(), cryo.MaxZ());
451  return json << "{ \"min\": " << r0 << ", \"max\": " << r1 << " }";
452 }
453 
454 // ----------------------------------------------------------------------------
456 {
457  return json << "{ \"name\": " << opdet.ID() << ", "
458  << "\"center\": " << TVector3(opdet.GetCenter().X(),
459  opdet.GetCenter().Y(),
460  opdet.GetCenter().Z()) << ", "
461  << "\"length\": " << opdet.Length() << ", "
462  << "\"width\": " << opdet.Width() << ", "
463  << "\"height\": " << opdet.Height() << " }";
464 }
465 
466 // ----------------------------------------------------------------------------
468 {
469  bool first = true;
470  os << "{\"blocks\": [\n";
471  for(unsigned int ix = 0; ix < v.blocks.size(); ++ix){
472  for(unsigned int iy = 0; iy < v.blocks[ix].size(); ++iy){
473  const png_byte* b = v.blocks[ix][iy];
474  if(!b) continue;
475 
476  if(!first) os << ",\n";
477  first = false;
478 
479  int dataidx = 0;
480  for(unsigned int d = 0; d < v.arena.data.size(); ++d){
481  if(b >= &v.arena.data[d]->front() &&
482  b < &v.arena.data[d]->front() + 4*PNGArena::kArenaSize*PNGArena::kArenaSize){
483  dataidx = d;
484  break;
485  }
486  }
487 
488  const int texdx = ((b-&v.arena.data[dataidx]->front())/4)%PNGArena::kArenaSize;
489  const int texdy = ((b-&v.arena.data[dataidx]->front())/4)/PNGArena::kArenaSize;
490 
491  os << "{"
492  << "\"x\": " << ix*PNGArena::kBlockSize << ", "
493  << "\"y\": " << iy*PNGArena::kBlockSize << ", "
494  << "\"dx\": " << PNGArena::kBlockSize << ", "
495  << "\"dy\": " << PNGArena::kBlockSize << ", "
496  << "\"fname\": \"" << v.arena.name << "_" << dataidx << "\", "
497  << "\"texdim\": " << PNGArena::kArenaSize << ", "
498  << "\"u\": " << texdx << ", "
499  << "\"v\": " << texdy << ", "
500  << "\"du\": " << PNGArena::kBlockSize << ", "
501  << "\"dv\": " << PNGArena::kBlockSize
502  << "}";
503  }
504  }
505  os << "\n]}";
506  return os;
507 }
508 
509 // ----------------------------------------------------------------------------
510 template<class TProd, class TEvt> void
512 {
513  json << "{";
514 
515  const std::vector<art::InputTag> tags = evt.template getInputTags<std::vector<TProd>>();
516 
517  for(const art::InputTag& tag: tags){
518  typename TEvt::template HandleT<std::vector<TProd>> prods; // deduce handle type
519  // This can fail in the case of dropped products
520  if(!evt.getByLabel(tag, prods)) continue;
521 
522  json << " " << tag << ": ";
523 
524  json << *prods;
525 
526  if(tag != tags.back()){
527  json << ",";
528  }
529  json << "\n";
530  }
531 
532  json << "}";
533 }
534 
535 // ----------------------------------------------------------------------------
536 template<class TProd, class TEvt> void
538  const std::string& in_label,
540 {
541  typename TEvt::template HandleT<std::vector<TProd>> prods; // deduce handle type
542  evt.getByLabel(in_label, prods);
543 
544  if(prods.isValid()){
545  json << *prods;
546  }
547  else{
548  json << "[]";
549  }
550 }
551 
552 // ----------------------------------------------------------------------------
553 template<class T> void SerializeEventID(const T& evt, JSONFormatter& json)
554 {
555  typedef std::map<std::string, int> EIdMap;
556  json << EIdMap({{"run", evt.run()}, {"subrun", evt.subRun()}, {"evt", evt.event()}});
557 }
558 
559 // ----------------------------------------------------------------------------
561 {
563 }
564 
565 // ----------------------------------------------------------------------------
567  const detinfo::DetectorPropertiesData& detprop,
569 {
570  bool first = true;
571 
572  json << " \"planes\": {\n";
573  for(geo::PlaneID plane: geom->IteratePlaneIDs()){
574  const geo::PlaneGeo& planegeo = geom->Plane(plane);
575  const int view = planegeo.View();
576  const unsigned int nwires = planegeo.Nwires();
577  const double pitch = planegeo.WirePitch();
578  const TVector3 c = planegeo.GetCenter();
579 
580  const TVector3 d = planegeo.GetIncreasingWireDirection();
581  const TVector3 n = planegeo.GetNormalDirection();
582  const TVector3 wiredir = planegeo.GetWireDirection();
583 
584  const double depth = planegeo.Depth();
585  const double width = planegeo.Width();
586  const TVector3 depthdir = planegeo.DepthDir();
587  const TVector3 widthdir = planegeo.WidthDir();
588 
589  const double tick_origin = detprop.ConvertTicksToX(0, plane);
590  const double tick_pitch = detprop.ConvertTicksToX(1, plane) - tick_origin;
591 
592  const int maxTick = detprop.NumberTimeSamples();
593 
594  if(!first) json << ",\n";
595  first = false;
596 
597  json << " " << plane << ": {"
598  << "\"view\": " << view << ", "
599  << "\"nwires\": " << nwires << ", "
600  << "\"pitch\": " << pitch << ", "
601  << "\"nticks\": " << maxTick << ", "
602  << "\"tick_origin\": " << tick_origin << ", "
603  << "\"tick_pitch\": " << tick_pitch << ", "
604  << "\"center\": " << c << ", "
605  << "\"across\": " << d << ", "
606  << "\"wiredir\": " << wiredir << ", "
607  << "\"depth\": " << depth << ", "
608  << "\"width\": " << width << ", "
609  << "\"depthdir\": " << depthdir << ", "
610  << "\"widthdir\": " << widthdir << ", "
611  << "\"normal\": " << n << "}";
612  }
613  json << "\n }";
614 }
615 
616 // ----------------------------------------------------------------------------
618  const detinfo::DetectorPropertiesData& detprop,
620 {
621  json << "{\n";
622  SerializePlanes(geom, detprop, json);
623  json << ",\n\n";
624 
625  json << " \"cryos\": [\n";
626  for(unsigned int i = 0; i < geom->Ncryostats(); ++i){
627  json << " " << geom->Cryostat(i);
628  if(i != geom->Ncryostats()-1) json << ",\n"; else json << "\n";
629  }
630  json << " ],\n\n";
631 
632  json << " \"opdets\": [\n";
633  for(unsigned int i = 0; i < geom->NOpDets(); ++i){
634  json << " " << geom->OpDetGeoFromOpDet(i);
635  if(i != geom->NOpDets()-1) json << ",\n"; else json << "\n";
636  }
637  json << " ]\n";
638  json << "}\n";
639 }
640 
641 // ----------------------------------------------------------------------------
642 template<class T> void
644 {
645  std::map<art::InputTag, std::map<geo::PlaneID, std::vector<recob::Hit>>> plane_hits;
646 
647  for(art::InputTag tag: evt.template getInputTags<std::vector<recob::Hit>>()){
648  typename T::template HandleT<std::vector<recob::Hit>> hits; // deduce handle type
649  // This can fail in the case of dropped products
650  if(!evt.getByLabel(tag, hits)) continue;
651 
652  for(const recob::Hit& hit: *hits){
653  // Would possibly be right for disambiguated hits?
654  // const geo::WireID wire(hit.WireID());
655 
656  for(geo::WireID wire: geom->ChannelToWire(hit.Channel())){
657  const geo::PlaneID plane(wire);
658 
659  // Correct for disambiguated hits
660  // plane_hits[plane].push_back(hit);
661 
662  // Otherwise we have to update the wire number
663  plane_hits[tag][plane].emplace_back(hit.Channel(), hit.StartTick(), hit.EndTick(), hit.PeakTime(), hit.SigmaPeakTime(), hit.RMS(), hit.PeakAmplitude(), hit.SigmaPeakAmplitude(), hit.SummedADC(), hit.Integral(), hit.SigmaIntegral(), hit.Multiplicity(), hit.LocalIndex(), hit.GoodnessOfFit(), hit.DegreesOfFreedom(), hit.View(), hit.SignalType(), wire);
664  }
665  }
666  } // end for tag
667 
668  json << plane_hits;
669 }
670 
671 // ----------------------------------------------------------------------------
672 template<class T> std::map<int, std::vector<T>> ToSnippets(const std::vector<T>& adcs, T pedestal = 0)
673 {
674  std::vector<T> snip;
675  snip.reserve(adcs.size());
676 
677  std::map<int, std::vector<T>> snips;
678 
679  int t = 0;
680  for(T adc: adcs){
681  if(adc == 0){
682  if(!snip.empty()){
683  snips[t-snip.size()] = snip;
684  snip.clear();
685  }
686  }
687  else{
688  snip.push_back(adc - pedestal);
689  }
690 
691  ++t;
692  } // end for adc
693 
694  // Save last in-progress snippet if necessary
695  if(!snip.empty()) snips[t-snip.size()] = snip;
696 
697  // this is a bit of a hack to teach the viewer how long the full trace
698  // is
699  snips[adcs.size()] = {};
700 
701  return snips;
702 }
703 
704 // ----------------------------------------------------------------------------
705 template<class T> void SerializeDigitTraces(const T& evt,
706  const geo::GeometryCore* geom,
708 {
709  // [tag][plane][wire index][t0]
710  std::map<art::InputTag, std::map<geo::PlaneID, std::map<int, std::map<int, std::vector<short>>>>> traces;
711 
712  for(art::InputTag tag: evt.template getInputTags<std::vector<raw::RawDigit>>()){
713  typename T::template HandleT<std::vector<raw::RawDigit>> digs; // deduce handle type
714  // This can fail in the case of dropped products
715  if(!evt.getByLabel(tag, digs)) continue;
716 
717  for(const raw::RawDigit& dig: *digs){
718  for(geo::WireID wire: geom->ChannelToWire(dig.Channel())){
719  const geo::PlaneID plane(wire);
720 
721  raw::RawDigit::ADCvector_t adcs(dig.Samples());
722  raw::Uncompress(dig.ADCs(), adcs, dig.Compression());
723 
724  traces[tag][plane][wire.Wire] = ToSnippets(adcs, short(dig.GetPedestal()));
725  } // end for wire
726  } // end for dig
727  } // end for tag
728 
729  json << traces;
730 }
731 
732 // ----------------------------------------------------------------------------
733 template<class T> void SerializeWireTraces(const T& evt,
734  const geo::GeometryCore* geom,
736 {
737  // [tag][plane][wire][t0]
738  std::map<art::InputTag, std::map<geo::PlaneID, std::map<int, std::map<int, std::vector<float>>>>> traces;
739 
740  for(art::InputTag tag: evt.template getInputTags<std::vector<recob::Wire>>()){
741  typename T::template HandleT<std::vector<recob::Wire>> wires; // deduce handle type
742  // This can fail in the case of dropped products
743  if(!evt.getByLabel(tag, wires)) continue;
744 
745  for(const recob::Wire& rbwire: *wires){
746  // Place all wire traces on the first wire (== channel) they are found on
747  const geo::WireID wire = geom->ChannelToWire(rbwire.Channel())[0];
748  const geo::PlaneID plane(wire);
749 
750  traces[tag][plane][wire.Wire] = ToSnippets(rbwire.Signal());
751  } // end for rbwire
752  } // end for tag
753 
754  json << traces;
755 }
756 
757 
758 // ----------------------------------------------------------------------------
759 template<class T> void _HandleGetJSON(std::string doc, int sock, const T* evt, const geo::GeometryCore* geom, const detinfo::DetectorPropertiesData* detprop, ILazy* digs, ILazy* wires)
760 {
761  const std::string mime = "application/json";
762 
763  std::stringstream ss;
764  JSONFormatter json(ss);
765 
766  /***/if(doc == "/evtid.json") SerializeEventID(*evt, json);
767  else if(doc == "/tracks.json") SerializeProduct<recob::Track>(*evt, json);
768  else if(doc == "/spacepoints.json") SerializeProduct<recob::SpacePoint>(*evt, json);
769  else if(doc == "/vtxs.json") SerializeProduct<recob::Vertex>(*evt, json);
770  else if(doc == "/trajs.json") SerializeProductByLabel<simb::MCParticle>(*evt, "largeant", json);
771  else if(doc == "/mctruth.json") SerializeProduct<simb::MCTruth>(*evt, json);
772  else if(doc == "/simedep.json") SerializeProduct<sim::SimEnergyDeposit>(*evt, json);
773  else if(doc == "/opflashes.json") SerializeProduct<recob::OpFlash>(*evt, json);
774  else if(doc == "/hits.json") SerializeHits(*evt, geom, json);
775  else if(doc == "/geom.json") SerializeGeometry(geom, *detprop, json);
776  else if(doc == "/digs.json") digs->Serialize(json);
777  else if(doc == "/wires.json") wires->Serialize(json);
778  else if(doc == "/dig_traces.json") SerializeDigitTraces(*evt, geom, json);
779  else if(doc == "/wire_traces.json") SerializeWireTraces(*evt, geom, json);
780  else{
781  write_notfound404(sock);
782  close(sock);
783  return;
784  }
785 
786  std::string response = ss.str();
787  write_ok200(sock, mime, true);
788  write_compressed_buffer((unsigned char*)response.data(), response.size(), sock, Z_DEFAULT_COMPRESSION, doc);
789  close(sock);
790 }
791 
792 // ----------------------------------------------------------------------------
793 template<class T> void _HandleGet(std::string doc, int sock, const T* evt, ILazy* digs, ILazy* wires, const geo::GeometryCore* geom, const detinfo::DetectorPropertiesData* detprop)
794 {
795  if(doc == "/") doc = "/index.html";
796 
797  if(endswith(doc, ".png")){
798  _HandleGetPNG(doc, sock, digs, wires);
799  return;
800  }
801 
802  if(endswith(doc, ".json")){
803  _HandleGetJSON(doc, sock, evt, geom, detprop, digs, wires);
804  return;
805  }
806 
807  // TODO - more sophisticated MIME type handling
808  std::string mime = "text/html";
809  if(endswith(doc, ".js" )) mime = "application/javascript";
810  if(endswith(doc, ".css")) mime = "text/css";
811  if(endswith(doc, ".ico")) mime = "image/vnd.microsoft.icon";
812 
813  // Otherwise it must be a physical file
814 
815  // Don't accidentally serve any file we shouldn't
816  const std::set<std::string> whitelist = {"/evd.css", "/evd.js", "/traces.js", "/favicon.ico", "/index.html", "/traces.html"};
817 
818  if(whitelist.count(doc)){
819  write_ok200(sock, mime, true);
820  write_compressed_file(FindWebDir()+doc, sock, Z_DEFAULT_COMPRESSION);
821  }
822  else{
823  write_notfound404(sock);
824  }
825 
826  close(sock);
827 }
828 
829 // ----------------------------------------------------------------------------
830 template<class T> int WebEVDServer<T>::EnsureListen()
831 {
832  if(fSock != 0) return 0;
833 
834  char host[1024];
835  gethostname(host, 1024);
836  char* user = getlogin();
837 
838  std::cout << "\n------------------------------------------------------------\n" << std::endl;
839 
840  // E1071 is DUNE :)
841  int port = 1071;
842 
843  // Search for an open port up-front
844  while(system(TString::Format("ss -an | grep -q %d", port).Data()) == 0) ++port;
845 
846 
847  fSock = socket(AF_INET, SOCK_STREAM, 0);
848  if(fSock == -1) return err("socket");
849 
850  // Reuse port immediately even if a previous instance just aborted.
851  const int one = 1;
852  if(setsockopt(fSock, SOL_SOCKET, SO_REUSEADDR,
853  &one, sizeof(one)) != 0) return err("setsockopt");
854 
855  sockaddr_in addr;
856  addr.sin_family = AF_INET;
857  addr.sin_port = swap_byte_order(port);
858  addr.sin_addr.s_addr = INADDR_ANY;
859 
860  if(bind(fSock, (sockaddr*)&addr, sizeof(addr)) != 0) return err("bind");
861 
862  if(listen(fSock, 128/*backlog*/) != 0) return err("listen");
863 
864 
865  std::cout << "First run" << std::endl;
866  std::cout << "ssh -L "
867  << port << ":localhost:" << port << " "
868  << user << "@" << host << std::endl << std::endl;
869  std::cout << "and then navigate to http://localhost:" << port << "/ in your favorite browser." << std::endl << std::endl;
870  // std::cout << "Press Ctrl-C here when done." << std::endl;
871 
872  return 0;
873 }
874 
875 template<class T> class LazyDigits: public ILazy
876 {
877 public:
878  LazyDigits(const T& evt, const geo::GeometryCore* geom)
879  : fEvt(&evt), fGeom(geom), fArena("dig")
880  {
881  }
882 
883  virtual void Serialize(JSONFormatter& json) override
884  {
885  Init();
886  json << fImgs;
887  }
888 
889  virtual PNGArena& GetArena() override
890  {
891  Init();
892  return fArena;
893  }
894 
895 protected:
896  void Init()
897  {
898  std::lock_guard guard(fLock);
899 
900  if(!fEvt || !fGeom) return; // already init'd
901 
902  for(art::InputTag tag: fEvt->template getInputTags<std::vector<raw::RawDigit>>()){
903  typename T::template HandleT<std::vector<raw::RawDigit>> digs; // deduce handle type
904  // This can fail in the case of dropped products
905  if(!fEvt->getByLabel(tag, digs)) continue;
906 
907  for(const raw::RawDigit& dig: *digs){
908  for(geo::WireID wire: fGeom->ChannelToWire(dig.Channel())){
909  // const geo::TPCID tpc(wire);
910  const geo::PlaneID plane(wire);
911 
912  const geo::WireID w0 = fGeom->GetBeginWireID(plane);
913 
914  if(fImgs[tag].count(plane) == 0){
915  fImgs[tag].emplace(plane, PNGView(fArena));
916  }
917 
918  PNGView& bytes = fImgs[tag].find(plane)->second;
919 
920  raw::RawDigit::ADCvector_t adcs(dig.Samples());
921  raw::Uncompress(dig.ADCs(), adcs, dig.Compression());
922 
923  for(unsigned int tick = 0; tick < adcs.size(); ++tick){
924  const int adc = adcs[tick] ? int(adcs[tick])-dig.GetPedestal() : 0;
925 
926  if(adc != 0){
927  // alpha
928  bytes(wire.Wire-w0.Wire, tick, 3) = std::min(abs(adc), 255);
929  if(adc > 0){
930  // red
931  bytes(wire.Wire-w0.Wire, tick, 0) = 255;
932  }
933  else{
934  // blue
935  bytes(wire.Wire-w0.Wire, tick, 2) = 255;
936  }
937  }
938  } // end for tick
939  } // end for wire
940  } // end for dig
941  } // end for tag
942 
943  fEvt = 0;
944  fGeom = 0;
945  }
946 
947  const T* fEvt;
949 
950  std::mutex fLock;
952 
953  std::map<art::InputTag, std::map<geo::PlaneID, PNGView>> fImgs;
954 };
955 
956 template<class T> class LazyWires: public ILazy
957 {
958 public:
959  LazyWires(const T& evt, const geo::GeometryCore* geom)
960  : fEvt(&evt), fGeom(geom), fArena("wire")
961  {
962  }
963 
964  virtual void Serialize(JSONFormatter& json) override
965  {
966  Init();
967  json << fImgs;
968  }
969 
970  virtual PNGArena& GetArena() override
971  {
972  Init();
973  return fArena;
974  }
975 
976 protected:
977  void Init()
978  {
979  std::lock_guard guard(fLock);
980 
981  if(!fEvt || !fGeom) return; // already init'd
982 
983  for(art::InputTag tag: fEvt->template getInputTags<std::vector<recob::Wire>>()){
984  typename T::template HandleT<std::vector<recob::Wire>> wires; // deduce handle type
985  // This can fail in the case of dropped products
986  if(!fEvt->getByLabel(tag, wires)) continue;
987 
988  for(const recob::Wire& rbwire: *wires){
989  for(geo::WireID wire: fGeom->ChannelToWire(rbwire.Channel())){
990  // const geo::TPCID tpc(wire);
991  const geo::PlaneID plane(wire);
992 
993  const geo::WireID w0 = fGeom->GetBeginWireID(plane);
994 
995  if(fImgs[tag].count(plane) == 0){
996  fImgs[tag].emplace(plane, PNGView(fArena));
997  }
998 
999  PNGView& bytes = fImgs[tag].find(plane)->second;
1000 
1001  const auto adcs = rbwire.Signal();
1002  for(unsigned int tick = 0; tick < adcs.size(); ++tick){
1003  if(adcs[tick] <= 0) continue;
1004 
1005  // green channel
1006  bytes(wire.Wire-w0.Wire, tick, 1) = 128; // dark green
1007  // alpha channel
1008  bytes(wire.Wire-w0.Wire, tick, 3) = std::max(0, std::min(int(10*adcs[tick]), 255));
1009  } // end for tick
1010  } // end for wire
1011  } // end for rbwire
1012  } // end for tag
1013 
1014  fEvt = 0;
1015  fGeom = 0;
1016  }
1017 
1018 protected:
1019  const T* fEvt;
1021 
1022  std::mutex fLock;
1024 
1025  std::map<art::InputTag, std::map<geo::PlaneID, PNGView>> fImgs;
1026 };
1027 
1028 // ----------------------------------------------------------------------------
1029 template<class T> Result WebEVDServer<T>::
1030 serve(const T& evt,
1031  const geo::GeometryCore* geom,
1032  const detinfo::DetectorPropertiesData& detprop)
1033 {
1034  // Don't want a sigpipe signal when the browser hangs up on us. This way we
1035  // will get an error return from the write() call instead.
1036  signal(SIGPIPE, SIG_IGN);
1037 
1038  if(EnsureListen() != 0) return kERROR;
1039 
1040  LazyDigits<T> digs(evt, geom);
1041  LazyWires<T> wires(evt, geom);
1042 
1043  std::list<std::thread> threads;
1044 
1045  while(true){
1046  int sock = accept(fSock, 0, 0);
1047  if(sock == -1) return err("accept");
1048 
1049  std::string req = read_all(sock);
1050 
1051  std::cout << req << std::endl;
1052 
1053  char* ctx;
1054  char* verb = strtok_r(&req.front(), " ", &ctx);
1055 
1056  if(verb && std::string(verb) == "GET"){
1057  char* freq = strtok_r(0, " ", &ctx);
1058  std::string sreq(freq);
1059 
1060  if(sreq == "/NEXT" ||
1061  sreq == "/PREV" ||
1062  sreq == "/NEXT_TRACES" ||
1063  sreq == "/PREV_TRACES" ||
1064  sreq == "/QUIT" ||
1065  sreq.find("/seek/") == 0 ||
1066  sreq.find("/seek_traces/") == 0){
1067  for(std::thread& t: threads) t.join();
1068  return HandleCommand(sreq, sock);
1069  }
1070  else{
1071  threads.emplace_back(_HandleGet<T>, sreq, sock, &evt, &digs, &wires, geom, &detprop);
1072  }
1073  }
1074  else{
1075  write_unimp501(sock);
1076  close(sock);
1077  }
1078  }
1079 
1080  // unreachable
1081 }
1082 
1083 template class WebEVDServer<art::Event>;
1084 // Don't provide an instantiation for gallery::Event. Callers must wrap it in
1085 // the threadsafe wrapper.
1087 
1088 } // namespace
virtual PNGArena & GetArena() override
JSONFormatter & operator<<(JSONFormatter &json, const art::InputTag &t)
void SerializeDigitTraces(const T &evt, const geo::GeometryCore *geom, JSONFormatter &json)
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
void write_ok200(int sock, const std::string content="text/html", bool gzip=false)
unsigned char png_byte
Definition: PNGArena.h:10
OpDetGeo const & OpDetGeoFromOpDet(unsigned int OpDet) const
Returns the geo::OpDetGeo object for the given detector number.
std::string FindWebDir()
std::map< int, std::vector< T > > ToSnippets(const std::vector< T > &adcs, T pedestal=0)
PlaneGeo const & Plane(unsigned int const p, unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified wire.
Collection of charge vs time digitized from a single readout channel.
Definition: RawDigit.h:69
void SerializeProductByLabel(const TEvt &evt, const std::string &in_label, JSONFormatter &json)
std::string name
Definition: PNGArena.h:55
std::map< art::InputTag, std::map< geo::PlaneID, PNGView > > fImgs
process_name opflash particleana ie x
std::vector< std::unique_ptr< std::array< png_byte, kTotBytes > > > data
Definition: PNGArena.h:59
void SerializeProduct(const TEvt &evt, JSONFormatter &json)
double TimeWidth() const
Definition: OpFlash.h:107
virtual PNGArena & GetArena() override
geo::WireID WireID() const
Definition: Hit.h:233
float RMS() const
RMS of the hit shape, in tick units.
Definition: Hit.h:220
constexpr bool operator<(CryostatID const &a, CryostatID const &b)
Order cryostats with increasing ID.
Definition: geo_types.h:706
const recob::TrackTrajectory & Trajectory() const
Access to the stored recob::TrackTrajectory.
size_t LastValidPoint() const
Returns the index of the last valid point in the trajectory.
Declaration of signal hit object.
EResult err(const char *call)
double MinX() const
Returns the world x coordinate of the start of the box.
Definition: BoxBoundedGeo.h:88
The data type to uniquely identify a Plane.
Definition: geo_types.h:472
LazyWires(const T &evt, const geo::GeometryCore *geom)
void SerializePlanes(const geo::GeometryCore *geom, const detinfo::DetectorPropertiesData &detprop, JSONFormatter &json)
std::vector< short > ADCvector_t
Type representing a (compressed) vector of ADC counts.
Definition: RawDigit.h:73
std::vector< geo::WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
void write_compressed_file(const std::string &loc, int fd_out, int level)
std::string read_all(int sock)
bool endswith(const std::string &s, const std::string &suffix)
Definition of basic raw digits.
void GetCenter(double *xyz, double localz=0.0) const
Definition: OpDetGeo.cxx:40
double MaxX() const
Returns the world x coordinate of the end of the box.
Definition: BoxBoundedGeo.h:91
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:580
process_name use argoneut_mc_hitfinder track
process_name hit
Definition: cheaterreco.fcl:51
def write
Definition: util.py:23
Geometry information for a single cryostat.
Definition: CryostatGeo.h:43
Vector GetNormalDirection() const
Returns the direction normal to the plane.
Definition: PlaneGeo.h:442
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
Vector GetIncreasingWireDirection() const
Returns the direction of increasing wires.
Definition: PlaneGeo.h:457
Definition of vertex object for LArSoft.
Definition: Vertex.h:35
LazyDigits(const T &evt, const geo::GeometryCore *geom)
float PeakAmplitude() const
The estimated amplitude of the hit at its peak, in ADC units.
Definition: Hit.h:221
void gzip_buffer(unsigned char *src, int length, std::vector< unsigned char > &dest, int level)
process_name gaushit a
short swap_byte_order(short x)
IteratorBox< plane_id_iterator,&GeometryCore::begin_plane_id,&GeometryCore::end_plane_id > IteratePlaneIDs() const
Enables ranged-for loops on all plane IDs of the detector.
double ZCenter() const
Definition: OpFlash.h:117
Access the description of detector geometry.
T abs(T value)
View_t View() const
Which coordinate does this plane measure.
Definition: PlaneGeo.h:184
double Time() const
Definition: OpFlash.h:106
virtual PNGArena & GetArena()=0
geo::Point_t Start() const
const geo::GeometryCore * fGeom
const geo::GeometryCore * fGeom
geo::Point_t End() const
IDparameter< geo::WireID > WireID
Member type of validated geo::WireID parameter.
Result HandleCommand(std::string cmd, int sock)
A trajectory in space reconstructed from hits.
T LocationAtPoint(unsigned int p) const
Position at point p. Use e.g. as:
double Depth() const
Return the depth of the plane.
Definition: PlaneGeo.h:254
Collect all the RawData header files together.
Point GetCenter() const
Returns the centre of the wire plane in world coordinates [cm].
Definition: PlaneGeo.h:479
Geometry information for a single wire plane.The plane is represented in the geometry by a solid whic...
Definition: PlaneGeo.h:82
double Width() const
Return the width of the plane.
Definition: PlaneGeo.h:246
double Length() const
Definition: OpDetGeo.h:81
bool HasValidPoint(size_t i) const
Returns whether the specified point has NoPoint flag unset.
PNGArena & arena
Definition: PNGArena.h:97
double MinZ() const
Returns the world z coordinate of the start of the box.
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Definition: electronics.h:75
void write_compressed_buffer(unsigned char *src, int length, int sock, int level, const std::string &name)
const art::EventAuxiliary & eventAuxiliary() const
virtual void Serialize(JSONFormatter &json)=0
const Cut kCosmicRay
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
const Double32_t * XYZ() const
Definition: SpacePoint.h:76
void _HandleGetPNG(std::string doc, int sock, ILazy *digs, ILazy *wires)
Description of geometry of one entire detector.
void _HandleGet(std::string doc, int sock, const T *evt, ILazy *digs, ILazy *wires, const geo::GeometryCore *geom, const detinfo::DetectorPropertiesData *detprop)
std::vector< std::vector< png_byte * > > blocks
Definition: PNGArena.h:99
void SerializeGeometry(const geo::GeometryCore *geom, const detinfo::DetectorPropertiesData &detprop, JSONFormatter &json)
Provides recob::Track data product.
unsigned int NOpDets() const
Number of OpDets in the whole detector.
double MaxY() const
Returns the world y coordinate of the end of the box.
double ConvertTicksToX(double ticks, int p, int t, int c) const
void SerializeEventID(const T &evt, JSONFormatter &json)
Vector DepthDir() const
Return the direction of plane depth.
Definition: PlaneGeo.h:236
then echo Cowardly refusing to create a new FHiCL file with the same name as the original one('${SourceName}')." >&2 exit 1 fi echo "'$
double YWidth() const
Definition: OpFlash.h:116
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:218
std::mutex fLock
geo::WireID GetBeginWireID(geo::CryostatID const &id) const
Returns the ID of the first wire in the specified cryostat.
virtual void Serialize(JSONFormatter &json) override
size_t FirstValidPoint() const
Returns the index of the first valid point in the trajectory.
void _HandleGetJSON(std::string doc, int sock, const T *evt, const geo::GeometryCore *geom, const detinfo::DetectorPropertiesData *detprop, ILazy *digs, ILazy *wires)
virtual void Serialize(JSONFormatter &json) override
void write_unimp501(int sock)
then echo File list $list not found else cat $list while read file do echo $file sed s
Definition: file_to_url.sh:60
double MaxZ() const
Returns the world z coordinate of the end of the box.
void write_notfound404(int sock)
contains information for a single step in the detector simulation
Result serve(const T &evt, const geo::GeometryCore *geom, const detinfo::DetectorPropertiesData &detprop)
unsigned int Nwires() const
Number of wires in this plane.
Definition: PlaneGeo.h:269
Energy deposition in the active material.
std::string MCTruthShortText(const simb::MCTruth &truth)
Definition: TruthText.cxx:250
Vector WidthDir() const
Return the direction of plane width.
Definition: PlaneGeo.h:221
Class holding the regions of interest of signal from a channel.
Definition: Wire.h:118
std::map< art::InputTag, std::map< geo::PlaneID, PNGView > > fImgs
Declaration of basic channel signal object.
then echo fcl name
geo::OpDetID const & ID() const
Returns the geometry ID of this optical detector.
Definition: OpDetGeo.h:72
std::mutex fLock
void SerializeWireTraces(const T &evt, const geo::GeometryCore *geom, JSONFormatter &json)
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:48
basic_json<> json
default JSON class
Definition: json.hpp:2445
double Width() const
Definition: OpDetGeo.h:82
double TotalPE() const
Definition: OpFlash.cxx:68
double Energy() const
TCEvent evt
Definition: DataStructs.cxx:8
std::size_t count(Cont const &cont)
print OUTPUT<< EOF;< setup name="Default"version="1.0">< worldref="volWorld"/></setup ></gdml > EOF close(OUTPUT)
void Uncompress(const std::vector< short > &adc, std::vector< short > &uncompressed, raw::Compress_t compress)
Uncompresses a raw data buffer.
Definition: raw.cxx:776
The data type to uniquely identify a optical detector.
Definition: geo_types.h:297
void WritePNGBytes(FILE *fout, int imgIdx, int dim)
Definition: PNGArena.cxx:108
void SerializeHits(const T &evt, const geo::GeometryCore *geom, JSONFormatter &json)
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:184
double YCenter() const
Definition: OpFlash.h:115
double Height() const
Definition: OpDetGeo.h:83
double MinY() const
Returns the world y coordinate of the start of the box.
byte bytes
Alias for common language habits.
Definition: datasize.h:101
const Point_t & position() const
Return vertex 3D position.
Definition: Vertex.h:60
BEGIN_PROLOG could also be cout
Track from a non-cascading particle.A recob::Track consists of a recob::TrackTrajectory, plus additional members relevant for a &quot;fitted&quot; track:
double WirePitch() const
Return the wire pitch (in centimeters). It is assumed constant.
Definition: PlaneGeo.h:411
double ZWidth() const
Definition: OpFlash.h:118
Vector GetWireDirection() const
Returns the direction of the wires.
Definition: PlaneGeo.h:513
open(RACETRACK) or die("Could not open file $RACETRACK for writing")