// Graph function Timer(){ let start = window.performance.now(); return function(x = "timer"){ console.log(x, window.performance.now()-start); } } window.addEventListener('load', function(){ var urlParams = new URLSearchParams(window.location.search); if(urlParams.has('white')){ document.body.classList.add('white'); } else if(urlParams.has('black')){ document.body.classList.add('black'); } }) function addClass(obj, cls){ if(!obj.classList.contains(cls)) obj.classList.add(cls) } function delClass(obj, cls){ if(obj.classList.contains(cls)) obj.classList.remove(cls) } function AJAX(addr){ var xhr = new XMLHttpRequest(); if (debugCommunication) console.log('AJAX', addr); this.sendJSON = function(array, returnIsJSON = true){ xhr.open("POST", addr, true); xhr.send(JSON.stringify(array)); return new Promise(function(resolve, reject){ xhr.addEventListener('load', function(){ if(this.status == 200){ if(returnIsJSON){ this.responseJSON = JSON.parse(this.responseText); } resolve(this); } }); }); } this.getJSON = function(){ xhr.open("GET", addr, true); xhr.send(); return new Promise(function(resolve, reject){ xhr.addEventListener('load', function(){ if(this.status == 200){ if (debugCommunication) console.log('A RES', JSON.parse(this.responseText)); resolve(JSON.parse(this.responseText)); } }); }); } this.get = function(responseType = "text"){ xhr.open("GET", addr, true); xhr.responseType = responseType; xhr.send(); return new Promise(function(resolve, reject){ xhr.addEventListener('load', function(){ if(this.status == 200){ resolve(this); } }); }); } return this; } function doubleTap(callback){ var timeout; var lastTap = 0, lastX=NaN, lastY=NaN; function handler(event) { var currentTime = new Date().getTime(); var tapLength = currentTime - lastTap; let touch = event.changedTouches ? event.changedTouches[0] : event, x = touch.clientX, y = touch.clientY; clearTimeout(timeout); if (tapLength < 500 && tapLength > 0 && Math.abs(lastX-x) < 40 && Math.abs(lastY-y) < 40) { event.preventDefault(); callback() } else { timeout = setTimeout(function() { clearTimeout(timeout); }, 500); } lastTap = currentTime; lastX = x; lastY = y; } window.addEventListener('touchend', handler); return {stop: function(){ window.removeEventListener('touchend', handler) }} } function maxAr(array){ return Math.max.apply(Math, array.map(function(o) { if (o.y == null) return -1e99; return o.y; })); } function minAr(array){ return Math.min.apply(Math, array.map(function(o) { if (o.y == null) return 1e99; return o.y; })); } function autoScale(chart) { axis = chart.options.scales.yAxes[0] datasets = chart.data.datasets; let max = -1e99; let min = 1e99; for (testwidth = 1; testwidth >= 0; testwidth--) { for(let i = 0; i < datasets.length; i++){ ds = datasets[i]; if (ds.borderWidth == testwidth) continue; let lmax = maxAr(ds.data); let lmin = minAr(ds.data); if(lmax > max) max = lmax; if(lmin < min) min = lmin; } if (min < max) { break; } else if (min == max) { max = min + 0.5; min = max - 1.0; break; } } //console.log('autoScale', min, max); axis.ticks.min = min - (max - min) * 0.05; axis.ticks.max = max + (max - min) * 0.01; axis.min = axis.ticks.min; axis.max = axis.ticks.max; } function strFormat(str, significant_digits) { if (str == null) return ''; evalue = str.toExponential(significant_digits-1).replace(/0*e/, 'e').replace(/\.e/, 'e').replace("e+", "e"); fvalue = Number.parseFloat(evalue).toString(); if(fvalue.length <= evalue.length) return fvalue; else return evalue; } let graphs = (function (){ let dataset_to_graph_map = {}, blocks, doUpdates=true, top_vars=[], bottom_vars=[], zoomed =false; let type = 'linear'; let ngraphs = 4; let chart_array = new Array(ngraphs); let graph_array = new Array(ngraphs); let vars_array = new Array(ngraphs); let container = document.createElement('div'); container.classList.add("graphs-container"); for (let i = 0; i < ngraphs; i++) { let gr = document.createElement('div'); gr.classList.add('graph'); gr.classList.add('graph-' + i); container.appendChild(gr); graph_array[i] = gr; } function clear(gindex){ let gr = graph_array[gindex]; let ch = chart_array[gindex]; gr.innerHTML = ''; chart_array[gindex] = undefined; vars_array[gindex] = []; if (ch) { for (let key in dataset_to_graph_map) { if (dataset_to_graph_map[key][0] == gindex) { delete dataset_to_graph_map[key]; } } } } function createSelection(gindex){ let el = graph_array[gindex]; clear(gindex); let selection = document.createElement('div'); selection.classList.add('selection'); for(let block of blocks){ let bel = document.createElement('div'); bel.classList.add('select'); let title = document.createElement('div'); title.classList.add('title'); title.innerHTML = block.tag; bel.appendChild(title); let params = document.createElement('div'); params.classList.add('params'); for(let param of block.curves){ let pel = document.createElement('div'); pel.classList.add('param'); pel.innerHTML = param.label; params.appendChild(pel); } bel.appendChild(params); bel.addEventListener('click', function(){ createGraph(gindex, block) }) selection.appendChild(bel); } el.appendChild(selection); } function createGraph(gindex, block){ clear(gindex); let dict = {} for (let curve of block.curves) { vars_array[gindex].push(curve.name); dict[curve.name] = curve; } //let varlist = top_vars.concat(bottom_vars); varlist = vars_array[gindex]; let el =graph_array[gindex]; AJAX("http://" + hostPort + "/graph?time=" + minTime/1000 + "," + maxTime/1000 + "&variables=" + varlist + "&id=" + clientID).getJSON().then(function(data){ //console.log('Graph', block, data) let chart = new Graph(gindex, el, "Time", block.unit, block.tag, type); chart_array[gindex] = chart; for(let key in data.graph){ if(!vars_array[gindex].includes(key)){ continue; } let pdata = []; for(let e of data.graph[key]){ //if(e[1] == null || e[1] == null){ // continue; //} pdata.push({x: e[0]*1000, y: e[1]}); } if(pdata.length > 0){ addDataset(gindex, key, [dict[key].label, dict[key].color, pdata]) /*console.log(timeRange); if(data[data.length-1].x-data[0].x > d && data[data.length-1].x-data[0].x < (30*60+10)*1000){ // Adjust to requested time d = data[data.length-1].x-data[0].x max = data[data.length-1].x; min = data[0].x; }*/ } } chart.setMinMax(minTime,maxTime); // chart.autoScaleChart(); chart.update(); AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON(); // why this? }) } // add dataset to graph with graph_id function addDataset(gindex, key, dataset){ let g = chart_array[gindex]; dataset_to_graph_map[key] = [gindex, g.addDataset(dataset)]; } function setMinMax(min, max){ for (let ch of chart_array) { if (ch) ch.setMinMax(min, max); } } /* function toggleAxesType(){ type = (type === 'linear') ? 'logarithmic' : 'linear'; if(top_chart) top_chart.setAxesType(type); if(bottom_chart) bottom_chart.setAxesType(type); return type; } */ // responsible for new data being displayed on chart function newDataHandler(key, data){ if(!(key in dataset_to_graph_map)) return maxTime = data.x; let i = dataset_to_graph_map[key]; chart_array[i[0]].pushData(i[1], data) } function clickHandler(evt) { if(evt.target.tagName == "CANVAS"){ for (let ch of chart_array) { if (ch) ch.clickHandler(evt); } } } container.addEventListener('click', clickHandler) function reloadDataFlag(key, data){ if(!(key in dataset_to_graph_map)) return let i = dataset_to_graph_map[key]; chart_array[i[0]].reloadData(i[1], data); } function reloadData(min, max){ min = min/1000; if(max > now()){ max = 0; }else{ max = max/1000; } let vardict = {}; for (let vars of vars_array) { for (let v of vars) { vardict[v] = 1; } } AJAX("http://" + hostPort + "/graph?time=" + min + ","+max+"&variables=" + Object.keys(vardict) + "&id=" + clientID).getJSON().then(function(data){ for(let key in data.graph){ let pdata = []; for(let e of data.graph[key]){ //if(e[0] == null || e[1] == null){ // continue; //} pdata.push({x: e[0]*1000, y: e[1]}); } if(pdata.length > 0){ reloadDataFlag(key, pdata); } } for (let ch of chart_array) { if (ch) ch.autoScaleChart(); // should depend on a flag } AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON(); // why this ? update(); }); } function checkReload(chart){ let xmin = chart.options.scales.xAxes[0].ticks.min, xmax = chart.options.scales.xAxes[0].ticks.max; if(xmax < now()-10000){ // was 100000 = 100sec doUpdates = false; }else{ doUpdates = true; } if(xmin < minTime || xmax > maxTime || xmax - xmin < 0.5 * (maxTime - minTime)){ //TODO: the criterium for getting finer resolution data should depend, if better res. is available // this information has to come from the server reloadData(xmin, xmax); minTime = xmin; maxTime = xmax; } else { autoScale(chart); // TODO: must depend on autoScale flag chart.update(); } } function zoompan(chart, redrawX = null){ let xmin = chart.options.scales.xAxes[0].ticks.min, xmax = chart.options.scales.xAxes[0].ticks.max; setMinMax(xmin,xmax); for (let ch of chart_array) { if (ch) ch.redraw(redrawX); } update(); } function update(){ for (let ch of chart_array) { if (ch) ch.update(); } } let g_varlist = []; function updateAllDisabled(){ // not used anymore? AJAX("http://" + hostPort + "/graph?time="+timeRange+"&variables=" + g_varlist + "&id=" + clientID).getJSON().then(function(a){ AJAX( "http://" + hostPort + "/updategraph?id=" + clientID).getJSON() }); } function getVarlist(blocks){ var varlist = []; for (var i = 0; i < blocks.length; i++) { for (var j = 0; j < blocks[i].curves.length; j++) { varlist.push(blocks[i].curves[j].name); } } return varlist; } let startTime, recvTime, minTime, maxTime; function receivedVars(nblocks){ maxTime = timeRange[1]*1000; minTime = timeRange[0]*1000; AJAX("http://" + hostPort + "/gettime?time=-1800,0&id="+ clientID).getJSON().then(function(data){ startTime = data.time[1]*1000; maxTime = startTime; minTime = data.time[0]*1000; recvTime = performance.now(); }); g_varlist = getVarlist(nblocks) let f = 0; insertSlide(f, "graphics", "graphics", container); blocks = nblocks; //createSelection(true); //createSelection(false); for (let i=0; i < ngraphs; i++) { if (blocks[i]) createGraph(i, blocks[i]); else createSelection(i); } container.parentNode.querySelector('.panel').classList.add('graphics'); if(isTouchDevice()){ let currentZoomSwipe = true; let zoomSwipe = document.createElement('div'); function innerZoomSwipe(){ if(currentZoomSwipe){ zoomSwipe.innerHTML = "panning"; swiper[f].params.noSwipingClass = "swiper-slide-main"; for (let ch of chart_array) { if (ch) ch.setPanOnOff(true); } }else{ zoomSwipe.innerHTML = "swiping"; swiper[f].params.noSwipingClass = "abc"; for (let ch of chart_array) { if (ch) ch.setPanOnOff(false); } } } innerZoomSwipe(); doubleTap(function(){ currentZoomSwipe = !currentZoomSwipe; innerZoomSwipe(); }); container.parentNode.querySelector('.panel span').appendChild(zoomSwipe); }else{ let currentMode = 0, modes = ['xy','x','y'], zoomMode = document.createElement('div'); function setInnerZoomMode(){ zoomMode.innerHTML = "Zoom mode: "; for(let k = 0; k < modes.length; k++){ if(k==currentMode){ zoomMode.innerHTML += ""+modes[k] + " "; }else zoomMode.innerHTML += modes[k] + " "; } } setInnerZoomMode(); function toggleZoomMode(){ currentMode++; if(currentMode >= modes.length){ currentMode = 0; } setInnerZoomMode(); for (let ch of chart_array) { if (ch) ch.setZoomMode(modes[currentMode]); } } zoomMode.addEventListener('click', toggleZoomMode); container.parentNode.querySelector('.panel span').appendChild(zoomMode); } } function now(){ return startTime + (performance.now()-recvTime); } function getBlocks(){ return blocks; } return { addDataset: addDataset, //toggleAxesType: toggleAxesType, newDataHandler: newDataHandler, zoompan: zoompan, receivedVars: receivedVars, createSelection: createSelection, doUpdates: function(){return doUpdates}, update: update, now: now, zoomed: zoomed, checkReload: checkReload, getBlocks: getBlocks, createGraph: createGraph, } })(); function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){ let chart; let maxspan = 10 * 864e5; let minspan = 120000; let parent = document.createElement("div"); parent.classList.add("chart-container"); container.appendChild(parent); let canvas = document.createElement("canvas"); canvas.setAttribute("width", "500"); canvas.setAttribute("height", "500"); canvas.style.width = "500px"; canvas.style.height = "500px"; parent.appendChild(canvas); let ctx = canvas.getContext("2d"); chart = new Chart(ctx, { type: 'scatter', options: { responsive: true, maintainAspectRatio: false, animation:{duration:0}, scales: { yAxes: [{ticks: { beginAtZero: false, mirror: true, padding: -10, //workaround for proper number format callback: function(label, index, labels) { if(index == 0 || index == labels.length-1) return ""; return strFormat(label, 9); } }, gridLines:{drawTicks:false}, scaleLabel: false, // {display: true, labelString: y_label}, type:scaleType, afterBuildTicks: function(axis, ticks) { if (scaleType == "logarithmic" && ticks.length <= 4) { y1 = ticks[0]; y0 = ticks.slice(-1)[0]; span = y1 - y0; step = Math.abs(span * 0.1).toExponential(0); if (step[0] > '5') { step = '5' + step.substr(1); } else if (step[0] > '2') { step = '2' + step.substr(1); } step = Number.parseFloat(step); ticks = [y1]; for (let yt = Math.ceil(y1 / step) * step; yt > y0; yt -= step) { ticks.push(yt); } ticks.push(y0); } return ticks }, }], xAxes: [{ scaleLabel: false,//{display: true, labelString: x_label}, type: 'time', time: { displayFormats: {'millisecond': 'HH:mm:ss.SSS', 'second': 'HH:mm:ss', 'minute': 'HH:mm','hour': 'dd HH:mm', 'day': 'dd MMM DD', 'week': 'MMM DD', 'month': 'MMM DD'}, }, ticks: { padding: -20, callback: function(label, index, labels) { let l = labels.length - 1; if (index == 0 || index == l) return ""; if (index == 1 || index == l - 1) { // skip first and / or last label, if too close to the end let minstep = 0.05 * (labels[l].value - labels[0].value); if (index == 1) { if (labels[1].value - labels[0].value < minstep) return ""; } else { if (labels[l].value - labels[l-1].value < minstep) return ""; } } hourofday = /\S+ (\d+:00)/.exec(label); if (hourofday && hourofday[1] != '00:00') { return hourofday[1]; } return label; } }, afterBuildTicks: function(axis, ticks) { if (!ticks || ticks.length <= 2) return ticks; first = ticks[0].value; step = ticks[1].value - first; offset = (first - axis._adapter.startOf(first, 'day')) % step; let start = 0; if (ticks[0].value - offset < axis.min) start = 1; let v = axis.min; result = [{value: v, major: false}]; for (tick of ticks.slice(start)) { v = tick.value - offset; result.push({value: v, major: false}); } v += step; if (v < axis.max) result.push({value:v, major: false}); result.push({value: axis.max, major: false}); return result; }, gridLines:{drawTicks:false}, }], }, tooltips: false, legend: false, pan: { enabled: true, mode: "xy", speed: 10, threshold: 10, onPan: function({chart}) { graphs.zoomed = true; graphs.zoompan(chart);}, onPanComplete: function({chart}){graphs.checkReload(chart);redraw()} }, zoom: { enabled: true, drag: false, mode: "xy", speed: 0.1, sensitivity: 1, onZoom: function({chart}) { graphs.zoomed = true; graphs.zoompan(chart);}, onZoomComplete: function({chart}){graphs.checkReload(chart);redraw()}, } } }); //console.log('create legend') let legend = document.createElement('div'); legend.classList.add('legend'); let legendels = {}; //graphSwiper.appendSlide(parent); let controls = document.createElement('div'); controls.classList.add('controls'); legend.appendChild(controls); function addControl(inner, callback){ let c = document.createElement('div'); c.classList.add('control'); c.classList.add('vcontrol'); c.innerHTML = inner; c.addEventListener('click', function(e){ if(!legendmoving) callback(e); }) controls.appendChild(c); return c; } /*changecontrol = addControl("Change Dataset", function(){ graphs.createSelection(gindex); });*/ let changecontrol = document.createElement('div'); controls.append(changecontrol); let blocks = graphs.getBlocks(); for (let i = 0; i < blocks.length; i++) { let block = blocks[i]; let c = document.createElement('div'); c.classList.add('control'); c.classList.add('subcontrol'); c.innerHTML = block.tag; if (tag == block.tag) { c.style.color = "#000000"; } c.addEventListener('click', function(e){ if(!legendmoving) {} graphs.createGraph(gindex, block); //console.log('BLOCK', block) }) changecontrol.appendChild(c); spacer = document.createElement('div'); spacer.classList.add('subspacer'); changecontrol.appendChild(spacer); } addControl("Hide legend", function(){ legend.style.display = 'none'; redrawX = null; /*for(let el of legendels){ el.innerHTML = ""; }*/ }); /*let update_max = null; addControl("Reset Zoom/Pan", function(){ graphs.zoomed = false; if(update_max !== null){ chart.options.scales.xAxes[0].ticks.max = update_max; update_max = null; } chart.resetZoom(); graphs.zoompan(chart); });*/ addControl("Autoscale Y", function(){ autoScale(chart); update(); }); addControl("Go to now", function(){ let length = chart.options.scales.xAxes[0].ticks.max - chart.options.scales.xAxes[0].ticks.min; chart.options.scales.xAxes[0].ticks.max = graphs.now(); chart.options.scales.xAxes[0].ticks.min = graphs.now()-length; graphs.zoomed= false; graphs.zoompan(chart, canvas.clientWidth); graphs.checkReload(chart); legend.style.left = "0px"; }); let linlog = addControl("Lin Log", function(e){ //graphs.toggleAxesType(); toggleAxesType(); }); if(scaleType !== "linear"){ linlog.innerHTML = "Lin Log"; } let startX=0,startY=0, startElX=0, startElY=0,legendmoving=false; function legendmousemove(e){ if (Math.abs(e.pageX-startX) + Math.abs(e.pageY-startY) < 4) return; legendmoving=true; let X = startElX + (e.pageX-startX), Y = startElY + (e.pageY-startY); if(X > 0 && X+legend.getBoundingClientRect().width < parent.getBoundingClientRect().width){ legend.style.left = X + "px"; }else if(X > 0){ legend.style.left = (parent.getBoundingClientRect().width-legend.getBoundingClientRect().width)+"px"; }else{ legend.style.left = "0px"; } if(Y>0 && Y+legend.getBoundingClientRect().height< parent.getBoundingClientRect().height){ legend.style.top = Y + "px"; }else if(Y>0){ legend.style.top = (parent.getBoundingClientRect().height-legend.getBoundingClientRect().height) + "px"; }else{ legend.style.top = "0px"; } } function legendmouseup(e){ setTimeout(function(){ legendmoving=false; }, 200); window.removeEventListener('mousemove', legendmousemove); window.removeEventListener('mouseup', legendmouseup); window.removeEventListener('blur', legendmouseup); } legend.addEventListener('mousedown', function(e){ if(e.which !== 1){ return; } startX = e.pageX; startY = e.pageY; startElX = legend.offsetLeft; startElY = legend.offsetTop; window.addEventListener('mousemove', legendmousemove); window.addEventListener('mouseup', legendmouseup); window.addEventListener('blur', legendmouseup); }) legend.style.display = 'none'; parent.appendChild(legend); let margin = 10; function clickHandler(e){ let trect = e.target.getBoundingClientRect(); let X = e.clientX - trect.x, Y = e.clientY - trect.y; prect = parent.getBoundingClientRect(); if(legend.style.display == 'none'){ legend.style.display = 'flex'; let lrect = legend.getBoundingClientRect(); if(X+lrect.width+margin < prect.width){ legend.style.left = X + "px"; }else if(X - lrect.width+margin < 0){ legend.style.left = "0px"; }else{ legend.style.left = (X-lrect.width)+"px"; } if(Y+lrect.height+margin < prect.height){ legend.style.top = Y + "px"; }else if(Y - lrect.height+margin <0 ){ legend.style.top = "0px"; }else{ legend.style.top = (Y-lrect.height) + "px"; } } redrawX = X; //drawLineX(e.layerX); graphs.update(); } //canvas.addEventListener('click', clickHandler) function hideLegend(){ //legend.style.display = 'none'; } function setZoomMode(to){ chart.options.zoom.mode = to; } function setPanOnOff(to){ chart.options.pan.enabled = to; } function addTime(data){ chart.data.labels = data chart.update(); } function addDataset(data){ let dataset_index = chart.data.datasets.length; chart.data.datasets.push({data: data[2], label: data[0], origLabel: data[0], spanGaps: false, lineJoin: 'round', borderWidth: 2, borderColor: data[1],fill: false, pointRadius: 0, tension:0, showLine: true}); let dataset = chart.data.datasets[dataset_index]; let legendel = document.createElement('div'); let legendelvalue = document.createElement('div'); legendelvalue.classList.add('value'); legendels[data[0]] = legendelvalue; legendel.classList.add('legendel') let color = document.createElement('div'); color.classList.add('color') color.style.backgroundColor = dataset.borderColor; legendel.appendChild(color); legendel.innerHTML += dataset.label; legendel.addEventListener('click', function(){ if(legendmoving) return let meta = dataset._meta[Object.keys(dataset._meta)[0]] /* if(meta.hidden == null){ meta.hidden = true; addClass(legendel, 'hidden'); }else{ meta.hidden = null; delClass(legendel, 'hidden'); } */ if (dataset.borderWidth == 1) { dataset.borderWidth = 2; } else { dataset.borderWidth = 1; } autoScale(chart); chart.update(); }) legendel.appendChild(legendelvalue); legend.appendChild(legendel); return dataset_index; } function autoScaleChart() { autoScale(chart); } function pushData(dataset_index, data_point){ chart.data.datasets[dataset_index].data.push(data_point); if(!graphs.zoomed){ chart.options.scales.xAxes[0].ticks.max = data_point.x; }else{ update_max = data_point.x; } } function reloadData(index, data){ chart.data.datasets[index].data = data; } function setMinMax(min, max){ let ax = chart.options.scales.xAxes[0]; let ay = chart.options.scales.yAxes[0]; // clamp X-span let span = max - min; let half = 0; if (chart.lastXmin) { if (span > maxspan) { half = maxspan * 0.5; } else if (span < minspan) { half = minspan * 0.5; } } if (half) { // clamped mid = (chart.lastXmin + chart.lastXmax) * 0.5; min = mid - half; max = mid + half; ay.ticks.min = chart.lastYmin; ay.ticks.max = chart.lastYmax; } else { chart.lastXmin = min; chart.lastXmax = max; chart.lastYmin = ay.ticks.min; chart.lastYmax = ay.ticks.max; } // custom algorithm for tick step mainstep = 1000; step = 1000; for (info of [['second', 60, [1, 2, 5, 10, 15, 30]], ['minute', 60, [1, 2, 5, 10, 15, 30]], ['hour', 24, [1, 2, 4, 6, 12]], ['day', 365, [1,2,4,7,14,31]]]) { if (span < 12 * mainstep * info[2].slice(-1)[0]) { for (fact of info[2]) { step = mainstep * fact; if (span < 12 * step) { break; } } break; } mainstep *= info[1]; } ax.time.unit = info[0]; ax.time.stepSize = Math.round(step / mainstep); //ax.ticks.unit = ax.time.unit; //ax.ticks.stepSize =ax.time.stepSize; //console.log('INFO', step, mainstep, info, ax, ax.time); ax.ticks.max = max; ax.ticks.min = min; } function toggleAxesType(){ setAxesType((chart.options.scales.yAxes[0].type=== 'linear') ? 'logarithmic' : 'linear'); } function setAxesType(type){ scaleType = type; if(type == "linear"){ linlog.innerHTML = "Lin Log"; }else{ linlog.innerHTML = "Lin Log"; } chart.options.scales.yAxes[0].type = type; chart.options.animation.duration = 800; update(); setTimeout(function(){chart.options.animation.duration = 0;},850) } let redrawX = null; function redraw(a=null){ if(a!==null) redrawX = a; if(redrawX !== null){ drawLineX(redrawX); } } function drawLineX(x){ test = [] for(let i in chart.data.datasets){ let d = 0; for(let j = 0;j < chart.getDatasetMeta(i).data.length; j++){ let dp = chart.getDatasetMeta(i).data[j]; let d2 = Math.abs(dp._model.x - x) if(d == 0 || d2 < d){ d = d2; legendels[chart.data.datasets[i].label].innerHTML = strFormat(chart.data.datasets[i].data[dp._index].y, 6); test[i]=dp } } } //chart.render() //console.log(test) /*for(let a of test){ if(a === undefined) continue; ctx.beginPath(); ctx.arc(a._model.x, a._model.y, 3, 0, 2 * Math.PI, false); ctx.fillStyle = "rgba(0,0,0,0.8)"; ctx.fill(); }*/ ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.lineWidth = 2; ctx.strokeStyle = "rgba(0,0,0,0.8)"; ctx.globalCompositeOperation = "destination-over"; ctx.stroke(); ctx.globalCompositeOperation = "source-over"; } redrawX = canvas.clientWidth || null; redraw(); function update(){ chart.update(); redraw(); } return { addTime: addTime, addDataset: addDataset, pushData: pushData, setMinMax: setMinMax, setAxesType: setAxesType, clickHandler: clickHandler, setZoomMode: setZoomMode, hideLegend: hideLegend, setPanOnOff:setPanOnOff, redraw: redraw, update: update, reloadData: reloadData, autoScaleChart: autoScaleChart, } } let map = {};/* function createCharts2(graph){ //console.log(graph, blocks, graphs); /*document.querySelector(".content-graphics").innerHTML = ""; document.querySelector(".content-graphics").style.overflow = "auto"; document.querySelector(".content-graphics").style.paddingTop = "2em"; document.querySelector(".content-graphics").style.paddingRight = "5em"; for(let block of blocks){ block.graph = graphs.addGraph("Time",block.unit) } graphs.addToDOM(); let max=0, min=0, d=0; for(let key in graph){ map[key] = {}; for(let block of blocks){ for(let curve of block.curves){ if(curve.name == key){ let data = []; for( let e of graph[key]){ if(e[1] == null || e[1] == null){ continue; } data.push({x: e[0]*1000, y: e[1]}); } if(data.length>0){ if(data[data.length-1].x-data[0].x > d && data[data.length-1].x-data[0].x < (30*60+10)*1000){ // Adjust to requested time d = data[data.length-1].x-data[0].x max = data[data.length-1].x; min = data[0].x; } graphs.addDataset(block.graph, key, {data: data, label:curve.label, origLabel: curve.label,borderColor: curve.color,fill: false, pointRadius: 0, tension:0, showLine: true}) } } } } } graphs.setMinMax(min,max); }*/ let lastupdate = performance.now(); function updateCharts2(graph){ if(!graphs.doUpdates()) { console.log('graphs.doUpdates skipped'); return; } for(let key in graph){ for (pt of graph[key]) { if (graph[key][1] != null) { // there is at least ONE valid datapoint graphs.newDataHandler(key, {x: graph[key][0][0]*1000, y: graph[key][0][1]}); break; } } } graphs.update(); }