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