serve static files
This commit is contained in:
parent
4c1aa120aa
commit
212cde5a5a
3
embed.go
3
embed.go
@ -4,3 +4,6 @@ import "embed"
|
|||||||
|
|
||||||
//go:embed templates/*
|
//go:embed templates/*
|
||||||
var templatesFS embed.FS
|
var templatesFS embed.FS
|
||||||
|
|
||||||
|
//go:embed static/*
|
||||||
|
var staticFS embed.FS
|
||||||
|
@ -79,6 +79,13 @@ func Start(c *Config) {
|
|||||||
tmpl := template.Must(template.ParseFS(templatesFS, "templates/*.tmpl"))
|
tmpl := template.Must(template.ParseFS(templatesFS, "templates/*.tmpl"))
|
||||||
r.SetHTMLTemplate(tmpl)
|
r.SetHTMLTemplate(tmpl)
|
||||||
|
|
||||||
|
// Serve static files
|
||||||
|
fileServer := http.FileServer(http.FS(staticFS))
|
||||||
|
r.GET("/static/*filepath", func(c *gin.Context) {
|
||||||
|
c.Header("Cache-Control", "public, max-age=3600")
|
||||||
|
fileServer.ServeHTTP(c.Writer, c.Request)
|
||||||
|
})
|
||||||
|
|
||||||
r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "/benchmarks") })
|
r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "/benchmarks") })
|
||||||
|
|
||||||
r.GET("/benchmarks", getBenchmarks)
|
r.GET("/benchmarks", getBenchmarks)
|
||||||
|
@ -0,0 +1,583 @@
|
|||||||
|
// Define a set of colors to be used for the charts
|
||||||
|
var colors = Highcharts.getOptions().colors;
|
||||||
|
|
||||||
|
function getLineChartOptions(title, description, unit, maxY = null) {
|
||||||
|
return {
|
||||||
|
chart: {
|
||||||
|
type: 'line',
|
||||||
|
backgroundColor: null, // Set background to transparent
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
zooming: {
|
||||||
|
type: 'x'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '16px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
text: description,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '12px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
lineColor: '#FFFFFF',
|
||||||
|
tickColor: '#FFFFFF',
|
||||||
|
labels: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: {
|
||||||
|
text: null
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
formatter: function() {
|
||||||
|
return this.value.toFixed(2) + ' ' + unit;
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gridLineColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
max: maxY
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
align: 'center',
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
shared: false,
|
||||||
|
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y:.2f} ' + unit + '</b><br/>', // Include unit in tooltip
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
line: {
|
||||||
|
marker: {
|
||||||
|
enabled: false,
|
||||||
|
symbol: 'circle',
|
||||||
|
lineColor: null,
|
||||||
|
radius: 1.5,
|
||||||
|
states: {
|
||||||
|
hover: {
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineWidth: 1,
|
||||||
|
animation: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
series: [],
|
||||||
|
exporting: {
|
||||||
|
buttons: {
|
||||||
|
contextButton: {
|
||||||
|
menuItems: [
|
||||||
|
'viewFullscreen',
|
||||||
|
'printChart',
|
||||||
|
'separator',
|
||||||
|
'downloadPNG',
|
||||||
|
'downloadJPEG',
|
||||||
|
'downloadPDF',
|
||||||
|
'downloadSVG',
|
||||||
|
'separator',
|
||||||
|
'downloadCSV',
|
||||||
|
'downloadXLS'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBarChartOptions(title, unit, maxY = null) {
|
||||||
|
return {
|
||||||
|
chart: {
|
||||||
|
type: 'bar',
|
||||||
|
backgroundColor: null, // Set background to transparent
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '16px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: [],
|
||||||
|
title: {
|
||||||
|
text: null
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
max: maxY,
|
||||||
|
title: {
|
||||||
|
text: unit,
|
||||||
|
align: 'high',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
overflow: 'justify',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return this.value.toFixed(2) + ' ' + unit;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
valueSuffix: ' ' + unit,
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return '<b>' + this.point.category + '</b>: ' + this.y.toFixed(2) + ' ' + unit;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
bar: {
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return this.y.toFixed(2) + ' ' + unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
enabled: false, // Disable legend
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
series: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createChart(chartId, title, description, unit, dataArrays, maxY = null) {
|
||||||
|
var options = getLineChartOptions(title, description, unit, maxY);
|
||||||
|
options.series = dataArrays.map(function(dataArray, index) {
|
||||||
|
return {name: dataArray.label, data: dataArray.data, color: colors[index % colors.length]};
|
||||||
|
});
|
||||||
|
|
||||||
|
Highcharts.chart(chartId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBarChart(chartId, title, unit, categories, data, colors, maxY = null) {
|
||||||
|
var options = getBarChartOptions(title, unit, maxY);
|
||||||
|
options.xAxis.categories = categories;
|
||||||
|
options.series = [{
|
||||||
|
name: title,
|
||||||
|
data: data,
|
||||||
|
colorByPoint: true,
|
||||||
|
colors: colors
|
||||||
|
}];
|
||||||
|
|
||||||
|
Highcharts.chart(chartId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateAverage(data) {
|
||||||
|
const sum = data.reduce((acc, value) => acc + value, 0);
|
||||||
|
return sum / data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculatePercentile(data, percentile) {
|
||||||
|
data.sort((a, b) => a - b);
|
||||||
|
const index = Math.ceil(percentile / 100 * data.length) - 1;
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create line charts
|
||||||
|
createChart('fpsChart', 'FPS', 'More is better', 'fps', fpsDataArrays);
|
||||||
|
createChart('frameTimeChart', 'Frametime', 'Less is better', 'ms', frameTimeDataArrays);
|
||||||
|
createChart('cpuLoadChart', 'CPU Load', '', '%', cpuLoadDataArrays, 100);
|
||||||
|
createChart('gpuLoadChart', 'GPU Load', '', '%', gpuLoadDataArrays, 100);
|
||||||
|
createChart('cpuTempChart', 'CPU Temperature', '', '°C', cpuTempDataArrays);
|
||||||
|
createChart('gpuTempChart', 'GPU Temperature', '', '°C', gpuTempDataArrays);
|
||||||
|
createChart('gpuCoreClockChart', 'GPU Core Clock', '', 'MHz', gpuCoreClockDataArrays);
|
||||||
|
createChart('gpuMemClockChart', 'GPU Memory Clock', '', 'MHz', gpuMemClockDataArrays);
|
||||||
|
createChart('gpuVRAMUsedChart', 'GPU VRAM Usage', '', '%', gpuVRAMUsedDataArrays, 100);
|
||||||
|
createChart('gpuPowerChart', 'GPU Power', '', 'W', gpuPowerDataArrays);
|
||||||
|
createChart('ramUsedChart', 'RAM Usage', '', 'GB', ramUsedDataArrays);
|
||||||
|
createChart('swapUsedChart', 'SWAP Usage', '', 'GB', swapUsedDataArrays);
|
||||||
|
|
||||||
|
// Calculate average CPU and GPU load
|
||||||
|
var cpuLoadAverages = cpuLoadDataArrays.map(function(dataArray) {
|
||||||
|
return calculateAverage(dataArray.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
var gpuLoadAverages = gpuLoadDataArrays.map(function(dataArray) {
|
||||||
|
return calculateAverage(dataArray.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create bar charts for average CPU and GPU load
|
||||||
|
createBarChart('cpuLoadSummaryChart', 'Average CPU Load', '%', cpuLoadDataArrays.map(function(dataArray) { return dataArray.label; }), cpuLoadAverages, colors, 100);
|
||||||
|
createBarChart('gpuLoadSummaryChart', 'Average GPU Load', '%', gpuLoadDataArrays.map(function(dataArray) { return dataArray.label; }), gpuLoadAverages, colors, 100);
|
||||||
|
|
||||||
|
// Calculate and render min, max, and average FPS
|
||||||
|
var categories = [];
|
||||||
|
var minFPSData = [];
|
||||||
|
var avgFPSData = [];
|
||||||
|
var maxFPSData = [];
|
||||||
|
|
||||||
|
fpsDataArrays.forEach(function(dataArray) {
|
||||||
|
var minFPS = calculatePercentile(dataArray.data, 1);
|
||||||
|
var avgFPS = calculateAverage(dataArray.data);
|
||||||
|
var maxFPS = calculatePercentile(dataArray.data, 97);
|
||||||
|
|
||||||
|
categories.push(dataArray.label);
|
||||||
|
minFPSData.push(minFPS);
|
||||||
|
avgFPSData.push(avgFPS);
|
||||||
|
maxFPSData.push(maxFPS);
|
||||||
|
});
|
||||||
|
|
||||||
|
Highcharts.chart('minMaxAvgChart', {
|
||||||
|
chart: {
|
||||||
|
type: 'bar',
|
||||||
|
backgroundColor: null
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'Min/Avg/Max FPS',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '16px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
text: 'More is better',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: categories,
|
||||||
|
title: {
|
||||||
|
text: null
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
title: {
|
||||||
|
text: 'FPS',
|
||||||
|
align: 'high',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
overflow: 'justify',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
valueSuffix: ' FPS',
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return '<b>' + this.series.name + '</b>: ' + this.y.toFixed(2) + ' FPS';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
bar: {
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return this.y.toFixed(2) + ' fps';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
reversed: true,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: '97th',
|
||||||
|
data: maxFPSData,
|
||||||
|
color: '#00FF00'
|
||||||
|
}, {
|
||||||
|
name: 'AVG',
|
||||||
|
data: avgFPSData,
|
||||||
|
color: '#0000FF'
|
||||||
|
}, {
|
||||||
|
name: '1%',
|
||||||
|
data: minFPSData,
|
||||||
|
color: '#FF0000'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate average FPS for each filename
|
||||||
|
var avgFPSData = fpsDataArrays.map(function(dataArray) {
|
||||||
|
return calculateAverage(dataArray.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate FPS as a percentage of the first element
|
||||||
|
var firstFPS = avgFPSData[0];
|
||||||
|
var percentageFPSData = avgFPSData.map(function(fps) {
|
||||||
|
return (fps / firstFPS) * 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create bar chart for FPS percentage
|
||||||
|
Highcharts.chart('avgChart', {
|
||||||
|
chart: {
|
||||||
|
type: 'bar',
|
||||||
|
backgroundColor: null
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'Average FPS in %',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '16px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: fpsDataArrays.map(function(dataArray) { return dataArray.label; }),
|
||||||
|
title: {
|
||||||
|
text: null
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
title: {
|
||||||
|
text: 'Percentage (%)',
|
||||||
|
align: 'high',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
overflow: 'justify',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
valueSuffix: ' %',
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return '<b>' + this.point.category + '</b>: ' + this.y.toFixed(2) + ' %';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
bar: {
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return this.y.toFixed(2) + ' %';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: 'FPS Percentage',
|
||||||
|
data: percentageFPSData,
|
||||||
|
colorByPoint: true,
|
||||||
|
colors: colors
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
function calculateSpikes(data, threshold) {
|
||||||
|
if (data.length < 6) {
|
||||||
|
throw new Error("Data length must be greater than or equal to 6.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let spikeCount = 0;
|
||||||
|
|
||||||
|
// Helper function to calculate the moving average with a minimum of 6 points
|
||||||
|
function movingAverage(arr, index) {
|
||||||
|
const windowSize = Math.max(6, Math.ceil(arr.length * 0.05)); // 5 % of the data
|
||||||
|
const halfWindowSize = Math.floor(windowSize / 2);
|
||||||
|
const start = Math.max(0, index - halfWindowSize);
|
||||||
|
const end = Math.min(arr.length - 1, index + halfWindowSize);
|
||||||
|
const actualWindowSize = end - start + 1;
|
||||||
|
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
sum += arr[i];
|
||||||
|
}
|
||||||
|
return sum / actualWindowSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const currentPoint = data[i];
|
||||||
|
const movingAvg = movingAverage(data, i);
|
||||||
|
|
||||||
|
const change = Math.abs(currentPoint - movingAvg) / movingAvg * 100;
|
||||||
|
|
||||||
|
if (change > threshold) {
|
||||||
|
spikeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (spikeCount / data.length) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSpikesChart(threshold) {
|
||||||
|
document.getElementById('spikeThresholdValue').innerText = threshold + '%';
|
||||||
|
|
||||||
|
var spikePercentages = fpsDataArrays.map(function(dataArray) {
|
||||||
|
return calculateSpikes(dataArray.data, threshold);
|
||||||
|
});
|
||||||
|
|
||||||
|
Highcharts.chart('spikesChart', {
|
||||||
|
chart: {
|
||||||
|
type: 'bar',
|
||||||
|
backgroundColor: null
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'FPS Spikes',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '16px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
text: 'Less is better',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '12px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: categories,
|
||||||
|
title: {
|
||||||
|
text: null
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
title: {
|
||||||
|
text: 'Percentage (%)',
|
||||||
|
align: 'high',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
overflow: 'justify',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
valueSuffix: ' %',
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return '<b>' + this.point.category + '</b>: ' + this.y.toFixed(2) + ' %';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
bar: {
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
return this.y.toFixed(2) + ' %';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: 'Spike Percentage',
|
||||||
|
data: spikePercentages,
|
||||||
|
colorByPoint: true,
|
||||||
|
colors: colors
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial render of spikes chart
|
||||||
|
updateSpikesChart(document.getElementById('spikeThreshold').value);
|
@ -195,592 +195,6 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script src="/static/js/benchmark.js"></script>
|
||||||
|
|
||||||
// Define a set of colors to be used for the charts
|
|
||||||
var colors = Highcharts.getOptions().colors;
|
|
||||||
|
|
||||||
function getLineChartOptions(title, description, unit, maxY = null) {
|
|
||||||
return {
|
|
||||||
chart: {
|
|
||||||
type: 'line',
|
|
||||||
backgroundColor: null, // Set background to transparent
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
zooming: {
|
|
||||||
type: 'x'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: title,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '16px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
subtitle: {
|
|
||||||
text: description,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '12px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
lineColor: '#FFFFFF',
|
|
||||||
tickColor: '#FFFFFF',
|
|
||||||
labels: {
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
title: {
|
|
||||||
text: null
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
formatter: function() {
|
|
||||||
return this.value.toFixed(2) + ' ' + unit;
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
gridLineColor: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
max: maxY
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
align: 'center',
|
|
||||||
verticalAlign: 'bottom',
|
|
||||||
itemStyle: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
shared: false,
|
|
||||||
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y:.2f} ' + unit + '</b><br/>', // Include unit in tooltip
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
borderColor: '#FFFFFF',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
line: {
|
|
||||||
marker: {
|
|
||||||
enabled: false,
|
|
||||||
symbol: 'circle',
|
|
||||||
lineColor: null,
|
|
||||||
radius: 1.5,
|
|
||||||
states: {
|
|
||||||
hover: {
|
|
||||||
enabled: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lineWidth: 1,
|
|
||||||
animation: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
credits: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
series: [],
|
|
||||||
exporting: {
|
|
||||||
buttons: {
|
|
||||||
contextButton: {
|
|
||||||
menuItems: [
|
|
||||||
'viewFullscreen',
|
|
||||||
'printChart',
|
|
||||||
'separator',
|
|
||||||
'downloadPNG',
|
|
||||||
'downloadJPEG',
|
|
||||||
'downloadPDF',
|
|
||||||
'downloadSVG',
|
|
||||||
'separator',
|
|
||||||
'downloadCSV',
|
|
||||||
'downloadXLS'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBarChartOptions(title, unit, maxY = null) {
|
|
||||||
return {
|
|
||||||
chart: {
|
|
||||||
type: 'bar',
|
|
||||||
backgroundColor: null, // Set background to transparent
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: title,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '16px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
categories: [],
|
|
||||||
title: {
|
|
||||||
text: null
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
min: 0,
|
|
||||||
max: maxY,
|
|
||||||
title: {
|
|
||||||
text: unit,
|
|
||||||
align: 'high',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
overflow: 'justify',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return this.value.toFixed(2) + ' ' + unit;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
valueSuffix: ' ' + unit,
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
borderColor: '#FFFFFF',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return '<b>' + this.series.name + '</b>: ' + this.y.toFixed(2) + ' ' + unit;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
dataLabels: {
|
|
||||||
enabled: true,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return this.y.toFixed(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
credits: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
series: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createChart(chartId, title, description, unit, dataArrays, maxY = null) {
|
|
||||||
var options = getLineChartOptions(title, description, unit, maxY);
|
|
||||||
options.series = dataArrays.map(function(dataArray, index) {
|
|
||||||
return {name: dataArray.label, data: dataArray.data, color: colors[index % colors.length]};
|
|
||||||
});
|
|
||||||
|
|
||||||
Highcharts.chart(chartId, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createBarChart(chartId, title, unit, categories, data, colors, maxY = null) {
|
|
||||||
var options = getBarChartOptions(title, unit, maxY);
|
|
||||||
options.xAxis.categories = categories;
|
|
||||||
options.series = [{
|
|
||||||
name: title,
|
|
||||||
data: data,
|
|
||||||
colorByPoint: true,
|
|
||||||
colors: colors
|
|
||||||
}];
|
|
||||||
|
|
||||||
Highcharts.chart(chartId, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateAverage(data) {
|
|
||||||
const sum = data.reduce((acc, value) => acc + value, 0);
|
|
||||||
return sum / data.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculatePercentile(data, percentile) {
|
|
||||||
data.sort((a, b) => a - b);
|
|
||||||
const index = Math.ceil(percentile / 100 * data.length) - 1;
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create line charts
|
|
||||||
createChart('fpsChart', 'FPS', 'More is better', 'fps', fpsDataArrays);
|
|
||||||
createChart('frameTimeChart', 'Frametime', 'Less is better', 'ms', frameTimeDataArrays);
|
|
||||||
createChart('cpuLoadChart', 'CPU Load', '', '%', cpuLoadDataArrays, 100);
|
|
||||||
createChart('gpuLoadChart', 'GPU Load', '', '%', gpuLoadDataArrays, 100);
|
|
||||||
createChart('cpuTempChart', 'CPU Temperature', '', '°C', cpuTempDataArrays);
|
|
||||||
createChart('gpuTempChart', 'GPU Temperature', '', '°C', gpuTempDataArrays);
|
|
||||||
createChart('gpuCoreClockChart', 'GPU Core Clock', '', 'MHz', gpuCoreClockDataArrays);
|
|
||||||
createChart('gpuMemClockChart', 'GPU Memory Clock', '', 'MHz', gpuMemClockDataArrays);
|
|
||||||
createChart('gpuVRAMUsedChart', 'GPU VRAM Usage', '', '%', gpuVRAMUsedDataArrays, 100);
|
|
||||||
createChart('gpuPowerChart', 'GPU Power', '', 'W', gpuPowerDataArrays);
|
|
||||||
createChart('ramUsedChart', 'RAM Usage', '', 'GB', ramUsedDataArrays);
|
|
||||||
createChart('swapUsedChart', 'SWAP Usage', '', 'GB', swapUsedDataArrays);
|
|
||||||
|
|
||||||
// Calculate average CPU and GPU load
|
|
||||||
var cpuLoadAverages = cpuLoadDataArrays.map(function(dataArray) {
|
|
||||||
return calculateAverage(dataArray.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
var gpuLoadAverages = gpuLoadDataArrays.map(function(dataArray) {
|
|
||||||
return calculateAverage(dataArray.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create bar charts for average CPU and GPU load
|
|
||||||
createBarChart('cpuLoadSummaryChart', 'Average CPU Load', '%', cpuLoadDataArrays.map(function(dataArray) { return dataArray.label; }), cpuLoadAverages, colors, 100);
|
|
||||||
createBarChart('gpuLoadSummaryChart', 'Average GPU Load', '%', gpuLoadDataArrays.map(function(dataArray) { return dataArray.label; }), gpuLoadAverages, colors, 100);
|
|
||||||
|
|
||||||
// Calculate and render min, max, and average FPS
|
|
||||||
var categories = [];
|
|
||||||
var minFPSData = [];
|
|
||||||
var avgFPSData = [];
|
|
||||||
var maxFPSData = [];
|
|
||||||
|
|
||||||
fpsDataArrays.forEach(function(dataArray) {
|
|
||||||
var minFPS = calculatePercentile(dataArray.data, 1);
|
|
||||||
var avgFPS = calculateAverage(dataArray.data);
|
|
||||||
var maxFPS = calculatePercentile(dataArray.data, 97);
|
|
||||||
|
|
||||||
categories.push(dataArray.label);
|
|
||||||
minFPSData.push(minFPS);
|
|
||||||
avgFPSData.push(avgFPS);
|
|
||||||
maxFPSData.push(maxFPS);
|
|
||||||
});
|
|
||||||
|
|
||||||
Highcharts.chart('minMaxAvgChart', {
|
|
||||||
chart: {
|
|
||||||
type: 'bar',
|
|
||||||
backgroundColor: null
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: 'Min/Avg/Max FPS',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '16px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
subtitle: {
|
|
||||||
text: 'More is better',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
categories: categories,
|
|
||||||
title: {
|
|
||||||
text: null
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
min: 0,
|
|
||||||
title: {
|
|
||||||
text: 'FPS',
|
|
||||||
align: 'high',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
overflow: 'justify',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
valueSuffix: ' FPS',
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
borderColor: '#FFFFFF',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return '<b>' + this.series.name + '</b>: ' + this.y.toFixed(2) + ' FPS';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
dataLabels: {
|
|
||||||
enabled: true,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return this.y.toFixed(2) + ' fps';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
reversed: true,
|
|
||||||
itemStyle: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
credits: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
name: '97th',
|
|
||||||
data: maxFPSData,
|
|
||||||
color: '#00FF00'
|
|
||||||
}, {
|
|
||||||
name: 'AVG',
|
|
||||||
data: avgFPSData,
|
|
||||||
color: '#0000FF'
|
|
||||||
}, {
|
|
||||||
name: '1%',
|
|
||||||
data: minFPSData,
|
|
||||||
color: '#FF0000'
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate average FPS for each filename
|
|
||||||
var avgFPSData = fpsDataArrays.map(function(dataArray) {
|
|
||||||
return calculateAverage(dataArray.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate FPS as a percentage of the first element
|
|
||||||
var firstFPS = avgFPSData[0];
|
|
||||||
var percentageFPSData = avgFPSData.map(function(fps) {
|
|
||||||
return (fps / firstFPS) * 100;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create bar chart for FPS percentage
|
|
||||||
Highcharts.chart('avgChart', {
|
|
||||||
chart: {
|
|
||||||
type: 'bar',
|
|
||||||
backgroundColor: null
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: 'Average FPS in %',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '16px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
categories: fpsDataArrays.map(function(dataArray) { return dataArray.label; }),
|
|
||||||
title: {
|
|
||||||
text: null
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
min: 0,
|
|
||||||
title: {
|
|
||||||
text: 'Percentage (%)',
|
|
||||||
align: 'high',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
overflow: 'justify',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
valueSuffix: ' %',
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
borderColor: '#FFFFFF',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return '<b>' + this.series.name + '</b>: ' + this.y.toFixed(2) + ' %';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
dataLabels: {
|
|
||||||
enabled: true,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return this.y.toFixed(2) + ' %';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
credits: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
name: 'FPS Percentage',
|
|
||||||
data: percentageFPSData,
|
|
||||||
colorByPoint: true,
|
|
||||||
colors: colors
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
function calculateSpikes(data, threshold) {
|
|
||||||
if (data.length < 6) {
|
|
||||||
throw new Error("Data length must be greater than or equal to 6.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let spikeCount = 0;
|
|
||||||
|
|
||||||
// Helper function to calculate the moving average with a minimum of 6 points
|
|
||||||
function movingAverage(arr, index) {
|
|
||||||
const windowSize = Math.max(6, Math.ceil(arr.length * 0.05)); // 5 % of the data
|
|
||||||
const halfWindowSize = Math.floor(windowSize / 2);
|
|
||||||
const start = Math.max(0, index - halfWindowSize);
|
|
||||||
const end = Math.min(arr.length - 1, index + halfWindowSize);
|
|
||||||
const actualWindowSize = end - start + 1;
|
|
||||||
|
|
||||||
let sum = 0;
|
|
||||||
for (let i = start; i <= end; i++) {
|
|
||||||
sum += arr[i];
|
|
||||||
}
|
|
||||||
return sum / actualWindowSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
const currentPoint = data[i];
|
|
||||||
const movingAvg = movingAverage(data, i);
|
|
||||||
|
|
||||||
const change = Math.abs(currentPoint - movingAvg) / movingAvg * 100;
|
|
||||||
|
|
||||||
if (change > threshold) {
|
|
||||||
spikeCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (spikeCount / data.length) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSpikesChart(threshold) {
|
|
||||||
document.getElementById('spikeThresholdValue').innerText = threshold + '%';
|
|
||||||
|
|
||||||
var spikePercentages = fpsDataArrays.map(function(dataArray) {
|
|
||||||
return calculateSpikes(dataArray.data, threshold);
|
|
||||||
});
|
|
||||||
|
|
||||||
Highcharts.chart('spikesChart', {
|
|
||||||
chart: {
|
|
||||||
type: 'bar',
|
|
||||||
backgroundColor: null
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: 'FPS Spikes',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '16px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
subtitle: {
|
|
||||||
text: 'Less is better',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: '12px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
categories: categories,
|
|
||||||
title: {
|
|
||||||
text: null
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
min: 0,
|
|
||||||
title: {
|
|
||||||
text: 'Percentage (%)',
|
|
||||||
align: 'high',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
overflow: 'justify',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
valueSuffix: ' %',
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
borderColor: '#FFFFFF',
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return '<b>' + this.series.name + '</b>: ' + this.y.toFixed(2) + ' %';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
dataLabels: {
|
|
||||||
enabled: true,
|
|
||||||
style: {
|
|
||||||
color: '#FFFFFF'
|
|
||||||
},
|
|
||||||
formatter: function() {
|
|
||||||
return this.y.toFixed(2) + ' %';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
credits: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
name: 'Spike Percentage',
|
|
||||||
data: spikePercentages,
|
|
||||||
colorByPoint: true,
|
|
||||||
colors: colors
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial render of spikes chart
|
|
||||||
updateSpikesChart(document.getElementById('spikeThreshold').value);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{template "footer.tmpl" .}}
|
{{template "footer.tmpl" .}}
|
||||||
|
Loading…
Reference in New Issue
Block a user