1import { BasePainter, ObjectPainter, httpRequest, addDrawFunc, ensureTCanvas, draw, getHPainter, BIT, d3_select } from 'jsroot';
3import { source_dir, GO4 } from './core.mjs';
5import { ConditionPainter } from './go4canvas.mjs'
7import { AnalysisStatusEditor } from './analysiseditor.mjs';
9import { ParameterEditor } from './pareditor.mjs';
11// if this script loaded - webcanvas features are disabled
12GO4.web_canvas = false;
14/** @summary Global function */
15GO4.DrawAnalysisRatemeter = function(divid, itemname) {
17 function CreateHTML() {
18 let elem = d3_select('#'+divid);
20 if (elem.size() == 0) return null;
21 if (elem.select(".event_rate").size() > 0) return elem;
23 let html = "<div style='padding-top:2px'>";
24 html += "<img class='go4_logo' style='vertical-align:middle;margin-left:5px;margin-right:5px;' src='go4sys/icons/go4logorun4.gif' alt='logo'></img>";
25 html += "<label class='event_source' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>file.lmd</label> ";
26 html += "<label class='event_rate' style='border: 1px solid gray; font-size:large; vertical-align:middle; background-color: grey'; padding-left:3px; padding-right:3px;>---</label> Ev/s ";
27 html += "<label class='aver_rate' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>---</label> Ev/s ";
28 html += "<label class='run_time' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>---</label> s ";
29 html += "<label class='total_events' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>---</label> Events ";
30 html += "<label class='analysis_time' style='border: 1px solid gray; font-size:large; vertical-align:middle; padding-left:3px; padding-right:3px;'>time</label>";
33 elem.style('overflow','hidden')
34 .style('padding-left','5px')
35 .style('display', 'inline-block')
36 .style('white-space', 'nowrap')
39 // use height of child element
40 let brlayout = getHPainter()?.brlayout,
41 sz = elem.node().clientHeight + 12;
44 brlayout.adjustSeparators(null, sz, true);
48 let xreq = false, was_running = null;
50 function UpdateRatemeter() {
53 let elem = CreateHTML();
57 httpRequest(itemname + "/root.json.gz", 'object').then(res => {
58 elem.select(".event_rate").style('background-color', res.fbRunning ? 'lightgreen' : 'red');
59 if (was_running != res.fbRunning)
60 elem.select(".go4_logo").attr("src", res.fbRunning ? 'go4sys/icons/go4logorun4.gif' : 'go4sys/icons/go4logo_t.png');
62 was_running = res.fbRunning;
64 elem.select(".event_source").text(res.fxEventSource == "" ? "<source>" : res.fxEventSource);
65 elem.select(".event_rate").text(res.fdRate.toFixed(1));
66 elem.select(".aver_rate").text((res.fdTime > 0 ? res.fuCurrentCount / res.fdTime : 0).toFixed(1));
67 elem.select(".run_time").text(res.fdTime.toFixed(1));
68 elem.select(".total_events").text(res.fuCurrentCount);
69 elem.select(".analysis_time").text(res.fxDateString == "" ? "<datime>" : res.fxDateString);
71 elem.select(".event_rate").style('background-color','grey');
79 setInterval(UpdateRatemeter, 2000);
82/** @summary Global function */
83GO4.MakeMsgListRequest = function(hitem, item) {
84 let arg = "&max=2000";
85 if ('last-id' in item) arg+= "&id="+item['last-id'];
86 return 'exe.json.gz?method=Select' + arg;
89/** @summary Global function */
90GO4.AfterMsgListRequest = function(hitem, item, obj) {
94 delete item['last-id'];
97 // ignore all other classes
98 if (obj._typename != 'TList') return;
100 obj._typename = "TGo4MsgList";
102 if (obj.arr.length>0) {
103 let duplicate = (('last-id' in item) && (item['last-id'] == obj.arr[0].fString));
105 item['last-id'] = obj.arr[0].fString;
107 // workaround for snapshot, it sends always same messages many times
108 if (duplicate) obj.arr.length = 1;
110 // add clear function for item
111 if (!('clear' in item))
112 item['clear'] = function() { delete this['last-id']; }
116class MsgListPainter extends BasePainter {
118 constructor(dom, lst) {
124 // if (!obj._typename != 'TList') return false;
131 if (!this.lst) return;
133 let frame = this.selectDom();
135 let main = frame.select("div");
137 main = frame.append("div")
138 .style('max-width','100%')
139 .style('max-height','100%')
140 .style('overflow','auto');
142 let old = main.selectAll("pre");
143 let newsize = old.size() + this.lst.arr.length - 1;
145 // in the browser keep maximum 2000 entries
147 old.select(function(d,i) { return i < newsize - 2000 ? this : null; }).remove();
149 for (let i = this.lst.arr.length-1; i > 0; i--)
150 main.append("pre").style('margin','3px').html(this.lst.arr[i].fString);
154function drawMsgList(dom, lst) {
156 let painter = new MsgListPainter(dom, lst);
160 painter.setTopPainter();
161 return Promise.resolve(painter);
164class AnalysisTerminalPainter extends BasePainter {
165 constructor(frame, hpainter, itemname, url) {
167 this.hpainter = hpainter;
168 this.itemname = itemname;
170 this.draw_ready = true;
171 this.needscroll = false;
175 if (p) this.log_painter = p;
176 if(this.needscroll) {
177 this.clickScroll(true);
178 this.needscroll = false;
180 this.draw_ready = true;
186 if (this.log_painter) {
187 this.log_painter.cleanup();
188 delete this.log_painter;
191 clearInterval(this.interval);
192 delete this.interval;
198 let subid = "anaterm_output_container";
199 // detect if drawing disappear
200 if (d3_select("#" + subid).empty())
201 return this.cleanup();
203 if (!this.draw_ready) return;
205 let msgitem = this.itemname.replace("Control/Terminal", "Status/Log");
207 this.draw_ready = false;
209 if (this.log_painter)
210 this.hpainter.display(msgitem, 'update:', subid).then(() => this.logReady());
212 this.hpainter.display(msgitem, '', subid).then(p => this.logReady(p));
216 let command = this.itemname.replace("Terminal", "CmdExecute");
217 this.hpainter.executeCommand(command, null, kind). then(() => { this.needscroll = true; });
221 d3_select("#anaterm_output_container").html("");
225 // inner frame created by hpainter has the scrollbars, i.e. first child
226 let nodes = d3_select("#anaterm_output_container").selectAll("pre").nodes();
227 if (nodes) nodes[last ? nodes.length-1 : 0].scrollIntoView();
231 this.setTopPainter();
232 this.interval = setInterval(() => this.processTimer(), 2000);
233 let dom = this.selectDom();
235 dom.select(".go4_clearterm")
236 .on("click", () => this.clickClear())
237 .style('background-image', `url(${source_dir}icons/clear.png)`);
239 dom.select(".go4_startterm")
240 .on("click", () => this.clickScroll(false))
241 .style('background-image', `url(${source_dir}icons/shiftup.png)`);
243 dom.select(".go4_endterm")
244 .on("click", () => this.clickScroll(true))
245 .style('background-image', `url(${source_dir}icons/shiftdown.png)`);
247 dom.select(".go4_printhistos")
248 .on("click", () => this.clickCommand("@PrintHistograms()"))
249 .style('background-image', `url(${source_dir}icons/hislist.png)`);
251 dom.select(".go4_printcond")
252 .on("click", () => this.clickCommand("@PrintConditions()"))
253 .style('background-image', `url(${source_dir}icons/condlist.png)`);
255 dom.select(".go4_executescript")
256 .style('background-image', `url(${source_dir}icons/macro_t.png)`);
258 dom.select(".go4_anaterm_cmd_form").on("submit", event => {
259 event.preventDefault();
260 let command = this.itemname.replace("Terminal", "CmdExecute");
261 let cmdpar = document.getElementById("go4_anaterm_command").value;
262 console.log("submit command - " + cmdpar);
263 this.hpainter.executeCommand(command, null, cmdpar).then(() => { this.needscroll = true; });
269/** @summary Global function */
270GO4.drawAnalysisTerminal = function(hpainter, itemname) {
271 let url = hpainter.getOnlineItemUrl(itemname),
272 mdi = hpainter.getDisplay(),
273 frame = mdi ? mdi.findFrame(itemname, true) : null;
275 if (!url || !frame) return null;
277 let elem = d3_select(frame),
278 h = frame.clientHeight,
279 w = frame.clientWidth;
280 if ((h < 10) && (w > 10)) elem.style("height", Math.round(w*0.7)+"px");
282 let player = new AnalysisTerminalPainter(frame, hpainter, itemname, url);
284 return httpRequest(`${source_dir}html5/terminal.html`, 'text')
287 player.fillDisplay();
292const op_LineColor = 5,
309function getOptValue(pic, indx, typ) {
310 if (!pic || !pic.fxOptIndex) return null;
311 let srch = indx + 1 + (typ << 16);
312 for (let k = 0; k < pic.fxOptIndex.length; ++k)
313 if (pic.fxOptIndex[k] == srch)
314 return pic.fxOptValue[k];
318function getOptDouble(pic, indx, typ) {
319 let ivalue = getOptValue(pic, indx, typ);
320 if (ivalue === null) return null;
321 const buffer = new ArrayBuffer(16),
322 view = new DataView(buffer),
323 big = BigInt(ivalue); // to be sure that BigInt is used
324 view.setUint32(0, Number(big >> 32n));
325 view.setUint32(4, Number(big % (2n ** 32n)));
326 return view.getFloat64(0);
329function drawSpecialObjects(dom, pic, k) {
330 if (!pic.fxSpecialObjects || (k >= pic.fxSpecialObjects.arr.length))
331 return Promise.resolve(false);
333 return draw(dom, pic.fxSpecialObjects.arr[k], pic.fxSpecialObjects.opt[k]).then(() => drawSpecialObjects(dom, pic, k+1));
336function drawPictureObjects(dom, pic, k) {
337 if (!dom || !pic.fxNames)
338 return Promise.resolve(false);
340 let arr = pic.fxNames ? pic.fxNames.arr : null;
341 if (!arr || (k >= arr.length))
342 return drawSpecialObjects(dom, pic, 0);
344 let n = pic.fxNames.arr[k], itemname = "", isth2 = false;
346 getHPainter().forEachItem(item => {
347 if (item._name == n.fString) {
348 itemname = getHPainter().itemFullName(item);
349 if (item._kind?.indexOf('ROOT.TH2') == 0) isth2 = true;
354 console.log('not found object with name', n.fString);
355 return drawPictureObjects(dom, pic, k+1);
358 let opt = isth2 ? "col" : "",
359 iopt = getOptValue(pic, k, op_Draw);
360 if ((iopt !== null) && pic.fxOptObjects)
361 opt = pic.fxOptObjects.arr[iopt].fString;
362 if (k > 0) opt += " same";
364 return getHPainter().display(itemname, opt, dom).then(painter => {
365 if (!painter) return;
366 let need_redraw = false;
368 if (painter.lineatt) {
369 let lcol = getOptValue(pic, k, op_LineColor),
370 lwidth = getOptValue(pic, k, op_LineWidth),
371 lstyle = getOptValue(pic, k, op_LineStyle);
372 if ((lcol !== null) && (lwidth !== null) && (lstyle !== null)) {
373 painter.lineatt.change(painter.getColor(lcol), lwidth, lstyle);
378 if (painter.fillatt) {
379 let fcol = getOptValue(pic, k, op_FillColor),
380 fstyle = getOptValue(pic, k, op_FillStyle);
382 if ((fcol !== null) && (fstyle !== null)) {
383 painter.fillatt.change(fcol, fstyle, painter.getCanvSvg());
388 if (typeof painter.getHisto == 'function' && painter.createHistDrawAttributes && painter.isMainPainter()) {
389 const kNoStats = BIT(9),
392 let histo = painter.getHisto(),
393 stat = painter.findStat(),
394 sx1 = getOptDouble(pic, PictureIndex, op_HisStatsX1),
395 sy1 = getOptDouble(pic, PictureIndex, op_HisStatsY1),
396 sx2 = getOptDouble(pic, PictureIndex, op_HisStatsX2),
397 sy2 = getOptDouble(pic, PictureIndex, op_HisStatsY2),
398 has_stats_pos = (sx1 !== null) && (sy1 !== null) && (sx2 !== null) && (sy2 !== null),
399 istitle = getOptValue(pic, PictureIndex, op_HisTitle),
400 isstats = getOptValue(pic, PictureIndex, op_HisStats),
401 statsopt = getOptValue(pic, PictureIndex, op_HisStatsOpt),
402 fitopt = getOptValue(pic, PictureIndex, op_HisStatsFit);
404 if ((istitle !== null) && (!istitle != histo.TestBit(kNoTitle))) {
405 histo.InvertBit(kNoTitle); need_redraw = true;
407 if ((isstats !== null) && (!isstats != histo.TestBit(kNoStats))) {
408 histo.InvertBit(kNoStats); need_redraw = true;
410 if (stat && ((statsopt !== null) || (fitopt !== null) || has_stats_pos)) {
411 if (statsopt !== null) stat.fOptStat = statsopt;
412 if (fitopt !== null) stat.fOptFit = fitopt;
413 let pp = painter.getPadPainter(),
414 statpainter = pp ? pp.findPainterFor(stat) : null;
421 if (statpainter) statpainter.redraw();
425 if (need_redraw) return painter.redraw();
427 }).then(() => drawPictureObjects(dom, pic, k+1));
430function drawSubPictures(pad_painter, pic, nsub) {
431 let arr = pic && pic.fxSubPictures ? pic.fxSubPictures.arr : null;
432 if (!arr || nsub >= arr.length)
433 return Promise.resolve(pad_painter);
435 let subpic = pic.fxSubPictures.arr[nsub];
437 let subpad_painter = pad_painter.getSubPadPainter(1 + subpic.fiPosY*pic.fiNDivX + subpic.fiPosX);
439 return drawPicture(subpad_painter, subpic).then(() => drawSubPictures(pad_painter, pic, nsub+1));
442function drawPicture(pad_painter, pic) {
444 return Promise.resolve(false);
446 if (pic.fiNDivX * pic.fiNDivY > 1)
447 return pad_painter.divide(pic.fiNDivX, pic.fiNDivY).then(() => drawSubPictures(pad_painter, pic, 0)).then(() => {
451 return drawPictureObjects(pad_painter, pic, 0).then(() => {
456function drawGo4Picture(dom, pic) {
460 let painter = new ObjectPainter(dom, pic);
462 return ensureTCanvas(painter, false).then(() => {
463 let pad_painter = painter.getPadPainter();
465 painter.removeFromPadPrimitives();
467 return drawPicture(pad_painter, pic);
468 }).then(() => painter); // return dummy painter
472addDrawFunc({ name: 'TGo4AnalysisWebStatus', func: AnalysisStatusEditor.draw, opt: 'editor' });
474addDrawFunc({ name: 'TGo4MsgList', func: drawMsgList });
475addDrawFunc({ name: 'TGo4Picture', func: drawGo4Picture, icon: `${source_dir}icons/picture.png` });
477addDrawFunc({ name: 'TGo4MbsEvent', noinspect: true });
478addDrawFunc({ name: 'TGo4EventElement', noinspect: true });