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>
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |