3import { httpRequest, BasePainter, createMenu, getHPainter, clone, d3_select } from 'jsroot';
4import { source_dir, executeMethod, GO4 } from './core.mjs';
7 GO4EV_NULL: 0, // no event store/source
8 GO4EV_FILE: 1, // root file with own tree
9 GO4EV_TREE: 2, // branch of singleton tree
10 GO4EV_MBS_FILE: 3, // mbs listmode file (input only)
11 GO4EV_MBS_STREAM: 4, // mbs stream server (input only)
12 GO4EV_MBS_TRANSPORT: 5, // mbs transport server (input only)
13 GO4EV_MBS_EVENTSERVER: 6, // mbs event server (input only)
14 GO4EV_MBS_REVSERV: 7, // remote event server (input only)
15 GO4EV_BACK: 8, // backstore in memory (pseudo-ringbuffer?)
16 GO4EV_USER: 9, // user defined source class
17 GO4EV_MBS_RANDOM: 10, // random generated mbs event
18 GO4EV_HDF5: 11 // HDF5 file format
21class AnalysisStatusEditor extends BasePainter {
22 constructor(dom, stat) {
25 this.changes = [["dummy0", "init0"],["dummy1","init1"]]; // changes array stepwise, index 0 = no step, index = stepindex+1
29 markChanged(key, step) {
30 // first avoid duplicate keys:
31 for (let index = 0; index < this.changes[step].length; index++) {
32 if (this.changes[step][index]== key) return;
34 this.changes[step].push(key);
35 console.log("Mark changed :%s at step %d", key, step);
37 this.selectDom().select(".buttonAnaChangeLabel").style('display', null); // show warning sign
40 // clear changed elements' list, make warning sign invisible
42 let numsteps = this.changes.length;
43 for (let step = 0; step < numsteps ; step++)
44 this.changes[step] = [];
45 this.selectDom().select(".buttonAnaChangeLabel").style('display', 'none'); // hide warning sign
48 /** @summary scan changed value list and return optionstring to be send to server */
49 evaluateChanges(optionstring) {
51 let numsteps = this.changes.length;
52 for (let step = 0; step < numsteps ; step++) {
53 let theElement = editor.stat.fxStepArray.arr[step],
55 len = this.changes[step].length;
57 for (let index = 0; index < len ; index++) {
59 let key = this.changes[step][index],
61 step_prefix = `&${key}_${step}=`;
63 // here mapping of key to editor field:
64 if (key == "stepenabled") {
65 stepoptions += step_prefix + theElement.fbProcessEnabled;
66 } else if (key == "sourceenabled") {
67 stepoptions += step_prefix + theElement.fbSourceEnabled;
68 } else if (key == "storeenabled") {
69 stepoptions += step_prefix + theElement.fbStoreEnabled;
70 } else if (key == "sourcesel") {
71 stepoptions += step_prefix + theElement.fxSourceType.fiID;
72 } else if (key == "sourcename") {
73 stepoptions += step_prefix + theElement.fxSourceType.fName;
74 } else if (key == "sourcetag") {
75 stepoptions += step_prefix + theElement.fxSourceType.fxTagFile;
76 } else if (key == "sourceport") {
77 stepoptions += step_prefix + theElement.fxSourceType.fiPort;
78 } else if (key == "sourcetmout") {
79 stepoptions += step_prefix + theElement.fxSourceType.fiTimeout;
80 } else if (key == "sourceretry") {
81 stepoptions += step_prefix + theElement.fxSourceType.fiRetryCnt;
82 } else if (key == "sourcefirst") {
83 stepoptions += step_prefix + theElement.fxSourceType.fuStartEvent;
84 } else if (key == "sourcelast") {
85 stepoptions += step_prefix + theElement.fxSourceType.fuStopEvent;
86 } else if (key == "sourceskip") {
87 stepoptions += step_prefix + theElement.fxSourceType.fuEventInterval;
88 } else if (key == "storesel") {
89 stepoptions += step_prefix + theElement.fxStoreType.fiID;
90 } else if (key == "storename") {
91 stepoptions += step_prefix + theElement.fxStoreType.fName;
92 } else if (key == "storesplit") {
93 stepoptions += step_prefix + theElement.fxStoreType.fiSplit;
94 } else if (key == "storebuf") {
95 stepoptions += step_prefix + theElement.fxStoreType.fiBufsize;
96 } else if (key == "storecomp") {
97 stepoptions += step_prefix + theElement.fxStoreType.fiCompression;
98 } else if (key == "storeasf") {
99 stepoptions += step_prefix + theElement.fxStoreType.fiAutosavesize;
100 } else if (key == "storeover") {
101 stepoptions += step_prefix + theElement.fxStoreType.fbOverwrite;
103 // non step specific options are in step 0 options too:
104 else if (key == "asfname") {
105 stepoptions += preifx + editor.stat.fxAutoFileName;
106 } else if (key == "asfenabled") {
107 stepoptions += preifx + editor.stat.fbAutoSaveOn;
108 } else if (key == "asftime") {
109 stepoptions += preifx + editor.stat.fiAutoSaveInterval;
110 } else if (key == "asfcomp") {
111 stepoptions += preifx + editor.stat.fiAutoSaveCompression;
112 } else if (key == "asfoverwrite") {
113 stepoptions += preifx + editor.stat.fbAutoSaveOverwrite;
114 } else if (key == "anaprefsname") {
115 stepoptions += preifx + editor.stat.fxConfigFileName;
117 console.log(`Warning: evaluateChanges found unknown key: ${key}`);
122 optionstring += stepoptions;
124 console.log("Resulting option string:%s", optionstring);
128 showStepEditor(tab, theElement, theIndex) {
130 let sourcebox = tab.select(".step_box_source_enab"),
131 storebox = tab.select(".step_box_store_enab"),
132 step_source = tab.select(".step_source"),
133 step_store = tab.select(".step_store"),
134 sourcemore = tab.select(".step_source_expand");
136 // here step control checkboxes and source/store visibility:
137 if (theElement.fbProcessEnabled) {
138 sourcebox.attr('disabled', null);
139 storebox.attr('disabled', null);
140 step_source.attr('disabled', theElement.fbSourceEnabled ? null : "true");
141 step_store.style('display', theElement.fbStoreEnabled ? null : "none");
143 sourcebox.attr('disabled', "true");
144 storebox.attr('disabled', "true");
145 step_source.attr('disabled', "true");
146 step_store.style('display', "none");
149 let show_tag = false, show_ports = false, show_args = false, show_more = sourcemore.property("checked");
151 switch (theElement.fxSourceType.fiID) {
152 case EvIOType.GO4EV_MBS_FILE:
153 show_tag = show_args = show_more; break;
154 case EvIOType.GO4EV_MBS_STREAM:
155 case EvIOType.GO4EV_MBS_TRANSPORT:
156 case EvIOType.GO4EV_MBS_EVENTSERVER:
157 case EvIOType.GO4EV_MBS_REVSERV:
158 show_ports = show_args = show_more; break;
159 case EvIOType.GO4EV_USER:
160 show_ports = show_more; break;
161 case EvIOType.GO4EV_FILE:
162 case EvIOType.GO4EV_MBS_RANDOM:
164 // show no extra parameters
168 tab.select(".step_source_tagfile_args").style('display', show_tag ? null : 'none');
169 tab.select(".step_source_port_args").style('display', show_ports ? null : 'none');
170 tab.select(".step_source_number_args").style('display', show_args ? 'inline' : 'none');
172 let enbale_store_pars = false, enbale_store_name = true;
173 switch (theElement.fxStoreType.fiID) {
174 case EvIOType.GO4EV_FILE:
175 enbale_store_pars = true;
177 case EvIOType.GO4EV_BACK:
178 enbale_store_pars = true;
179 enbale_store_name = false;
181 case EvIOType.GO4EV_USER:
187 tab.select(".step_store_pars").selectAll("input").each(function() {
188 d3_select(this).attr("disabled", enbale_store_pars ? null : "true");
191 tab.select(".step_store_name").attr("disabled", enbale_store_name ? null : "true");
195 let dom = this.selectDom(),
198 let head_html = "", step_html = "";
199 stat.fxStepArray.arr.forEach((step, indx) => {
200 head_html += `<button for="go4_analysis_step_${indx}">${step.fName}</button>`;
201 step_html += `<div class="go4_analysis_step_${indx}" style="display:none">${this.stepPageHtml}</div>`;
204 dom.select(".ana_step_tabs_header").html(head_html);
206 dom.select(".ana_step_tabs_body").html(step_html);
208 // assign tabs buttons handlers
209 dom.select('.ana_step_tabs_header').selectAll("button").on("click", function() {
210 let btn = d3_select(this);
212 dom.select('.ana_step_tabs_header').selectAll("button").each(function() {
213 d3_select(this).classed("active_btn", false);
216 btn.classed("active_btn", true);
218 dom.selectAll('.ana_step_tabs_body>div').each(function() {
219 let tab = d3_select(this);
220 tab.style('display', tab.classed(btn.attr("for")) ? null : "none");
224 // activate first step
225 dom.select(".ana_step_tabs_body").select(".go4_analysis_step_0").style('display', null);
226 dom.select('.ana_step_tabs_header').select("button").classed("active_btn", true);
228 stat.fxStepArray.arr.forEach((theElement, theIndex) => {
229 let tab = dom.select(".ana_step_tabs_body").select(`.go4_analysis_step_${theIndex}`);
231 let enablebox = tab.select(".step_box_step_enab"),
232 sourcebox = tab.select(".step_box_source_enab"),
233 storebox = tab.select(".step_box_store_enab"),
234 sourcesel = tab.select(".step_source_select"),
235 sourcemore = tab.select(".step_source_expand"),
236 sourcename = tab.select(".step_source_name"),
237 sourcetag = tab.select(".step_source_tagfile"),
238 sourceport = tab.select(".step_source_port"),
239 sourcetmout = tab.select(".step_source_tmout"),
240 sourceretry = tab.select(".step_source_retry"),
241 sourcefirst = tab.select(".step_source_firstev"),
242 sourcelast = tab.select(".step_source_lastev"),
243 sourceskip = tab.select(".step_source_stepev"),
244 storesel = tab.select(".step_store_select"),
245 storename = tab.select(".step_store_name"),
246 storesplit = tab.select(".step_store_split"),
247 storebuf = tab.select(".step_store_buf"),
248 storecomp = tab.select(".step_store_comp"),
249 storetreeasf = tab.select(".step_store_asf"),
250 storeover = tab.select(" .step_store_overwrite");
252 enablebox.property('checked', theElement.fbProcessEnabled)
254 this.markChanged("stepenabled", theIndex);
255 theElement.fbProcessEnabled = enablebox.property('checked');
256 this.showStepEditor(tab, theElement, theIndex);
259 sourcebox.property('checked', theElement.fbSourceEnabled)
261 this.markChanged("sourceenabled", theIndex);
262 theElement.fbSourceEnabled = sourcebox.property('checked');
263 this.showStepEditor(tab, theElement, theIndex);
266 storebox.property('checked', theElement.fbStoreEnabled)
268 this.markChanged("storeenabled", theIndex);
269 theElement.fbStoreEnabled = storebox.property('checked');
270 this.showStepEditor(tab, theElement, theIndex);
273 //// EVENT SOURCE: /////////////////////////////////////////////////
274 sourcesel.property("value", theElement.fxSourceType.fiID).on("change", () => {
275 this.markChanged("sourcesel", theIndex);
277 theElement.fxSourceType.fiID = parseInt(sourcesel.property("value"));
279 this.showStepEditor(tab, theElement, theIndex);
280 }); // source selectmenu change
282 sourcemore.on("click", () => {
283 this.showStepEditor(tab, theElement, theIndex);
286 sourcename.property("value", theElement.fxSourceType.fName)
287 .on("change", () => {
288 this.markChanged("sourcename", theIndex);
289 theElement.fxSourceType.fName = sourcename.property("value").trim();
292 sourcetag.property("value", theElement.fxSourceType.fxTagFile || "")
293 .on("change", () => {
294 this.markChanged("sourcetag", theIndex);
295 theElement.fxSourceType.fxTagFile = sourcetag.property("value").trim();
298 sourceport.property("value", theElement.fxSourceType.fiPort || 0)
299 .on("change", () => {
300 this.markChanged("sourceport", theIndex);
301 theElement.fxSourceType.fiPort = parseInt(sourceport.property("value"));
304 sourcetmout.property("value", theElement.fxSourceType.fiTimeout || 100)
305 .on("change", () => {
306 this.markChanged("sourcetmout", theIndex);
307 theElement.fxSourceType.fiTimeout = parseInt(sourcetmout.property("value"));
310 sourceretry.property("value", theElement.fxSourceType.fiRetryCnt || 0)
311 .on("change", () => {
312 this.markChanged("sourceretry", theIndex);
313 theElement.fxSourceType.fiRetryCnt = parseInt(sourceretry.property("value"));
316 sourcefirst.property("value", theElement.fxSourceType.fuStartEvent || 0)
317 .on("change", () => {
318 this.markChanged("sourcefirst", theIndex);
319 theElement.fxSourceType.fuStartEvent = parseInt(sourcefirst.property("value"));
322 sourcelast.property("value", theElement.fxSourceType.fuStopEvent || 0)
323 .on("change", () => {
324 this.markChanged("sourcelast", theIndex);
325 theElement.fxSourceType.fuStopEvent = parseInt(sourcelast.property("value"));
328 sourceskip.property("value", theElement.fxSourceType.fuEventInterval || 0)
329 .on("change", () => {
330 this.markChanged("sourceskip", theIndex);
331 theElement.fxSourceType.fuEventInterval = parseInt(sourceskip.property("value"));
334 storesel.property("value", theElement.fxStoreType.fiID).on("change", () => {
335 this.markChanged("storesel", theIndex);
336 theElement.fxStoreType.fiID = parseInt(storesel.property("value"));
337 this.showStepEditor(tab, theElement, theIndex);
340 storename.property("value", theElement.fxStoreType.fName)
341 .on("change", () => {
342 this.markChanged("storename", theIndex);
343 theElement.fxStoreType.fName = storename.property("value").trim();
346 storesplit.property("value", theElement.fxStoreType.fiSplit || 1)
347 .on("change", () => {
348 theElement.fxStoreType.fiSplit = parseInt(storesplit.property("value"));
349 this.markChanged("storesplit", theIndex);
352 storebuf.property("value", Math.round((theElement.fxStoreType.fiBufsize || 16*1024) / 1024))
353 .on("change", () => {
354 theElement.fxStoreType.fiBufsize = parseInt(storebuf.property("value")) * 1024;
355 this.markChanged("storebuf", theIndex);
358 storecomp.property("value", theElement.fxStoreType.fiCompression || 0)
359 .on("change", () => {
360 theElement.fxStoreType.fiCompression = parseInt(storecomp.property("value"));
361 this.markChanged("storecomp", theIndex);
364 storetreeasf.property("value", theElement.fxStoreType.fiAutosavesize || 0)
365 .on("change", () => {
366 theElement.fxStoreType.fiAutosavesize = parseInt(storetreeasf.property("value"));
367 this.markChanged("storeasf", theIndex);
370 storeover.property("checked", theElement.fxStoreType.fbOverwrite || true)
372 theElement.fxStoreType.fbOverwrite = storeover.property("checked");
373 this.markChanged("storeover", theIndex);
376 this.showStepEditor(tab, theElement, theIndex); // handle all visibility issues here, also refresh tabs
380 dom.select(".buttonGetAnalysis")
381 .style('background-image', `url(${source_dir}icons/right.png)`)
383 getHPainter()?.display(this.getItemName());
386 dom.select(".buttonSetAnalysis")
387 .style('background-image', `url(${source_dir}icons/left.png)`)
389 let options = this.evaluateChanges(""); // complete option string from all changed elements
390 console.log("submit analysis " + this.getItemName() + ", options=" + options);
391 executeMethod(this, "UpdateFromUrl", options).then(() => {
392 console.log("setting analyis configuration done.");
394 getHPainter()?.reload();
398 dom.select(".buttonAnaChangeLabel")
399 .style('background-image', `url(${source_dir}icons/info1.png)`)
400 .style('display', 'none'); // do not show at the begin
402 dom.select(".buttonSetStartAnalysis")
403 .style('background-image', `url(${source_dir}icons/restart.png)`)
405 let options = this.evaluateChanges(""); // complete option string from all changed elements
407 console.log("submit and start analysis " + this.getItemName() + ", options=" + options);
408 executeMethod(this, "UpdateFromUrl", options).then(() => {
409 console.log("submit and start analyis configuration done. ");
411 getHPainter()?.reload();
415 dom.select(".buttonCloseAnalysis")
416 .style('background-image', `url(${source_dir}icons/close.png)`)
418 console.log("close analysis " + this.getItemName());
419 executeMethod(this, "UpdateFromUrl", "&close").then(() => {
420 console.log("closing down analyis done. ");
424 dom.select(".buttonSaveAnaASF")
425 .style('background-image', `url(${source_dir}icons/filesave.png)`);
427 dom.select(".anaASF_name").property("value", stat.fxAutoFileName);
428 dom.select(".anaASF_enabled")
429 .property('checked', stat.fbAutoSaveOn)
431 this.markChanged("asfenabled",0);
432 this.stat.fbAutoSaveOn = dom.select(".anaASF_enabled").property('checked');
435 dom.select(".anaASF_time")
436 .property("value", stat.fiAutoSaveInterval)
437 .on("change", () => {
438 this.markChanged("asftime", 0);
439 this.stat.fiAutoSaveInterval = dom.select(".anaASF_time").property("value");
442 dom.select(".anaASF_compression")
443 .property("value", stat.fiAutoSaveCompression)
444 .on("change", () => {
445 this.markChanged("asfcomp", 0);
446 this.stat.fiAutoSaveCompression = dom.select(".anaASF_compression").property("value");
449 dom.select(".anaASF_overwrite")
450 .property('checked', stat.fbAutoSaveOverwrite)
452 this.markChanged("asfoverwrite",0);
453 this.stat.fbAutoSaveOverwrite = dom.select(".anaASF_overwrite").property('checked');
456 ////////////////// PREFS FILE:
457 dom.select(".anaprefs_name").property("value", stat.fxConfigFileName);
459 dom.select(".anaASF_form").on("submit", event => {
460 event.preventDefault(); // do not send automatic request to server!
461 let content = dom.select(".anaASF_name").property("value");
462 content = content.trim();
463 // before we write immediately, mark name as changed in setup:
464 this.markChanged("asfname", 0);
465 this.stat.fxAutoFileName = content;
467 createMenu(event, this)
468 .then(menu => menu.confirm("Save auto save file", content))
469 .then(ok => (ok ? executeMethod(this, "UpdateFromUrl", "&saveasf=" + content) : null))
470 .then(res => console.log(res ? "Writing autosave file done." : "Ignore or failed"));
474 dom.select(".buttonSaveAnaConf")
475 .style('background-image', `url(${source_dir}icons/filesave.png)`)
476 .on("click", event => {
477 let content = dom.select(".anaprefs_name").property("value").trim(),
478 requestmsg = "Really save analysis preferences: " + content;
479 createMenu(event, this)
480 .then(menu => menu.confirm("Saving analysis preferences", requestmsg))
482 if (!ok) return null;
483 this.markChanged("anaprefsname", 0);
484 this.stat.fxConfigFileName = content;
485 return executeMethod(this, "UpdateFromUrl", "&saveprefs=" + content);
486 }).then(res => console.log(res !== null ? "Loading Saving preferences done. " : "Saving preferences FAILED."));
489 dom.select(".buttonLoadAnaConf")
490 .style('background-image', `url(${source_dir}icons/fileopen.png)`)
491 .on("click", event => {
492 let content = dom.select(".anaprefs_name").property("value").trim(),
493 requestmsg = "Really load analysis preferences: " + content;
494 createMenu(event, this)
495 .then(menu => menu.confirm("Loading analysis preferences", requestmsg))
496 .then(ok => (ok ? executeMethod(this, "UpdateFromUrl", "&loadprefs=" + content) : null))
497 .then(res => { if (res !== null) getHPainter()?.display(this.getItemName()); });
501 redrawObject(obj /*, opt */) {
502 if (obj._typename != this.stat._typename) return false;
503 this.stat = clone(obj);
509 static async draw(domarg, stat) {
510 let editor = new AnalysisStatusEditor(domarg, stat),
511 dom = editor.selectDom(),
512 h = dom.node().clientHeight,
513 w = dom.node().clientWidth;
515 if ((h < 10) && (w > 10)) dom.style("height", Math.round(w * 0.7)+"px");
517 return httpRequest(`${source_dir}html5/analysiseditor.htm`, 'text').then(code => {
520 return httpRequest(`${source_dir}html5/stepeditor.html`, 'text');
522 }).then(step_code => {
524 editor.stepPageHtml = step_code;
528 editor.setTopPainter();
534export { AnalysisStatusEditor };