253 lines
8.1 KiB
HTML
253 lines
8.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>SwimTrainer</title>
|
|
|
|
<!-- plot.ly -->
|
|
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
|
|
<!-- chart.js -->
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
|
|
|
|
<!-- Materialize -->
|
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
|
<!-- MsgPack -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/msgpack5/4.2.0/msgpack5.js"></script>
|
|
|
|
<link rel="manifest" href="swimtrainer.webmanifest">
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
<nav class="blue " role="navigation">
|
|
<div class="nav-wrapper container"><a id="logo-container" href="#" class="brand-logo">SwimTrainer</a>
|
|
</div>
|
|
</nav>
|
|
|
|
|
|
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="row">
|
|
<div class="col s4 center-align">
|
|
<h5>Bahnen</h5>
|
|
<h1><span id="lanes"></span></h1>
|
|
</div>
|
|
<div class="col s4 center-align">
|
|
<h5>Züge</h5>
|
|
<h1><span id="peaks"></span></h1>
|
|
</div>
|
|
<div class="col s4 center-align">
|
|
<a class="waves-effect waves-light btn-large" onclick="reload()">Neu laden</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col s12">
|
|
<canvas id="plotCanvas"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col s3">
|
|
</div>
|
|
<div class="col s6">
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col s6"><label>Seconds</label></div>
|
|
<div class="col s6"> <input type="number" id="seconds" min="30" max="3600" value="120"> </div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s6"><label>Threshold</label></div>
|
|
<div class="col s6"> <input type="number" id="threshold" min="100" max="60000" value="2000"> </div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s6"><label>y max</label></div>
|
|
<div class="col s6"> <input type="number" id="ymax" min="1000" max="60000" value="4000"> </div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="btn red white-text waves-effect waves-light" id="clickMe" type="button" onclick="save();">
|
|
<i class="material-icons left">save</i>
|
|
Speichern
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const url = 'http://smartswim/api/session/data';
|
|
|
|
// ------------------ MsgPack --------------------------------------------------------------
|
|
var msgpack = msgpack5();
|
|
msgpack.registerDecoder(0xd1, function (byteArr) {
|
|
function typedArrayToBuffer(array) {
|
|
return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
|
|
}
|
|
|
|
return new Int16Array(typedArrayToBuffer(byteArr));
|
|
});
|
|
|
|
function countPeaks(array, threshold=500)
|
|
{
|
|
var result = 0;
|
|
var lastMin = 0;
|
|
for(var i = 1; i < array.length - 2; i++)
|
|
{
|
|
var current = array[i].y;
|
|
var last = array[i-1].y;
|
|
var next = array[i+1].y;
|
|
|
|
if(current > next && current > last && (current - lastMin) > threshold) {
|
|
result += 1;
|
|
lastMin = array[i].y;
|
|
}
|
|
lastMin = Math.min(lastMin, array[i].y);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ------------------ Plot data -------------------------------------------------------------
|
|
|
|
|
|
var ctx = document.getElementById('plotCanvas').getContext('2d');
|
|
var chart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
datasets: [{
|
|
data: [],
|
|
backgroundColor: '#2196f388',
|
|
color: 'blue'
|
|
}]
|
|
},
|
|
options: {color: ['red']}
|
|
});
|
|
|
|
|
|
var appendToPlotArray = function (typedTimeArr, typedValueArr, currentTime) {
|
|
var plotArray = chart.data.datasets[0].data;
|
|
// remove last element, pop on empty is ok
|
|
plotArray.pop();
|
|
|
|
for (var i = 0; i < typedValueArr.length; ++i) {
|
|
plotArray.push({x: typedTimeArr[i], y: typedValueArr[i]});
|
|
}
|
|
plotArray.push({x: currentTime, y: plotArray[plotArray.length - 1].y});
|
|
|
|
var secondsToShow = parseInt( document.getElementById("seconds").value );
|
|
var yMax = parseInt( document.getElementById("ymax").value );
|
|
|
|
chart.options = {
|
|
legend: {
|
|
display: false
|
|
},
|
|
elements: {
|
|
point: {
|
|
radius: 2
|
|
},
|
|
line: {
|
|
tension: 0 // disables bezier curves
|
|
}
|
|
},
|
|
scales: {
|
|
xAxes: [{
|
|
ticks: {
|
|
min: plotArray[plotArray.length - 1].x - secondsToShow * 10,
|
|
max: plotArray[plotArray.length - 1].x
|
|
},
|
|
type: 'linear',
|
|
display: false
|
|
}],
|
|
yAxes: [{
|
|
ticks: {
|
|
min: 0,
|
|
max: yMax
|
|
},
|
|
type: 'linear'
|
|
}],
|
|
}
|
|
};
|
|
chart.data.datasets[0].data = plotArray;
|
|
chart.update();
|
|
};
|
|
|
|
var reload = function() {
|
|
chart.data.datasets[0].data = [];
|
|
};
|
|
/*
|
|
var appendToPlot = function (typedTimeArr, typedValueArr, currentTime)
|
|
{
|
|
return;
|
|
// remove last element, pop on empty is ok
|
|
plotData.x.pop();
|
|
plotData.y.pop();
|
|
|
|
var timeArr = Array.prototype.slice.call(typedTimeArr);
|
|
var valueArr = Array.prototype.slice.call(typedValueArr);
|
|
plotData.x = plotData.x.concat(timeArr);
|
|
plotData.y = plotData.y.concat(valueArr);
|
|
|
|
// add last element
|
|
plotData.x.push(currentTime);
|
|
plotData.y.push(plotData.y[plotData.y.length - 1]);
|
|
|
|
var plotObj = document.getElementById('plot');
|
|
Plotly.purge(plotObj);
|
|
Plotly.plot(plotObj, [plotData]);
|
|
};*/
|
|
|
|
var updateRunning = false;
|
|
var update = function ()
|
|
{
|
|
if( updateRunning === true ) {
|
|
//console.log("Skipping update, because update is running");
|
|
return;
|
|
}
|
|
var oReq = new XMLHttpRequest();
|
|
var startIndex = chart.data.datasets[0].data.length;
|
|
if (startIndex > 0) {
|
|
startIndex -= 1;
|
|
}
|
|
|
|
oReq.open("GET", url + "?startIndex=" + startIndex, true);
|
|
oReq.responseType = "arraybuffer";
|
|
//console.log("-> Query, startIndex=", startIndex);
|
|
oReq.onload = function (oEvent) {
|
|
var arrayBuffer = oReq.response; // Note: not oReq.responseText
|
|
if (arrayBuffer) {
|
|
var decoded = msgpack.decode(arrayBuffer);
|
|
//console.log("-> Finished query", startIndex, "#old:", chart.data.datasets[0].data.length,
|
|
// "#new:", decoded['values'].length);
|
|
appendToPlotArray(decoded['timestamps'], decoded['values'], decoded['currentTime']);
|
|
//console.log("-> After unpack", chart.data.datasets[0].data.length);
|
|
var peakThreshold = parseInt( document.getElementById("threshold").value );
|
|
var peaks = countPeaks(chart.data.datasets[0].data, peakThreshold);
|
|
document.getElementById("peaks").textContent = peaks;
|
|
document.getElementById("lanes").textContent = (peaks / 30).toFixed(1);
|
|
|
|
}
|
|
updateRunning = false;
|
|
};
|
|
oReq.send(null);
|
|
updateRunning = true;
|
|
};
|
|
update();
|
|
|
|
setInterval(update, 1000);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|
|
|
|
|
|
|
|
|
|
|