File indexing completed on 2024-11-25 02:29:37
0001 const kBoxHeight = 15;
0002 const kRowHeight = 20;
0003
0004 const kTypeOffset = 16
0005 const kSourceColor = "#DD00DD";
0006 const typeToColor = [
0007 "#000000",
0008 0,
0009 0,
0010 0,
0011 "#FF0000",
0012 "#FF7777",
0013 "#BBBBBB",
0014 "#FFFFFF",
0015 0,
0016 "#0000BB",
0017 "#0000FF",
0018 "#0000FF",
0019 "#00BB00",
0020 "#00FF00",
0021 "#00FF00",
0022 "#CCCC00",
0023 "#FFFF00",
0024 0,
0025 "#007700",
0026 "#007700",
0027 0,
0028 "#000077",
0029 "#000077",
0030 0,
0031 "#BBBBBB",
0032 "#FFFFFF",
0033 kSourceColor,
0034 "#FF7777",
0035 "#FF0000",
0036 "#00FFFF",
0037 "#007777",
0038 kSourceColor,
0039 "#DD00DD",
0040 "#FF0000",
0041 ]
0042
0043 const overlappingTransitions =[15]
0044
0045 const typeToName = [
0046 "destructor",
0047 0,
0048 0,
0049 0,
0050 "end job",
0051 "end stream",
0052 "write process block",
0053 "end process block",
0054 0,
0055 "write end run",
0056 "global end run",
0057 "stream end run",
0058 "write end lumi",
0059 "global end lumi",
0060 "stream end lumi",
0061 "clear event",
0062 "event",
0063 0,
0064 "stream begin lumi",
0065 "global begin lumi",
0066 0,
0067 "stream begin run",
0068 "global begin run",
0069 0,
0070 "access input process block",
0071 "begin process block",
0072 "open file",
0073 "begin stream",
0074 "begin job",
0075 "EventSetup synch",
0076 "EventSetup sych enqueue",
0077 "find next transition",
0078 "construction",
0079 "startup",
0080 ]
0081
0082 const activityToName = [ "prefetch",
0083 "acquire",
0084 "process",
0085 "delayedGet",
0086 "externalWork"]
0087
0088 const activityToColor = ["#FF5F1F", "#CC7722", null, "#FF4433", "#8B4513"];
0089
0090
0091
0092 window.onload = async() => {
0093
0094 const jsonDropdown = document.getElementById('jsonDropdown');
0095
0096 async function fetchData(file) {
0097 showLoadingSpinner();
0098 try {
0099 const response = await fetch(file);
0100 const data = await response.json();
0101 processData(data);
0102 } catch (error) {
0103 console.error('Error fetching data:', error);
0104 }finally {
0105 hideLoadingSpinner();
0106 }
0107 }
0108
0109
0110 function processData(data) {
0111 const left = document.querySelector('.name_div');
0112 const div = document.querySelector('.graph_div');
0113 const bottom = document.querySelector('.time_div');
0114 const graph = document.getElementById('graph_view');
0115 const graph_context = graph.getContext('2d');
0116 const name_view = document.getElementById('name_view');
0117 const name_context = name_view.getContext('2d');
0118 const time_view = document.getElementById('time_view');
0119 const time_context = time_view.getContext('2d');
0120 const selected_view = document.getElementById('selected_paragraph');
0121 const zoom_in_button = document.getElementById('zoom_in');
0122 const zoom_out_button = document.getElementById('zoom_out');
0123 let selected_item = null;
0124 selected_view.innerHTML = "Selected: [click on box in graph]";
0125 let mouse_is_down = false;
0126
0127 let rows = 0;
0128 for( let grouping of data.transitions ) {
0129 for( let slot in grouping.slots ) {
0130 ++rows;
0131 }
0132 }
0133
0134 const max_graph_height = kRowHeight*rows;
0135
0136
0137 let graph_vertical_offset = 0.;
0138 let minVisibleTime = 0.;
0139 let timeZoomFactor = 1.0;
0140
0141 function maxTime() {
0142 let maxTime = 0;
0143 for( let grouping of data.transitions) {
0144 for( let slot of grouping.slots) {
0145 for (let transition of slot) {
0146 if (maxTime < transition.finish) {
0147 maxTime = transition.finish;
0148 }
0149 }
0150 }
0151 }
0152
0153 maxTime = Math.ceil(maxTime);
0154
0155 const digits = maxTime.toString().length;
0156 const digitsToZeroOut = digits -2;
0157 if (digitsToZeroOut > 0) {
0158 for(let i = 0; i < digitsToZeroOut; ++i) {
0159 maxTime /= 10;
0160 }
0161 maxTime *=10;
0162 maxTime +=9;
0163 for( let i = 1; i < digitsToZeroOut; ++i) {
0164 maxTime *=10;
0165 }
0166 maxTime = Math.ceil(maxTime);
0167 }
0168 return maxTime;
0169 }
0170
0171 const kEndTime = maxTime();
0172
0173 function drawNames() {
0174 name_context.setTransform(1,0,0,1,0,0);
0175 name_context.fillStyle = "#AACD6E"
0176 name_context.fillRect(0,0,name_view.width,name_view.height);
0177 name_context.scale(1,1)
0178 name_context.fillStyle = "black"
0179 name_context.strokeStyle = "black"
0180 name_context.font = '9pt monospace';
0181
0182 let offset = kRowHeight/2 + graph_vertical_offset;
0183 for( let grouping of data.transitions ) {
0184 name_context.fillText(grouping.name, 0, offset);
0185 offset += grouping.slots.length*kRowHeight;
0186 }
0187 }
0188
0189 function timeStepPower() {
0190 let timeDecade = 0;
0191 let nSteps = time_view.width/kInitPixelsPerSecond/timeZoomFactor;
0192 while (nSteps < 3) {
0193 --timeDecade;
0194 nSteps *=10.;
0195 }
0196 while (nSteps > 20) {
0197 ++timeDecade;
0198 nSteps /=10.;
0199 }
0200 return timeDecade;
0201 }
0202
0203 function drawTime() {
0204 time_context.save()
0205 time_context.setTransform(1,0,0,1,0,0);
0206 time_context.fillStyle = "#CD6E6E";
0207 time_context.fillRect(0,0,time_view.width,time_view.height);
0208 time_context.restore();
0209 time_context.scale(1,1);
0210 const end = kEndTime;
0211 const timePower = timeStepPower();
0212 const stepSize = Math.pow(10., timePower);
0213 let fixedValue = timePower;
0214 if (fixedValue < 0 ) {
0215 fixedValue *= -1;
0216 }
0217
0218 const tickDistance = stepSize/10.*kInitPixelsPerSecond*timeZoomFactor;
0219 const bigTickDistance = stepSize*kInitPixelsPerSecond*timeZoomFactor;
0220 for( let bigTick = 0; bigTick < end; bigTick +=stepSize) {
0221 let t = (bigTick-minVisibleTime)*kInitPixelsPerSecond*timeZoomFactor;
0222 if ((t + bigTickDistance) < 0 ) continue;
0223 if (t > time_view.width) break;
0224 graph_context.strokeStyle = "black";
0225 time_context.beginPath()
0226 time_context.moveTo(t,0)
0227 time_context.lineTo(t, time_view.height/2);
0228 time_context.stroke()
0229 const sec = bigTick.toFixed(fixedValue);
0230
0231 time_context.fillText(sec.toString()+"s", t, time_view.height*0.75)
0232 for(let tick = 1; tick < 10; ++tick) {
0233 let pos = t+tick*tickDistance;
0234 time_context.beginPath();
0235 time_context.moveTo(pos,0);
0236 time_context.lineTo(pos, time_view.height/4);
0237 time_context.stroke();
0238 }
0239 }
0240 }
0241
0242 function colorToUse(transition) {
0243 if (!transition.isSrc) {
0244 if ( ('act' in transition) && transition.act != 2) {
0245 return activityToColor[transition.act];
0246 } else {
0247 return typeToColor[transition.type+kTypeOffset];
0248 }
0249 }
0250 return kSourceColor;
0251 }
0252
0253 function drawGraph() {
0254 const scale = kInitPixelsPerSecond * timeZoomFactor;
0255 const maxVisibleTime = time_view.width/scale+minVisibleTime;
0256
0257 graph_context.save();
0258 graph_context.setTransform(1,0,0,1,0,0);
0259 graph_context.fillStyle = "#D5D6C6"
0260 graph_context.fillRect(0,0,graph.width,graph.height);
0261 graph_context.restore();
0262
0263 graph_context.strokeStyle = "black";
0264 graph_context.scale(1,1);
0265
0266 let offset = graph_vertical_offset;
0267 for( let grouping of data.transitions) {
0268 if (offset > graph.height) break;
0269 for( let slot of grouping.slots ) {
0270 if (offset > graph.height) break;
0271 if (offset+kBoxHeight >= 0) {
0272 for( let transition of slot) {
0273 if (maxVisibleTime < transition.start) {
0274 break;
0275 }
0276 if (minVisibleTime > transition.finish) {
0277 continue;
0278 }
0279 if(transition == selected_item) {
0280 graph_context.fillStyle = "white";
0281 } else {
0282 graph_context.fillStyle = colorToUse(transition);
0283 }
0284 graph_context.fillRect(scale*(transition.start-minVisibleTime), offset, scale*(transition.finish-transition.start), kBoxHeight);
0285 }
0286 }
0287 offset += kRowHeight;
0288 }
0289 }
0290 drawNames();
0291 drawTime();
0292
0293 }
0294 graph.width = div.clientWidth;
0295 const kInitPixelsPerSecond = graph.width/kEndTime;
0296 const max_graph_width = graph.width;
0297
0298 graph.height = div.clientHeight
0299 name_view.width = left.clientWidth
0300 name_view.height = left.clientHeight
0301 time_view.width = bottom.clientWidth
0302 time_view.height = bottom.clientHeight
0303 drawGraph()
0304
0305 let graph_isDragging = false;
0306 let graph_mouseDown = false;
0307 let graph_mouseDownPosition = {x:0, y:0};
0308 let graph_dragStartPosition = { x: 0, y: 0 };
0309
0310 let time_isDragging = false;
0311 let time_dragStartPosition = 0;
0312
0313 function graph_translate(xDiff, yDiff) {
0314 let original = minVisibleTime;
0315 minVisibleTime -= xDiff/kInitPixelsPerSecond/timeZoomFactor;
0316
0317 const timeEnd = (max_graph_width)/kInitPixelsPerSecond/timeZoomFactor+minVisibleTime;
0318 if (timeEnd > kEndTime) {
0319 minVisibleTime = kEndTime - max_graph_width/kInitPixelsPerSecond/timeZoomFactor;
0320 }
0321 if (minVisibleTime < 0) {
0322 minVisibleTime = 0;
0323 }
0324 original = graph_vertical_offset;
0325 graph_vertical_offset += yDiff;
0326 if (graph_vertical_offset < -max_graph_height) {
0327 graph_vertical_offset = -max_graph_height;
0328 }
0329 if (graph_vertical_offset > 0) {
0330 graph_vertical_offset = 0;
0331 }
0332 }
0333
0334 function getTransformedPoint(x, y) {
0335 const originalPoint = new DOMPoint(x, y);
0336 return graph_context.getTransform().invertSelf().transformPoint(originalPoint);
0337 }
0338
0339 function graph_onMouseDown(event) {
0340 graph_mouseDown = true;
0341 graph_mouseDownPosition = {x:event.offsetX, y:event.offsetY};
0342 graph_dragStartPosition = getTransformedPoint(event.offsetX, event.offsetY);
0343 }
0344
0345 function graph_onMouseMove(event) {
0346 let currentTransformedCursor = getTransformedPoint(event.offsetX, event.offsetY);
0347
0348 if (graph_mouseDown) {
0349 if (Math.abs(graph_mouseDownPosition.x-event.offsetX)> 5 ||
0350 Math.abs(graph_mouseDownPosition.y-event.offsetY)> 5) {
0351 graph_isDragging = true;
0352 }
0353 }
0354 if (graph_isDragging) {
0355 graph_translate(currentTransformedCursor.x - graph_dragStartPosition.x, currentTransformedCursor.y - graph_dragStartPosition.y);
0356 graph_dragStartPosition.x = currentTransformedCursor.x;
0357 graph_dragStartPosition.y = currentTransformedCursor.y;
0358 drawGraph();
0359 }
0360 }
0361
0362 function moduleName(id) {
0363 if (id ==0) {
0364 return "source";
0365 }
0366 return "";
0367 }
0368
0369 function doUnselection() {
0370 selected_view.innerHTML = "Selected: [click on box in graph]";
0371 selected_item = null;
0372 }
0373
0374 function moduleIdToName(id) {
0375 if (id < 0) {
0376 return data.esModules[-1*id];
0377 }
0378 return data.modules[id];
0379 }
0380
0381 function duration(t) {
0382 if (t < 0.001) {
0383 return (t*1000000).toFixed()+"us";
0384 }
0385 if (t < 0.1) {
0386 return (t*1000).toFixed(3)+"ms";
0387 }
0388 return t.toFixed(6)+"s";
0389 }
0390
0391 function updateSelectedView(item) {
0392 if ('isSrc' in item) {
0393 if (item.isSrc) {
0394 if(item.mod) {
0395 selected_view.innerHTML ="Selected: source reading data product: for module "+item.mod+" "+moduleIdToName(item.mod)+ " while "+activityToName[item.act]+" "+typeToName[item.type+kTypeOffset] +" start: "+item.start.toFixed(6)+"s finish: "+item.finish.toFixed(6)+"s duration: "+duration(item.finish-item.start);
0396
0397 } else {
0398 selected_view.innerHTML = "Selected: source "+typeToName[item.type+kTypeOffset]+" id: "+item.id+" run: "+item.sync[0]
0399 +" lumi: "+item.sync[1]+ " event: "+item.sync[2]+" start: "+item.start.toFixed(6)+"s finish:"+item.finish.toFixed(6)+"s duration: "+duration(item.finish-item.start);
0400 }
0401 } else {
0402 selected_view.innerHTML = "Selected: "+typeToName[item.type+kTypeOffset]+" id: "+item.id+" run: "+item.sync[0]
0403 +" lumi: "+item.sync[1]+ " event: "+item.sync[2]+" start: "+item.start.toFixed(6)+"s finish:"+item.finish.toFixed(6)+"s duration: "+duration(item.finish-item.start);
0404 }
0405 } else {
0406 let transform = '';
0407 if (item.call != 0 && item.mod > 0) {
0408 transform = ' transform '
0409 }
0410 selected_view.innerHTML ="Selected: module : "+item.mod+" "+moduleIdToName(item.mod)+ " while "+activityToName[item.act]+transform+" "+typeToName[item.type+kTypeOffset] +" start: "+item.start.toFixed(6)+"s finish: "+item.finish.toFixed(6)+"s duration: "+duration(item.finish-item.start);
0411 }
0412 }
0413 function doSelection(items, x) {
0414 let time = x/kInitPixelsPerSecond/timeZoomFactor+minVisibleTime;
0415
0416 if( time < 0 || time > kEndTime) {
0417 doUnselection();
0418 drawGraph();
0419 return;
0420 }
0421 selected_item = null;
0422 for( let item of items) {
0423 if (time < item.start) {
0424 break;
0425 }
0426 if (time > item.start && time < item.finish) {
0427 selected_item = item;
0428 if ( overlappingTransitions.includes(selected_item.type)) {
0429 continue;
0430 } else {
0431 break;
0432 }
0433 }
0434 }
0435 if (selected_item) {
0436 updateSelectedView(selected_item);
0437 } else {
0438 doUnselection();
0439 }
0440 drawGraph();
0441 }
0442
0443 function graph_onMouseUp(event) {
0444 if (graph_mouseDown && ! graph_isDragging) {
0445
0446 const selectionPoint = getTransformedPoint(event.offsetX, event.offsetY);
0447 const vertIndex = Math.floor((selectionPoint.y-graph_vertical_offset)/kRowHeight);
0448
0449 let presentIndex = 0;
0450 let container = null;
0451 outer: for(let grouping of data.transitions) {
0452 for(let slot of grouping.slots) {
0453 if (presentIndex == vertIndex) {
0454 container = slot;
0455 break outer;
0456 }
0457 ++presentIndex;
0458 }
0459 }
0460 if (!container) {
0461 doUnselection();
0462 drawGraph();
0463 } else {
0464 doSelection(container, selectionPoint.x);
0465 }
0466 }
0467 graph_isDragging = false;
0468 graph_mouseDown = false;
0469 }
0470
0471 function graph_onMouseOut() {
0472 graph_isDragging = false
0473 graph_mouseDown = false;
0474 }
0475 function graph_onWheel(event) {
0476 if (event.ctrlKey) {
0477 let currentTransformedCursor = getTransformedPoint(event.offsetX, event.offsetY);
0478 const zoom = event.deltaY < 0 ? 1.02 : 0.98;
0479 const originalScale = 1./timeZoomFactor/kInitPixelsPerSecond;
0480 timeZoomFactor *= zoom;
0481 const newScale = 1./timeZoomFactor/kInitPixelsPerSecond;
0482
0483 minVisibleTime = minVisibleTime + currentTransformedCursor.x*(originalScale-newScale);
0484 } else {
0485 graph_translate(-1*event.deltaX, -1*event.deltaY);
0486 }
0487 drawGraph();
0488 event.preventDefault()
0489 }
0490
0491 window.addEventListener('resize',function(){
0492 graph_context.canvas.width = graph.clientWidth;
0493 graph_context.canvas.height = graph.clientHeight;
0494 name_context.canvas.width = name_view.clientWidth;
0495 name_context.canvas.height = name_view.clientHeight;
0496 time_context.canvas.width = time_view.clientWidth;
0497 time_context.canvas.height = time_view.clientHeight;
0498 drawGraph();
0499 }, false);
0500 graph.addEventListener('mousedown', graph_onMouseDown)
0501 graph.addEventListener('mousemove', graph_onMouseMove)
0502 graph.addEventListener('mouseup', graph_onMouseUp)
0503 graph.addEventListener('mouseout', graph_onMouseOut)
0504 graph.addEventListener('wheel', graph_onWheel);
0505
0506 function time_onMouseDown(event) {
0507 time_isDragging = true;
0508 time_dragStartPosition = getTransformedPoint(event.offsetX, 0).x;
0509 }
0510
0511 function time_onMouseMove(event) {
0512 let currentTransformedCursor = getTransformedPoint(event.offsetX, 0);
0513
0514 if (time_isDragging) {
0515 graph_translate(currentTransformedCursor.x - time_dragStartPosition, 0);
0516 time_dragStartPosition = currentTransformedCursor.x;
0517 drawGraph();
0518 }
0519 }
0520
0521 function time_onMouseUp() {
0522 time_isDragging = false;
0523 }
0524
0525 function time_onMouseOut() {
0526 time_isDragging = false
0527 }
0528
0529 function time_onWheel(event) {
0530 if (event.ctrlKey) {
0531 let currentTransformedCursor = getTransformedPoint(event.offsetX, event.offsetY);
0532 const zoom = event.deltaY < 0 ? 1.02 : 0.98;
0533 const originalScale = 1./timeZoomFactor/kInitPixelsPerSecond;
0534 timeZoomFactor *= zoom;
0535 const newScale = 1./timeZoomFactor/kInitPixelsPerSecond;
0536
0537 minVisibleTime = minVisibleTime + currentTransformedCursor.x*(originalScale-newScale);
0538 } else {
0539 graph_translate(-1*event.deltaX, 0);
0540 }
0541
0542 drawGraph();
0543 event.preventDefault();
0544
0545 }
0546
0547 time_view.addEventListener('mousedown', time_onMouseDown)
0548 time_view.addEventListener('mousemove', time_onMouseMove)
0549 time_view.addEventListener('mouseup', time_onMouseUp)
0550 time_view.addEventListener('mouseout', time_onMouseOut)
0551 time_view.addEventListener('wheel', time_onWheel);
0552
0553 function zoom_in_click(event) {
0554 const zoom = 1.1;
0555 const originalScale = 1. / timeZoomFactor / kInitPixelsPerSecond;
0556 timeZoomFactor *= zoom;
0557 const newScale = 1. / timeZoomFactor / kInitPixelsPerSecond;
0558
0559 minVisibleTime = minVisibleTime + max_graph_width/2*(originalScale-newScale);
0560 drawGraph();
0561 }
0562 function zoom_out_click(event) {
0563 const zoom = 0.909;
0564 const originalScale = 1. / timeZoomFactor / kInitPixelsPerSecond;
0565 timeZoomFactor *= zoom;
0566 const newScale = 1. / timeZoomFactor / kInitPixelsPerSecond;
0567
0568 minVisibleTime = minVisibleTime + max_graph_width/2*(originalScale-newScale);
0569 drawGraph();
0570 }
0571 zoom_in_button.addEventListener("click", zoom_in_click);
0572 zoom_out_button.addEventListener("click", zoom_out_click);
0573
0574 function name_onWheel(event) {
0575 let offset = 0;
0576 graph_translate(0, -1*event.deltaY);
0577 drawGraph();
0578 event.preventDefault()
0579 }
0580 name_view.addEventListener('wheel', name_onWheel);
0581 }
0582 async function populateDropdown() {
0583 showLoadingSpinner();
0584 try {
0585 const response = await fetch('/list-json');
0586 const jsonFiles = await response.json();
0587 const dropdown = document.getElementById('jsonDropdown');
0588
0589 jsonFiles.forEach(file => {
0590 const option = document.createElement('option');
0591 option.value = file;
0592 option.textContent = file;
0593 dropdown.appendChild(option);
0594 });
0595 } catch (error) {
0596 console.error('Error fetching JSON file list:', error);
0597 }
0598 finally {
0599 hideLoadingSpinner();
0600 }
0601 }
0602
0603 jsonDropdown.addEventListener('change', function() {
0604 const selectedFile = this.value;
0605 if (selectedFile) {
0606 hideSelectFilePrompt();
0607 fetchData(selectedFile);
0608 } else {
0609 console.log('No file selected');
0610 }
0611 });
0612
0613 populateDropdown();
0614 function showLoadingSpinner() {
0615 document.getElementById('loadingSpinner').style.display = 'block';
0616 }
0617
0618 function hideLoadingSpinner() {
0619 document.getElementById('loadingSpinner').style.display = 'none';
0620 }
0621
0622 function showSelectFilePrompt() {
0623 document.getElementById('selectFilePrompt').style.display = 'block';
0624 }
0625
0626 function hideSelectFilePrompt() {
0627 document.getElementById('selectFilePrompt').style.display = 'none';
0628 }
0629
0630 };