GSI Object Oriented Online Offline (Go4) GO4-6.4.0
Loading...
Searching...
No Matches
go4.mjs
Go to the documentation of this file.
1import { BasePainter, ObjectPainter, httpRequest, addDrawFunc, ensureTCanvas, draw, getHPainter, BIT, d3_select } from 'jsroot';
2
3import { source_dir, GO4 } from './core.mjs';
4
5import { ConditionPainter } from './go4canvas.mjs'
6
7import { AnalysisStatusEditor } from './analysiseditor.mjs';
8
9import { ParameterEditor } from './pareditor.mjs';
10
11// if this script loaded - webcanvas features are disabled
12GO4.web_canvas = false;
13
14/** @summary Global function */
15GO4.DrawAnalysisRatemeter = function(divid, itemname) {
16
17 function CreateHTML() {
18 let elem = d3_select('#'+divid);
19
20 if (elem.size() == 0) return null;
21 if (elem.select(".event_rate").size() > 0) return elem;
22
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>";
31 html += "</div>";
32
33 elem.style('overflow','hidden')
34 .style('padding-left','5px')
35 .style('display', 'inline-block')
36 .style('white-space', 'nowrap')
37 .html(html);
38
39 // use height of child element
40 let brlayout = getHPainter()?.brlayout,
41 sz = elem.node().clientHeight + 12;
42
43 if (brlayout)
44 brlayout.adjustSeparators(null, sz, true);
45 return elem;
46 }
47
48 let xreq = false, was_running = null;
49
50 function UpdateRatemeter() {
51 if (xreq) return;
52
53 let elem = CreateHTML();
54 if (!elem) return;
55
56 xreq = true;
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');
61
62 was_running = res.fbRunning;
63
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);
70 }).catch(() => {
71 elem.select(".event_rate").style('background-color','grey');
72 }).finally(() => {
73 xreq = false;
74 });
75 }
76
77 CreateHTML();
78
79 setInterval(UpdateRatemeter, 2000);
80}
81
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;
87}
88
89/** @summary Global function */
90GO4.AfterMsgListRequest = function(hitem, item, obj) {
91 if (!item) return;
92
93 if (!obj) {
94 delete item['last-id'];
95 return;
96 }
97 // ignore all other classes
98 if (obj._typename != 'TList') return;
99
100 obj._typename = "TGo4MsgList";
101
102 if (obj.arr.length>0) {
103 let duplicate = (('last-id' in item) && (item['last-id'] == obj.arr[0].fString));
104
105 item['last-id'] = obj.arr[0].fString;
106
107 // workaround for snapshot, it sends always same messages many times
108 if (duplicate) obj.arr.length = 1;
109
110 // add clear function for item
111 if (!('clear' in item))
112 item['clear'] = function() { delete this['last-id']; }
113 }
114}
115
116class MsgListPainter extends BasePainter {
117
118 constructor(dom, lst) {
119 super(dom);
120 this.lst = lst;
121 }
122
123 redrawObject(obj) {
124 // if (!obj._typename != 'TList') return false;
125 this.lst = obj;
126 this.drawList();
127 return true;
128 }
129
130 drawList() {
131 if (!this.lst) return;
132
133 let frame = this.selectDom();
134
135 let main = frame.select("div");
136 if (main.empty())
137 main = frame.append("div")
138 .style('max-width','100%')
139 .style('max-height','100%')
140 .style('overflow','auto');
141
142 let old = main.selectAll("pre");
143 let newsize = old.size() + this.lst.arr.length - 1;
144
145 // in the browser keep maximum 2000 entries
146 if (newsize > 2000)
147 old.select(function(d,i) { return i < newsize - 2000 ? this : null; }).remove();
148
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);
151 }
152}
153
154function drawMsgList(dom, lst) {
155
156 let painter = new MsgListPainter(dom, lst);
157
158 painter.drawList();
159
160 painter.setTopPainter();
161 return Promise.resolve(painter);
162}
163
164class AnalysisTerminalPainter extends BasePainter {
165 constructor(frame, hpainter, itemname, url) {
166 super(frame);
167 this.hpainter = hpainter;
168 this.itemname = itemname;
169 this.url = url;
170 this.draw_ready = true;
171 this.needscroll = false;
172 }
173
174 logReady(p) {
175 if (p) this.log_painter = p;
176 if(this.needscroll) {
177 this.clickScroll(true);
178 this.needscroll = false;
179 }
180 this.draw_ready = true;
181 }
182
183 checkResize() {}
184
185 cleanup(arg) {
186 if (this.log_painter) {
187 this.log_painter.cleanup();
188 delete this.log_painter;
189 }
190 if (this.interval) {
191 clearInterval(this.interval);
192 delete this.interval;
193 }
194 super.cleanup(arg);
195 }
196
197 processTimer() {
198 let subid = "anaterm_output_container";
199 // detect if drawing disappear
200 if (d3_select("#" + subid).empty())
201 return this.cleanup();
202
203 if (!this.draw_ready) return;
204
205 let msgitem = this.itemname.replace("Control/Terminal", "Status/Log");
206
207 this.draw_ready = false;
208
209 if (this.log_painter)
210 this.hpainter.display(msgitem, 'update:', subid).then(() => this.logReady());
211 else
212 this.hpainter.display(msgitem, '', subid).then(p => this.logReady(p));
213 }
214
215 clickCommand(kind) {
216 let command = this.itemname.replace("Terminal", "CmdExecute");
217 this.hpainter.executeCommand(command, null, kind). then(() => { this.needscroll = true; });
218 }
219
220 clickClear() {
221 d3_select("#anaterm_output_container").html("");
222 }
223
224 clickScroll(last) {
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();
228 }
229
230 fillDisplay() {
231 this.setTopPainter();
232 this.interval = setInterval(() => this.processTimer(), 2000);
233 let dom = this.selectDom();
234
235 dom.select(".go4_clearterm")
236 .on("click", () => this.clickClear())
237 .style('background-image', `url(${source_dir}icons/clear.png)`);
238
239 dom.select(".go4_startterm")
240 .on("click", () => this.clickScroll(false))
241 .style('background-image', `url(${source_dir}icons/shiftup.png)`);
242
243 dom.select(".go4_endterm")
244 .on("click", () => this.clickScroll(true))
245 .style('background-image', `url(${source_dir}icons/shiftdown.png)`);
246
247 dom.select(".go4_printhistos")
248 .on("click", () => this.clickCommand("@PrintHistograms()"))
249 .style('background-image', `url(${source_dir}icons/hislist.png)`);
250
251 dom.select(".go4_printcond")
252 .on("click", () => this.clickCommand("@PrintConditions()"))
253 .style('background-image', `url(${source_dir}icons/condlist.png)`);
254
255 dom.select(".go4_executescript")
256 .style('background-image', `url(${source_dir}icons/macro_t.png)`);
257
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; });
264 });
265
266 }
267}
268
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;
274
275 if (!url || !frame) return null;
276
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");
281
282 let player = new AnalysisTerminalPainter(frame, hpainter, itemname, url);
283
284 return httpRequest(`${source_dir}html5/terminal.html`, 'text')
285 .then(code => {
286 elem.html(code);
287 player.fillDisplay();
288 return player;
289 });
290}
291
292const op_LineColor = 5,
293 op_LineStyle = 6,
294 op_LineWidth = 7,
295 op_FillColor = 8,
296 op_FillStyle = 9,
297 op_HisStatsX1 = 19,
298 op_HisStatsY1 = 20,
299 op_HisStatsX2 = 21,
300 op_HisStatsY2 = 22,
301 op_HisStats = 24,
302 op_HisTitle = 25,
303 op_HisStatsOpt = 85,
304 op_HisStatsFit = 86,
305 op_Draw = 0x4001,
306
307 PictureIndex = -1;
308
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];
315 return null;
316}
317
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);
327}
328
329function drawSpecialObjects(dom, pic, k) {
330 if (!pic.fxSpecialObjects || (k >= pic.fxSpecialObjects.arr.length))
331 return Promise.resolve(false);
332
333 return draw(dom, pic.fxSpecialObjects.arr[k], pic.fxSpecialObjects.opt[k]).then(() => drawSpecialObjects(dom, pic, k+1));
334}
335
336function drawPictureObjects(dom, pic, k) {
337 if (!dom || !pic.fxNames)
338 return Promise.resolve(false);
339
340 let arr = pic.fxNames ? pic.fxNames.arr : null;
341 if (!arr || (k >= arr.length))
342 return drawSpecialObjects(dom, pic, 0);
343
344 let n = pic.fxNames.arr[k], itemname = "", isth2 = false;
345
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;
350 }
351 });
352
353 if (!itemname) {
354 console.log('not found object with name', n.fString);
355 return drawPictureObjects(dom, pic, k+1);
356 }
357
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";
363
364 return getHPainter().display(itemname, opt, dom).then(painter => {
365 if (!painter) return;
366 let need_redraw = false;
367
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);
374 need_redraw = true;
375 }
376 }
377
378 if (painter.fillatt) {
379 let fcol = getOptValue(pic, k, op_FillColor),
380 fstyle = getOptValue(pic, k, op_FillStyle);
381
382 if ((fcol !== null) && (fstyle !== null)) {
383 painter.fillatt.change(fcol, fstyle, painter.getCanvSvg());
384 need_redraw = true;
385 }
386 }
387
388 if (typeof painter.getHisto == 'function' && painter.createHistDrawAttributes && painter.isMainPainter()) {
389 const kNoStats = BIT(9),
390 kNoTitle = BIT(17);
391
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);
403
404 if ((istitle !== null) && (!istitle != histo.TestBit(kNoTitle))) {
405 histo.InvertBit(kNoTitle); need_redraw = true;
406 }
407 if ((isstats !== null) && (!isstats != histo.TestBit(kNoStats))) {
408 histo.InvertBit(kNoStats); need_redraw = true;
409 }
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;
415 if (has_stats_pos) {
416 stat.fX1NDC = sx1;
417 stat.fY1NDC = sy1;
418 stat.fX2NDC = sx2;
419 stat.fY2NDC = sy2;
420 }
421 if (statpainter) statpainter.redraw();
422 }
423 }
424
425 if (need_redraw) return painter.redraw();
426
427 }).then(() => drawPictureObjects(dom, pic, k+1));
428}
429
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);
434
435 let subpic = pic.fxSubPictures.arr[nsub];
436
437 let subpad_painter = pad_painter.getSubPadPainter(1 + subpic.fiPosY*pic.fiNDivX + subpic.fiPosX);
438
439 return drawPicture(subpad_painter, subpic).then(() => drawSubPictures(pad_painter, pic, nsub+1));
440}
441
442function drawPicture(pad_painter, pic) {
443 if (!pad_painter)
444 return Promise.resolve(false);
445
446 if (pic.fiNDivX * pic.fiNDivY > 1)
447 return pad_painter.divide(pic.fiNDivX, pic.fiNDivY).then(() => drawSubPictures(pad_painter, pic, 0)).then(() => {
448 return pad_painter;
449 });
450
451 return drawPictureObjects(pad_painter, pic, 0).then(() => {
452 return pad_painter;
453 });
454}
455
456function drawGo4Picture(dom, pic) {
457 if (!getHPainter())
458 return null;
459
460 let painter = new ObjectPainter(dom, pic);
461
462 return ensureTCanvas(painter, false).then(() => {
463 let pad_painter = painter.getPadPainter();
464
465 painter.removeFromPadPrimitives();
466
467 return drawPicture(pad_painter, pic);
468 }).then(() => painter); // return dummy painter
469}
470
471
472addDrawFunc({ name: 'TGo4AnalysisWebStatus', func: AnalysisStatusEditor.draw, opt: 'editor' });
473
474addDrawFunc({ name: 'TGo4MsgList', func: drawMsgList });
475addDrawFunc({ name: 'TGo4Picture', func: drawGo4Picture, icon: `${source_dir}icons/picture.png` });
476
477addDrawFunc({ name: 'TGo4MbsEvent', noinspect: true });
478addDrawFunc({ name: 'TGo4EventElement', noinspect: true });