Add FPS density chart, add better way of serving static files
This commit is contained in:
parent
73b42dce4b
commit
ebed937b76
44
server.go
44
server.go
@ -1,7 +1,11 @@
|
|||||||
package flightlesssomething
|
package flightlesssomething
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -80,10 +84,46 @@ func Start(c *Config) {
|
|||||||
r.SetHTMLTemplate(tmpl)
|
r.SetHTMLTemplate(tmpl)
|
||||||
|
|
||||||
// Serve static files
|
// Serve static files
|
||||||
fileServer := http.FileServer(http.FS(staticFS))
|
|
||||||
r.GET("/static/*filepath", func(c *gin.Context) {
|
r.GET("/static/*filepath", func(c *gin.Context) {
|
||||||
|
filepath := c.Param("filepath")
|
||||||
|
file, err := staticFS.Open("static" + filepath)
|
||||||
|
if err != nil {
|
||||||
|
c.Status(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Get file info
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
c.Status(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file content into a byte slice
|
||||||
|
content, err := fs.ReadFile(staticFS, "static"+filepath)
|
||||||
|
if err != nil {
|
||||||
|
c.Status(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate ETag based on file content
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write(content)
|
||||||
|
etag := hex.EncodeToString(hash.Sum(nil))
|
||||||
|
|
||||||
|
// Set ETag and Cache-Control headers
|
||||||
|
c.Header("ETag", etag)
|
||||||
c.Header("Cache-Control", "public, max-age=3600")
|
c.Header("Cache-Control", "public, max-age=3600")
|
||||||
fileServer.ServeHTTP(c.Writer, c.Request)
|
|
||||||
|
// Check if the ETag matches
|
||||||
|
if match := c.GetHeader("If-None-Match"); match == etag {
|
||||||
|
c.Status(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the file with ETag and Last-Modified headers
|
||||||
|
http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), bytes.NewReader(content))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "/benchmarks") })
|
r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "/benchmarks") })
|
||||||
|
@ -461,6 +461,133 @@ Highcharts.chart('avgChart', {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Function to filter out the top and bottom 3% of FPS values
|
||||||
|
function filterOutliers(data) {
|
||||||
|
data.sort((a, b) => a - b);
|
||||||
|
var start = Math.floor(data.length * 0.01); // Ignore bottom 1%
|
||||||
|
var end = Math.ceil(data.length * 0.97); // Ignore top 1%
|
||||||
|
return data.slice(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to count occurrences of each FPS value
|
||||||
|
function countFPS(data) {
|
||||||
|
var counts = {};
|
||||||
|
data.forEach(function(fps) {
|
||||||
|
var roundedFPS = Math.round(fps);
|
||||||
|
counts[roundedFPS] = (counts[roundedFPS] || 0) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
var fpsArray = Object.keys(counts).map(function(key) {
|
||||||
|
return [parseInt(key), counts[key]];
|
||||||
|
}).sort(function(a, b) {
|
||||||
|
return a[0] - b[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Combine closest FPS values until we have 100 or fewer points
|
||||||
|
while (fpsArray.length > 100) {
|
||||||
|
var minDiff = Infinity;
|
||||||
|
var minIndex = -1;
|
||||||
|
|
||||||
|
// Find the pair with the smallest difference
|
||||||
|
for (var i = 0; i < fpsArray.length - 1; i++) {
|
||||||
|
var diff = fpsArray[i + 1][0] - fpsArray[i][0];
|
||||||
|
if (diff < minDiff) {
|
||||||
|
minDiff = diff;
|
||||||
|
minIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine the closest pair
|
||||||
|
fpsArray[minIndex][1] += fpsArray[minIndex + 1][1];
|
||||||
|
fpsArray[minIndex][0] = (fpsArray[minIndex][0] + fpsArray[minIndex + 1][0]) / 2;
|
||||||
|
fpsArray.splice(minIndex + 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fpsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate counts for each dataset after filtering outliers
|
||||||
|
var densityData = fpsDataArrays.map(function(dataArray) {
|
||||||
|
var filteredData = filterOutliers(dataArray.data);
|
||||||
|
return {
|
||||||
|
name: dataArray.label,
|
||||||
|
data: countFPS(filteredData)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create the chart
|
||||||
|
Highcharts.chart('densityChart', {
|
||||||
|
chart: {
|
||||||
|
type: 'areaspline',
|
||||||
|
backgroundColor: null
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'FPS Density',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: '16px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
title: {
|
||||||
|
text: 'FPS',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: {
|
||||||
|
text: 'Count',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gridLineColor: 'rgba(255, 255, 255, 0.1)'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
shared: true,
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
style: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
},
|
||||||
|
formatter: function() {
|
||||||
|
var points = this.points;
|
||||||
|
var tooltipText = '<b>' + points[0].series.name + '</b>: ' + points[0].y + ' points at ~' + Math.round(points[0].x) + ' FPS';
|
||||||
|
return tooltipText;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
areaspline: {
|
||||||
|
fillOpacity: 0.5,
|
||||||
|
marker: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
enabled: true,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
series: densityData
|
||||||
|
});
|
||||||
|
|
||||||
function calculateSpikes(data, threshold) {
|
function calculateSpikes(data, threshold) {
|
||||||
if (data.length < 6) {
|
if (data.length < 6) {
|
||||||
throw new Error("Data length must be greater than or equal to 6.");
|
throw new Error("Data length must be greater than or equal to 6.");
|
||||||
|
@ -90,6 +90,8 @@
|
|||||||
|
|
||||||
<div id="avgChart" style="height:250pt;"></div>
|
<div id="avgChart" style="height:250pt;"></div>
|
||||||
|
|
||||||
|
<div id="densityChart" style="height:250pt;"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="spikeThreshold" style="color: #FFFFFF;">Ignore Spike Threshold (%):</label>
|
<label for="spikeThreshold" style="color: #FFFFFF;">Ignore Spike Threshold (%):</label>
|
||||||
<input type="range" id="spikeThreshold" name="spikeThreshold" min="5" max="150" value="50" oninput="updateSpikesChart(this.value)">
|
<input type="range" id="spikeThreshold" name="spikeThreshold" min="5" max="150" value="50" oninput="updateSpikesChart(this.value)">
|
||||||
|
Loading…
Reference in New Issue
Block a user