3JSROOT.define(["painter"], jsrp => {
5 if (typeof GO4 != "object") {
6 let e1 = new Error("analysiseditor.js requires GO4 to be already loaded");
7 e1.source = "analysiseditor.js";
12 GO4EV_NULL: 0, // no event store/source
13 GO4EV_FILE: 1, // root file with own tree
14 GO4EV_TREE: 2, // branch of singleton tree
15 GO4EV_MBS_FILE: 3, // mbs listmode file (input only)
16 GO4EV_MBS_STREAM: 4, // mbs stream server (input only)
17 GO4EV_MBS_TRANSPORT: 5, // mbs transport server (input only)
18 GO4EV_MBS_EVENTSERVER: 6, // mbs event server (input only)
19 GO4EV_MBS_REVSERV: 7, // remote event server (input only)
20 GO4EV_BACK: 8, // backstore in memory (pseudo-ringbuffer?)
21 GO4EV_USER: 9, // user defined source class
22 GO4EV_MBS_RANDOM: 10, // random generated mbs event
23 GO4EV_HDF5: 11 // HDF5 file format
26 function createJSMenu(event, painter) {
27 return jsrp.createMenu(event,painter).then(menu => {
29 menu.confirm = function(head, msg) {
30 let res = window.confirm(`${head}\n${msg}`);
31 return Promise.resolve(res);
37 class AnalysisStatusEditor extends JSROOT.BasePainter {
38 constructor(dom, stat) {
41 this.changes = [["dummy0", "init0"],["dummy1","init1"]]; // changes array stepwise, index 0 = no step, index = stepindex+1
45 markChanged(key, step) {
46 // first avoid duplicate keys:
47 for (let index = 0; index < this.changes[step].length; index++) {
48 if (this.changes[step][index]== key) return;
50 this.changes[step].push(key);
51 console.log("Mark changed :%s at step %d", key, step);
53 this.selectDom().select(".buttonAnaChangeLabel").style('display', null); // show warning sign
56 // clear changed elements' list, make warning sign invisible
58 let numsteps = this.changes.length;
59 for (let step = 0; step < numsteps ; step++)
60 this.changes[step] = [];
61 this.selectDom().select(".buttonAnaChangeLabel").style('display', 'none'); // hide warning sign
64 /** @summary scan changed value list and return optionstring to be send to server */
65 evaluateChanges(optionstring) {
67 let numsteps = this.changes.length;
68 for (let step = 0; step < numsteps ; step++) {
69 let theElement = editor.stat.fxStepArray.arr[step],
71 len = this.changes[step].length;
73 for (let index = 0; index < len ; index++) {
75 let key = this.changes[step][index],
77 step_prefix = `&${key}_${step}=`;
79 // here mapping of key to editor field:
80 if (key == "stepenabled") {
81 stepoptions += step_prefix + theElement.fbProcessEnabled;
82 } else if (key == "sourceenabled") {
83 stepoptions += step_prefix + theElement.fbSourceEnabled;
84 } else if (key == "storeenabled") {
85 stepoptions += step_prefix + theElement.fbStoreEnabled;
86 } else if (key == "sourcesel") {
87 stepoptions += step_prefix + theElement.fxSourceType.fiID;
88 } else if (key == "sourcename") {
89 stepoptions += step_prefix + theElement.fxSourceType.fName;
90 } else if (key == "sourcetag") {
91 stepoptions += step_prefix + theElement.fxSourceType.fxTagFile;
92 } else if (key == "sourceport") {
93 stepoptions += step_prefix + theElement.fxSourceType.fiPort;
94 } else if (key == "sourcetmout") {
95 stepoptions += step_prefix + theElement.fxSourceType.fiTimeout;
96 } else if (key == "sourceretry") {
97 stepoptions += step_prefix + theElement.fxSourceType.fiRetryCnt;
98 } else if (key == "sourcefirst") {
99 stepoptions += step_prefix + theElement.fxSourceType.fuStartEvent;
100 } else if (key == "sourcelast") {
101 stepoptions += step_prefix + theElement.fxSourceType.fuStopEvent;
102 } else if (key == "sourceskip") {
103 stepoptions += step_prefix + theElement.fxSourceType.fuEventInterval;
104 } else if (key == "storesel") {
105 stepoptions += step_prefix + theElement.fxStoreType.fiID;
106 } else if (key == "storename") {
107 stepoptions += step_prefix + theElement.fxStoreType.fName;
108 } else if (key == "storesplit") {
109 stepoptions += step_prefix + theElement.fxStoreType.fiSplit;
110 } else if (key == "storebuf") {
111 stepoptions += step_prefix + theElement.fxStoreType.fiBufsize;
112 } else if (key == "storecomp") {
113 stepoptions += step_prefix + theElement.fxStoreType.fiCompression;
114 } else if (key == "storeasf") {
115 stepoptions += step_prefix + theElement.fxStoreType.fiAutosavesize;
116 } else if (key == "storeover") {
117 stepoptions += step_prefix + theElement.fxStoreType.fbOverwrite;
119 // non step specific options are in step 0 options too:
120 else if (key == "asfname") {
121 stepoptions += preifx + editor.stat.fxAutoFileName;
122 } else if (key == "asfenabled") {
123 stepoptions += preifx + editor.stat.fbAutoSaveOn;
124 } else if (key == "asftime") {
125 stepoptions += preifx + editor.stat.fiAutoSaveInterval;
126 } else if (key == "asfcomp") {
127 stepoptions += preifx + editor.stat.fiAutoSaveCompression;
128 } else if (key == "asfoverwrite") {
129 stepoptions += preifx + editor.stat.fbAutoSaveOverwrite;
130 } else if (key == "anaprefsname") {
131 stepoptions += preifx + editor.stat.fxConfigFileName;
133 console.log(`Warning: evaluateChanges found unknown key: ${key}`);
138 optionstring += stepoptions;
140 console.log("Resulting option string:%s", optionstring);
144 showStepEditor(tab, theElement, theIndex) {
146 let sourcebox = tab.select(".step_box_source_enab"),
147 storebox = tab.select(".step_box_store_enab"),
148 step_source = tab.select(".step_source"),
149 step_store = tab.select(".step_store"),
150 sourcemore = tab.select(".step_source_expand");
152 // here step control checkboxes and source/store visibility:
153 if (theElement.fbProcessEnabled) {
154 sourcebox.attr('disabled', null);
155 storebox.attr('disabled', null);
156 step_source.attr('disabled', theElement.fbSourceEnabled ? null : "true");
157 step_store.style('display', theElement.fbStoreEnabled ? null : "none");
159 sourcebox.attr('disabled', "true");
160 storebox.attr('disabled', "true");
161 step_source.attr('disabled', "true");
162 step_store.style('display', "none");
165 let show_tag = false, show_ports = false, show_args = false, show_more = sourcemore.property("checked");
167 switch (theElement.fxSourceType.fiID) {
168 case GO4.EvIOType.GO4EV_MBS_FILE:
169 show_tag = show_args = show_more; break;
170 case GO4.EvIOType.GO4EV_MBS_STREAM:
171 case GO4.EvIOType.GO4EV_MBS_TRANSPORT:
172 case GO4.EvIOType.GO4EV_MBS_EVENTSERVER:
173 case GO4.EvIOType.GO4EV_MBS_REVSERV:
174 show_ports = show_args = show_more; break;
175 case GO4.EvIOType.GO4EV_USER:
176 show_ports = show_more; break;
177 case GO4.EvIOType.GO4EV_FILE:
178 case GO4.EvIOType.GO4EV_MBS_RANDOM:
180 // show no extra parameters
184 tab.select(".step_source_tagfile_args").style('display', show_tag ? null : 'none');
185 tab.select(".step_source_port_args").style('display', show_ports ? null : 'none');
186 tab.select(".step_source_number_args").style('display', show_args ? 'inline' : 'none');
188 let enbale_store_pars = false, enbale_store_name = true;
189 switch (theElement.fxStoreType.fiID) {
190 case GO4.EvIOType.GO4EV_FILE:
191 enbale_store_pars = true;
193 case GO4.EvIOType.GO4EV_BACK:
194 enbale_store_pars = true;
195 enbale_store_name = false;
197 case GO4.EvIOType.GO4EV_USER:
203 tab.select(".step_store_pars").selectAll("input").each(function() {
204 d3.select(this).attr("disabled", enbale_store_pars ? null : "true");
207 tab.select(".step_store_name").attr("disabled", enbale_store_name ? null : "true");
211 let dom = this.selectDom(),
214 let head_html = "", step_html = "";
215 stat.fxStepArray.arr.forEach((step, indx) => {
216 head_html += `<button for="go4_analysis_step_${indx}">${step.fName}</button>`;
217 step_html += `<div class="go4_analysis_step_${indx}" style="display:none">${this.stepPageHtml}</div>`;
220 dom.select(".ana_step_tabs_header").html(head_html);
222 dom.select(".ana_step_tabs_body").html(step_html);
224 // assign tabs buttons handlers
225 dom.select('.ana_step_tabs_header').selectAll("button").on("click", function() {
226 let btn = d3.select(this);
228 dom.select('.ana_step_tabs_header').selectAll("button").each(function() {
229 d3.select(this).classed("active_btn", false);
232 btn.classed("active_btn", true);
234 dom.selectAll('.ana_step_tabs_body>div').each(function() {
235 let tab = d3.select(this);
236 tab.style('display', tab.classed(btn.attr("for")) ? null : "none");
240 // activate first step
241 dom.select(".ana_step_tabs_body").select(".go4_analysis_step_0").style('display', null);
242 dom.select('.ana_step_tabs_header').select("button").classed("active_btn", true);
244 stat.fxStepArray.arr.forEach((theElement, theIndex) => {
245 let tab = dom.select(".ana_step_tabs_body").select(`.go4_analysis_step_${theIndex}`);
247 let enablebox = tab.select(".step_box_step_enab"),
248 sourcebox = tab.select(".step_box_source_enab"),
249 storebox = tab.select(".step_box_store_enab"),
250 sourcesel = tab.select(".step_source_select"),
251 sourcemore = tab.select(".step_source_expand"),
252 sourcename = tab.select(".step_source_name"),
253 sourcetag = tab.select(".step_source_tagfile"),
254 sourceport = tab.select(".step_source_port"),
255 sourcetmout = tab.select(".step_source_tmout"),
256 sourceretry = tab.select(".step_source_retry"),
257 sourcefirst = tab.select(".step_source_firstev"),
258 sourcelast = tab.select(".step_source_lastev"),
259 sourceskip = tab.select(".step_source_stepev"),
260 storesel = tab.select(".step_store_select"),
261 storename = tab.select(".step_store_name"),
262 storesplit = tab.select(".step_store_split"),
263 storebuf = tab.select(".step_store_buf"),
264 storecomp = tab.select(".step_store_comp"),
265 storetreeasf = tab.select(".step_store_asf"),
266 storeover = tab.select(" .step_store_overwrite");
268 enablebox.property('checked', theElement.fbProcessEnabled)
270 this.markChanged("stepenabled", theIndex);
271 theElement.fbProcessEnabled = enablebox.property('checked');
272 this.showStepEditor(tab, theElement, theIndex);
275 sourcebox.property('checked', theElement.fbSourceEnabled)
277 this.markChanged("sourceenabled", theIndex);
278 theElement.fbSourceEnabled = sourcebox.property('checked');
279 this.showStepEditor(tab, theElement, theIndex);
282 storebox.property('checked', theElement.fbStoreEnabled)
284 this.markChanged("storeenabled", theIndex);
285 theElement.fbStoreEnabled = storebox.property('checked');
286 this.showStepEditor(tab, theElement, theIndex);
289 //// EVENT SOURCE: /////////////////////////////////////////////////
290 sourcesel.property("value", theElement.fxSourceType.fiID).on("change", () => {
291 this.markChanged("sourcesel", theIndex);
293 theElement.fxSourceType.fiID = parseInt(sourcesel.property("value"));
295 this.showStepEditor(tab, theElement, theIndex);
296 }); // source selectmenu change
298 sourcemore.on("click", () => {
299 this.showStepEditor(tab, theElement, theIndex);
302 sourcename.property("value", theElement.fxSourceType.fName)
303 .on("change", () => {
304 this.markChanged("sourcename", theIndex);
305 theElement.fxSourceType.fName = sourcename.property("value").trim();
308 sourcetag.property("value", theElement.fxSourceType.fxTagFile || "")
309 .on("change", () => {
310 this.markChanged("sourcetag", theIndex);
311 theElement.fxSourceType.fxTagFile = sourcetag.property("value").trim();
314 sourceport.property("value", theElement.fxSourceType.fiPort || 0)
315 .on("change", () => {
316 this.markChanged("sourceport", theIndex);
317 theElement.fxSourceType.fiPort = parseInt(sourceport.property("value"));
320 sourcetmout.property("value", theElement.fxSourceType.fiTimeout || 100)
321 .on("change", () => {
322 this.markChanged("sourcetmout", theIndex);
323 theElement.fxSourceType.fiTimeout = parseInt(sourcetmout.property("value"));
326 sourceretry.property("value", theElement.fxSourceType.fiRetryCnt || 0)
327 .on("change", () => {
328 this.markChanged("sourceretry", theIndex);
329 theElement.fxSourceType.fiRetryCnt = parseInt(sourceretry.property("value"));
332 sourcefirst.property("value", theElement.fxSourceType.fuStartEvent || 0)
333 .on("change", () => {
334 this.markChanged("sourcefirst", theIndex);
335 theElement.fxSourceType.fuStartEvent = parseInt(sourcefirst.property("value"));
338 sourcelast.property("value", theElement.fxSourceType.fuStopEvent || 0)
339 .on("change", () => {
340 this.markChanged("sourcelast", theIndex);
341 theElement.fxSourceType.fuStopEvent = parseInt(sourcelast.property("value"));
344 sourceskip.property("value", theElement.fxSourceType.fuEventInterval || 0)
345 .on("change", () => {
346 this.markChanged("sourceskip", theIndex);
347 theElement.fxSourceType.fuEventInterval = parseInt(sourceskip.property("value"));
350 storesel.property("value", theElement.fxStoreType.fiID).on("change", () => {
351 this.markChanged("storesel", theIndex);
352 theElement.fxStoreType.fiID = parseInt(storesel.property("value"));
353 this.showStepEditor(tab, theElement, theIndex);
356 storename.property("value", theElement.fxStoreType.fName)
357 .on("change", () => {
358 this.markChanged("storename", theIndex);
359 theElement.fxStoreType.fName = storename.property("value").trim();
362 storesplit.property("value", theElement.fxStoreType.fiSplit || 1)
363 .on("change", () => {
364 theElement.fxStoreType.fiSplit = parseInt(storesplit.property("value"));
365 this.markChanged("storesplit", theIndex);
368 storebuf.property("value", Math.round((theElement.fxStoreType.fiBufsize || 16*1024) / 1024))
369 .on("change", () => {
370 theElement.fxStoreType.fiBufsize = parseInt(storebuf.property("value")) * 1024;
371 this.markChanged("storebuf", theIndex);
374 storecomp.property("value", theElement.fxStoreType.fiCompression || 0)
375 .on("change", () => {
376 theElement.fxStoreType.fiCompression = parseInt(storecomp.property("value"));
377 this.markChanged("storecomp", theIndex);
380 storetreeasf.property("value", theElement.fxStoreType.fiAutosavesize || 0)
381 .on("change", () => {
382 theElement.fxStoreType.fiAutosavesize = parseInt(storetreeasf.property("value"));
383 this.markChanged("storeasf", theIndex);
386 storeover.property("checked", theElement.fxStoreType.fbOverwrite || true)
388 theElement.fxStoreType.fbOverwrite = storeover.property("checked");
389 this.markChanged("storeover", theIndex);
392 this.showStepEditor(tab, theElement, theIndex); // handle all visibility issues here, also refresh tabs
396 dom.select(".buttonGetAnalysis")
397 .style('background-image', "url(" + GO4.source_dir + "icons/right.png)")
399 if (JSROOT.hpainter) JSROOT.hpainter.display(this.getItemName());
402 dom.select(".buttonSetAnalysis")
403 .style('background-image', "url(" + GO4.source_dir + "icons/left.png)")
405 let options = this.evaluateChanges(""); // complete option string from all changed elements
406 console.log("submit analysis " + this.getItemName() + ", options=" + options);
407 GO4.ExecuteMethod(this, "UpdateFromUrl", options).then(() => {
408 console.log("setting analyis configuration done.");
410 if (JSROOT.hpainter && (typeof JSROOT.hpainter.reload == 'function')) JSROOT.hpainter.reload();
414 dom.select(".buttonAnaChangeLabel")
415 .style('background-image', "url(" + GO4.source_dir + "icons/info1.png)")
416 .style('display', 'none'); // do not show at the begin
418 dom.select(".buttonSetStartAnalysis")
419 .style('background-image', "url(" + GO4.source_dir + "icons/restart.png)")
421 let options = this.evaluateChanges(""); // complete option string from all changed elements
423 console.log("submit and start analysis " + this.getItemName() + ", options=" + options);
424 GO4.ExecuteMethod(this, "UpdateFromUrl", options).then(() => {
425 console.log("submit and start analyis configuration done. ");
427 if (JSROOT.hpainter && (typeof JSROOT.hpainter.reload == 'function')) JSROOT.hpainter.reload();
431 dom.select(".buttonCloseAnalysis")
432 .style('background-image', "url(" + GO4.source_dir + "icons/close.png)")
434 console.log("close analysis " + this.getItemName());
435 GO4.ExecuteMethod(this, "UpdateFromUrl", "&close").then(() => {
436 console.log("closing down analyis done. ");
440 dom.select(".buttonSaveAnaASF")
441 .style('background-image', "url(" + GO4.source_dir + "icons/filesave.png)");
443 dom.select(".anaASF_name").property("value", stat.fxAutoFileName);
444 dom.select(".anaASF_enabled")
445 .property('checked', stat.fbAutoSaveOn)
447 this.markChanged("asfenabled",0);
448 this.stat.fbAutoSaveOn = dom.select(".anaASF_enabled").property('checked');
451 dom.select(".anaASF_time")
452 .property("value", stat.fiAutoSaveInterval)
453 .on("change", () => {
454 this.markChanged("asftime", 0);
455 this.stat.fiAutoSaveInterval = dom.select(".anaASF_time").property("value");
458 dom.select(".anaASF_compression")
459 .property("value", stat.fiAutoSaveCompression)
460 .on("change", () => {
461 this.markChanged("asfcomp", 0);
462 this.stat.fiAutoSaveCompression = dom.select(".anaASF_compression").property("value");
465 dom.select(".anaASF_overwrite")
466 .property('checked', stat.fbAutoSaveOverwrite)
468 this.markChanged("asfoverwrite",0);
469 this.stat.fbAutoSaveOverwrite = dom.select(".anaASF_overwrite").property('checked');
472 ////////////////// PREFS FILE:
473 dom.select(".anaprefs_name").property("value", stat.fxConfigFileName);
475 dom.select(".anaASF_form").on("submit", event => {
476 event.preventDefault(); // do not send automatic request to server!
477 let content = dom.select(".anaASF_name").property("value");
478 content = content.trim();
479 // before we write immediately, mark name as changed in setup:
480 this.markChanged("asfname", 0);
481 this.stat.fxAutoFileName = content;
483 createJSMenu(event, this)
484 .then(menu => menu.confirm("Save auto save file", content))
485 .then(ok => (ok ? GO4.ExecuteMethod(this, "UpdateFromUrl", "&saveasf=" + content) : null))
486 .then(res => console.log(res ? "Writing autosave file done." : "Ignore or failed"));
490 dom.select(".buttonSaveAnaConf")
491 .style('background-image', "url(" + GO4.source_dir + "icons/filesave.png)")
492 .on("click", event => {
493 let content = dom.select(".anaprefs_name").property("value").trim(),
494 requestmsg = "Really save analysis preferences: " + content;
495 createJSMenu(event, this)
496 .then(menu => menu.confirm("Saving analysis preferences", requestmsg))
498 if (!ok) return null;
499 this.markChanged("anaprefsname", 0);
500 this.stat.fxConfigFileName = content;
501 return GO4.ExecuteMethod(this, "UpdateFromUrl", "&saveprefs=" + content);
502 }).then(res => console.log(res !== null ? "Loading Saving preferences done. " : "Saving preferences FAILED."));
505 dom.select(".buttonLoadAnaConf")
506 .style('background-image', "url(" + GO4.source_dir + "icons/fileopen.png)")
507 .on("click", event => {
508 let content = dom.select(".anaprefs_name").property("value").trim(),
509 requestmsg = "Really load analysis preferences: " + content;
510 createJSMenu(event, this)
511 .then(menu => menu.confirm("Loading analysis preferences", requestmsg))
512 .then(ok => (ok ? GO4.ExecuteMethod(this, "UpdateFromUrl", "&loadprefs=" + content) : null))
513 .then(res => { if ((res!==null) && JSROOT.hpainter) JSROOT.hpainter.display(this.getItemName()); });
517 redrawObject(obj /*, opt */) {
518 if (obj._typename != this.stat._typename) return false;
519 this.stat = JSROOT.clone(obj);
526 GO4.drawGo4AnalysisStatus = function(domarg, stat) {
527 let editor = new AnalysisStatusEditor(domarg, stat),
528 dom = editor.selectDom(),
529 h = dom.node().clientHeight,
530 w = dom.node().clientWidth;
532 if ((h < 10) && (w > 10)) dom.style("height", Math.round(w * 0.7)+"px");
534 return JSROOT.httpRequest(GO4.source_dir + "html/analysiseditor.htm", "text").then(code => {
537 return JSROOT.httpRequest(GO4.source_dir + "html/stepeditor.htm", "text");
539 }).then(step_code => {
541 editor.stepPageHtml = step_code;
545 editor.setTopPainter();