diff --git a/ui/charts.go b/ui/charts.go index 4305d6ef..18de3501 100644 --- a/ui/charts.go +++ b/ui/charts.go @@ -73,11 +73,6 @@ func CreateItem() []Item { Content: "This is a double bar chart", Widget: "doubleBarChart", }, - { - Title: "Multiple Line Chart", - Content: "This is a multiple line chart", - Widget: "multipleLineChart", - }, { Title: "Step Chart", Content: "This is a step chart", diff --git a/ui/web/static/js/charts.js b/ui/web/static/js/charts.js index 2b55ad99..096a4257 100644 --- a/ui/web/static/js/charts.js +++ b/ui/web/static/js/charts.js @@ -791,6 +791,10 @@ class TimeSeriesLineChart extends Echart { } #generateScript() { + const channels = JSON.stringify(this.chartData.channels); + const things = JSON.stringify(this.chartData.things); + const names = JSON.stringify(this.chartData.seriesNames); + const colors = JSON.stringify(this.chartData.colors); return ` var lineChart = echarts.init(document.getElementById("${this.ID}")); var option = { @@ -815,7 +819,6 @@ class TimeSeriesLineChart extends Echart { }, legend: { show: true, - left: 'right', }, series: [ { @@ -824,18 +827,18 @@ class TimeSeriesLineChart extends Echart { lineStyle: { width: ${this.chartData.lineWidth}, type: 'solid', - color: '${this.chartData.lineColor}' }, - name: '${this.chartData.seriesName}' } ] }; lineChart.setOption(option); var chartdata = { - channel:'${this.chartData.channel}', - publisher:'${this.chartData.thing}', - name:'${this.chartData.valueName}', + channels:${channels}, + publishers:${things}, + names:${names}, + colors:${colors}, + valueName: '${this.chartData.valueName}', from:${this.chartData.startTime}, to:${this.chartData.stopTime}, aggregation:'${this.chartData.aggregationType}', @@ -846,68 +849,136 @@ class TimeSeriesLineChart extends Echart { async function getData(linechart,chartData) { try { - const response = await fetch( - "${pathPrefix}/data?channel=" + chartData.channel + - "&publisher=" + chartData.publisher + - "&name=" + chartData.name + - "&from=" + chartData.from + - "&to=" + chartData.to + - "&aggregation=" + chartData.aggregation + - "&limit=" + chartData.limit + - "&interval=" + chartData.interval, - ); - if (response.ok) { - const data = await response.json(); - const xAxisArray = []; - const yAxisArray = []; - let currentTimestamp = chartData.from; - let previousTimestamp; - const endTimestamp = chartData.to; - const intervalNum = 3600; - while (currentTimestamp <= endTimestamp) { - let messageIndex = data.messages.length - 1; - let isempty= true; - while (messageIndex >= 0 && (data.messages[messageIndex].time) >= previousTimestamp) { - const item = data.messages[messageIndex]; - if ((item.time) <= currentTimestamp) { - const date = new Date(item.time); - xAxisArray.push(date.toLocaleTimeString()); - yAxisArray.push(item.value); - isempty=false; + let xAxisArray=[]; + const yAxisArray = []; + let currentTimestamp = chartData.from; + let previousTimestamp=0; + const endTimestamp = chartData.to; + const timeDifference = chartData.to - chartData.from; + let intervalNum; + switch (true) { + case (timeDifference <= 3.6*1e6): + intervalNum = 6*1e5; + break; + case (timeDifference <= 8.64 * 1e7): + intervalNum = 6*1e6; + break; + case (timeDifference <= 6.05 * 1e8): + intervalNum = 6*1e7; + break; + case (timeDifference <= 2.63 * 1e9): + intervalNum = 6*1e8; + break; + case (timeDifference <= 3.16 * 1e10): + intervalNum = 6*1e9; + break; + default: + intervalNum = 6*1e8; + break; + } + + const initialXAxisArray = [] + const NoOfValues = Math.ceil(timeDifference/intervalNum); + for (i=0; i<=NoOfValues; i++) { + initialXAxisArray.push(currentTimestamp); + currentTimestamp += intervalNum; + } + xAxisArray.push(initialXAxisArray); + + for (i=0; i < chartData.channels.length; i++) { + const url = "${pathPrefix}/data?channel=" + chartData.channels[i] + + "&publisher=" + chartData.publishers[i] + + "&name=" + chartData.valueName + + "&from=" + chartData.from + + "&to=" + chartData.to + + "&aggregation=" + chartData.aggregation + + "&limit=" + chartData.limit + + "&interval=" + chartData.interval; + const response = await fetch(url); + if (response.ok) { + const data = await response.json(); + const xAxis=[]; + const yAxis=[]; + if (data.messages){ + const currArray = xAxisArray[xAxisArray.length-1]; + let currentTime, previousTime; + for (j=0; j < currArray.length; j++) { + let isempty = true; + let currentTime = currArray[j]; + for (k = (data.messages.length -1); k>= 0 ; k--) { + const item = data.messages[k]; + if (item.time > previousTime && item.time <= currentTime ) { + xAxis.push(item.time); + yAxis.push(item.value); + isEmpty= false; + } + } + if (isempty) { + xAxis.push(currentTime); + yAxis.push("-"); + } + previousTime=currentTime; } - messageIndex--; - } - if (isempty) { - const date = new Date(currentTimestamp); - xAxisArray.push(date.toLocaleTimeString()); - yAxisArray.push("-"); } - previousTimestamp = currentTimestamp; - currentTimestamp += intervalNum * 1e3; + xAxisArray.push(xAxis); + yAxisArray.push(yAxis); + } else { + // Handle errors + console.error("HTTP request failed with status:", response.status); } - linechart.setOption({ + } + + for (i =1; i< xAxisArray.length; i++) { + const missingData = findMissingValuesIndices(xAxisArray[i], xAxisArray[xAxisArray.length-1]); + missingData.forEach((value, index) => { + yAxisArray[i-1].splice(value,0,"-"); + }); + } + + function findMissingValuesIndices(array1, array2) { + const missingIndices = []; + array2.forEach((item, index) => { + if (!array1.includes(item)) { + missingIndices.push(index); + } + }); + return missingIndices; + } + + const xAxisData=[]; + xAxisArray[xAxisArray.length-1].forEach((element) => { + const date = new Date(element); + xAxisData.push(date); + }) + const seriesData = [] + for (i=0; i { + legendData.push(element); + }); + var option = { + title: { + text: "${this.chartData.title}", + left: 'left', }, - { - name: '${seriesName[1]}', - type: 'line', - stack: 'Total', - data: [220, 182, 191, 234, 290, 330, 310] + tooltip: { + trigger: 'axis' }, - { - name: '${seriesName[2]}', - type: 'line', - stack: 'Total', - data: [150, 232, 201, 154, 190, 330, 410] + legend: { + data: legendData, }, - ] + grid: { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + name: '${this.chartData.xAxisLabel}', + + }, + yAxis: { + type: 'value', + name: '${this.chartData.yAxisLabel}', + }, + series: [ + { + data: [], + type: 'line', + stack: 'Total', + lineStyle: { + width: ${this.chartData.lineWidth}, + type: 'solid', + }, + } + ] + } + + stackedLineChart.setOption(option); + var chartdata = { + channels:${channels}, + publishers:${things}, + names:${names}, + colors:${colors}, + valueName: '${this.chartData.valueName}', + from:${this.chartData.startTime}, + to:${this.chartData.stopTime}, + aggregation:'${this.chartData.aggregationType}', + limit:100, + interval:'${this.chartData.updateInterval}', }; + getData(stackedLineChart,chartdata); + async function getData(stackedLineChart,chartData) { + try { + let xAxisArray=[]; + const yAxisArray = []; + let currentTimestamp = chartData.from; + let previousTimestamp=0; + const endTimestamp = chartData.to; + const timeDifference = chartData.to - chartData.from; + let intervalNum; + switch (true) { + case (timeDifference <= 3.6*1e6): + intervalNum = 6*1e5; + break; + case (timeDifference <= 8.64 * 1e7): + intervalNum = 6*1e6; + break; + case (timeDifference <= 6.05 * 1e8): + intervalNum = 6*1e7; + break; + case (timeDifference <= 2.63 * 1e9): + intervalNum = 6*1e8; + break; + case (timeDifference <= 3.16 * 1e10): + intervalNum = 6*1e9; + break; + default: + intervalNum = 6*1e8; + break; + } + + const initialXAxisArray = [] + const NoOfValues = Math.ceil(timeDifference/intervalNum); + for (i=0; i<=NoOfValues; i++) { + initialXAxisArray.push(currentTimestamp); + currentTimestamp += intervalNum; + } + xAxisArray.push(initialXAxisArray); - stackedLineChart.setOption(option);`; + for (i=0; i < chartData.channels.length; i++) { + const url = "${pathPrefix}/data?channel=" + chartData.channels[i] + + "&publisher=" + chartData.publishers[i] + + "&name=" + chartData.valueName + + "&from=" + chartData.from + + "&to=" + chartData.to + + "&aggregation=" + chartData.aggregation + + "&limit=" + chartData.limit + + "&interval=" + chartData.interval; + const response = await fetch(url); + if (response.ok) { + const data = await response.json(); + const xAxis=[]; + const yAxis=[]; + if (data.messages){ + const currArray = xAxisArray[xAxisArray.length-1]; + let currentTime, previousTime; + for (j=0; j < currArray.length; j++) { + let isempty = true; + let currentTime = currArray[j]; + for (k = (data.messages.length -1); k>= 0 ; k--) { + const item = data.messages[k]; + if (item.time > previousTime && item.time <= currentTime ) { + xAxis.push(item.time); + yAxis.push(item.value); + isEmpty= false; + } + } + if (isempty) { + xAxis.push(currentTime); + yAxis.push("-"); + } + previousTime=currentTime; + } + } + xAxisArray.push(xAxis); + yAxisArray.push(yAxis); + } else { + // Handle errors + console.error("HTTP request failed with status:", response.status); + } + } + + for (i =1; i< xAxisArray.length; i++) { + const missingData = findMissingValuesIndices(xAxisArray[i], xAxisArray[xAxisArray.length-1]); + missingData.forEach((value, index) => { + yAxisArray[i-1].splice(value,0,"-"); + }); + } + + function findMissingValuesIndices(array1, array2) { + const missingIndices = []; + array2.forEach((item, index) => { + if (!array1.includes(item)) { + missingIndices.push(index); + } + }); + return missingIndices; + } + + const xAxisData=[]; + xAxisArray[xAxisArray.length-1].forEach((element) => { + const date = new Date(element); + xAxisData.push(date); + }) + const seriesData = [] + for (i=0; iTime Series Line Chart @@ -64,31 +64,67 @@ role="tabpanel" aria-labelledby="data-tab" > -
- - -
Please enter a valid uuid
-
-
- - -
Please enter a valid uuid
+
+
Data Source
+ + + + + + + + + + + + + + + + + + + +
ChannelThingValueColor
+ + + + + + + + + +
+
@@ -183,20 +219,6 @@ placeholder="Enter the x axis label" />
-
- - -
-
- - -
+ + + + + + + + + + + + + `; + } + + function removeLineChartRow(button) { + const row = button.parentNode.parentNode; + row.parentNode.removeChild(row); + } + {{ end }} diff --git a/ui/web/templates/charts/multiplelinechartmodal.html b/ui/web/templates/charts/multiplelinechartmodal.html deleted file mode 100644 index 106aca35..00000000 --- a/ui/web/templates/charts/multiplelinechartmodal.html +++ /dev/null @@ -1,278 +0,0 @@ - - -{{ define "multiplelinechartmodal" }} - - - -{{ end }} diff --git a/ui/web/templates/charts/stackedlinechartmodal.html b/ui/web/templates/charts/stackedlinechartmodal.html index 1d4bc3cf..fdc825c5 100644 --- a/ui/web/templates/charts/stackedlinechartmodal.html +++ b/ui/web/templates/charts/stackedlinechartmodal.html @@ -17,7 +17,7 @@ @@ -54,7 +54,6 @@ -
@@ -64,31 +63,71 @@ role="tabpanel" aria-labelledby="data-tab" > -
- - -
Please enter a valid uuid
-
-
- - -
Please enter a valid uuid
+
+
Data Source
+ + + + + + + + + + + + + + + + + + + +
ChannelThingValueColor
+ + + + + + + + + +
+
@@ -122,6 +161,17 @@ required />
+
+ + +
Stacked Line Charts name="updateInterval" id="update-interval" placeholder="Enter the update interval, eg. 5s, 10m, 1h, 1d" - required /> -
Please enter a valid interval
-
-
- - +
Please enter a valid interval
@@ -184,16 +222,6 @@ placeholder="Enter the x axis label" />
-
- - -