279 lines
8.6 KiB
HTML
279 lines
8.6 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<!-- <link href="https://cdn.jsdelivr.net/npm/nvd3@1.8.6/build/nv.d3.min.css" rel="stylesheet" type="text/css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js" charset="utf-8"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/nvd3@1.8.6/build/nv.d3.min.js"></script>
|
|
<script src="http://nvd3.org/assets/js/data/stream_layers.js"></script>-->
|
|
<link rel="stylesheet" src="//cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.min.css" />
|
|
<script src="//cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/dygraphs@2.0.0/src/extras/crosshair.js"></script>
|
|
|
|
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
|
|
|
|
|
</head>
|
|
<body>
|
|
|
|
<style>
|
|
.dygraph-label { text-align: center; }
|
|
.dygraph-xlabel{
|
|
position: relative;
|
|
right: 130px;
|
|
text-anchor:center;
|
|
text-align:right;
|
|
}
|
|
.dygraph-ylabel {
|
|
position: relative;
|
|
top: 25px;
|
|
left: 20px;
|
|
text-align:right;
|
|
transform: rotate(-90deg);
|
|
-webkit-transform: rotate(-90deg);
|
|
-moz-transform: rotate(-90deg);
|
|
-o-transform: rotate(-90deg);
|
|
-ms-transform: rotate(-90deg);
|
|
}
|
|
</style>
|
|
|
|
<div id="noroll" style="width:400px; height:300px;border:1px solid black"></div>
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
var seriesKeys = [ //"allframespersecond",
|
|
"aclist_size",
|
|
"aclist_listed",
|
|
"aclist_nof_adsb",
|
|
"aclist_nof_mlat",
|
|
"aclist_nof_flawa",
|
|
"aclist_nof_ogn",
|
|
"aclist_nof_flarm"
|
|
];
|
|
$(document).ready(function () {
|
|
|
|
var data = [ [new Date()].concat(Array(seriesKeys.length).fill(0)) ];
|
|
|
|
var userIsInteracting = false;
|
|
const myInteractions = Object.assign({}, Dygraph.defaultInteractionModel, {
|
|
mousedown: (event, g, context) => {
|
|
console.log("start mousedown");
|
|
userIsInteracting = true;
|
|
context.initializeMouseDown(event, g, context);
|
|
Dygraph.startPan(event, g, context);
|
|
},
|
|
|
|
mouseup: (event, g, context) => {
|
|
console.log("mouseup", event, g, context);
|
|
userIsInteracting = false;
|
|
Dygraph.endPan(event, g, context);
|
|
context.isPanning = false;
|
|
}
|
|
});
|
|
|
|
function zeropad(x) {
|
|
if (x < 10) return "0" + x;else return "" + x;
|
|
}
|
|
var g = new Dygraph(
|
|
document.getElementById("noroll"),
|
|
data,
|
|
{
|
|
customBars: false, // no error bars
|
|
labels: ["Date", /*"Frames/s",*/ "Total", "Listed", "ADS-B", "MLAT", "FlAwa", "OGN", "FLARM"],
|
|
title: 'AC Stats',
|
|
ylabel:'Number of Aircrafts',
|
|
xlabel:'Time (UTC)',
|
|
//legend: 'always',
|
|
axisLineWidth:2.0,
|
|
interactionModel: myInteractions,
|
|
plugins: [
|
|
new Dygraph.Plugins.Crosshair({
|
|
direction: "vertical"
|
|
})
|
|
],
|
|
series:{
|
|
'Total': {
|
|
strokePattern: Dygraph.DOT_DASH_LINE
|
|
},
|
|
'Recent':{
|
|
strokePattern: Dygraph.DASHED_LINE
|
|
}
|
|
},
|
|
axes: {
|
|
'x': {
|
|
axisLabelFormatter: function(d, gran) {
|
|
return zeropad(d.getHours()) + ":"
|
|
+ zeropad(d.getMinutes()) + ":"
|
|
+ zeropad(d.getSeconds());
|
|
}
|
|
}
|
|
},
|
|
|
|
digitsAfterDecimal:0, // always display same-length of date
|
|
strokeWidth: 3,
|
|
showRangeSelector: true,
|
|
rangeSelectorHeight: 40,
|
|
axisLabelFontSize: 15,
|
|
labelsUTC:true,
|
|
//showRoller: true,
|
|
rollPeriod: 10,
|
|
drawPoints:false,
|
|
rangeSelectorPlotFillColor: "#A7B1C4"
|
|
}
|
|
);
|
|
|
|
|
|
var plotRefresh = function(userIsInteracting) {
|
|
|
|
if(userIsInteracting){
|
|
console.log("inhibiting");
|
|
return;
|
|
}
|
|
|
|
const fetchStatisticData = unit =>
|
|
fetch('/statistic.json?select='+unit, {credentials: 'include'}).then(function(rawResponse) {
|
|
return rawResponse.json();
|
|
}).then(function(arg){
|
|
var result = {
|
|
unit: unit, // unit to which the arrayfragment belongs
|
|
arrayFragment: parseBaseData({json:arg, unit:unit}) // parse json and return part of that array
|
|
}
|
|
return result;
|
|
});
|
|
|
|
|
|
var units = ["seconds", "minutes", "hours"];
|
|
|
|
|
|
/* Determine whether we need to clamp the window right or left or not at all */
|
|
// start and end timestamp of previous run's data arrays
|
|
var dateWindow = g.dateWindow_; // get currently set dateWindow
|
|
var oldLastDate = data[data.length-1][0].getTime();
|
|
var oldFirstDate = data[0][0].getTime();
|
|
var clampRight = false;
|
|
var clampLeft = false;
|
|
if(dateWindow != null){
|
|
if(dateWindow[1] == oldLastDate){
|
|
clampRight=true;
|
|
}
|
|
// update date window to clamp series left
|
|
if(dateWindow[0] == oldFirstDate){
|
|
clampLeft=true;
|
|
}
|
|
}
|
|
|
|
// clear base data to visualize
|
|
data = [];
|
|
|
|
// submit all unit URL fetches simultaneously
|
|
Promise
|
|
.all(units.map(fetchStatisticData))
|
|
// then collect results
|
|
.then(function(resultSet){
|
|
// sort result array by their
|
|
/* ATTENTION: this assumes that the timeranges returned are not overlapping */
|
|
/*resultSet.sort(function(a, b) {
|
|
return units.indexOf(b) - units.indexOf(a);
|
|
});*/
|
|
|
|
// merge arrays into one
|
|
resultSet.forEach(function(resultObj){
|
|
data = data.concat(resultObj.arrayFragment)
|
|
});
|
|
console.log(data);
|
|
|
|
console.log("refreshing plot");
|
|
// prepare and initialize re-rendering on last fetched unit
|
|
// sort base data by time (FIXME: can this be avoided?)
|
|
data.sort(function(a, b){ return a[0] - b[0]; });
|
|
|
|
// clamp to edges and exchange base dataset on graph
|
|
if(dateWindow != null){
|
|
var currentLastDate = data[data.length-1][0].getTime();
|
|
var currentFirstDate = data[0][0].getTime();
|
|
clampedDateWindow = [];
|
|
var diffToLastRun = 0;
|
|
if(clampRight){
|
|
diffToLastRun = currentLastDate - dateWindow[1];
|
|
} else if(clampLeft){
|
|
diffToLastRun = currentFirstDate - dateWindow[0];
|
|
}
|
|
clampedDateWindow = [dateWindow[0]+diffToLastRun, dateWindow[1]+diffToLastRun]
|
|
|
|
g.updateOptions( { 'file': data,
|
|
dateWindow: clampedDateWindow
|
|
}
|
|
);
|
|
} else {
|
|
g.updateOptions( { 'file': data } );
|
|
}
|
|
|
|
})
|
|
.catch(function(e) {
|
|
if(!e.message.includes("Failed to fetch")){
|
|
console.log("A problem occurred: "+e.message);
|
|
if (!e.stack)
|
|
try {
|
|
// IE requires the Error to actually be thrown or else the
|
|
// Error's 'stack' property is undefined.
|
|
throw e;
|
|
} catch (e) {
|
|
if (!e.stack) {
|
|
//return 0; // IE < 10, likely
|
|
}
|
|
}
|
|
var stack = e.stack.toString().split(/\r\n|\n/);
|
|
msg = e.message;
|
|
if (msg === '') {
|
|
msg = '""';
|
|
}
|
|
console.log(msg, ' [' + stack[1] + ']');
|
|
}
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
plotRefresh();
|
|
window.intervalId = setInterval(function(){plotRefresh(userIsInteracting);}, 1000);
|
|
|
|
});
|
|
|
|
/* FIXME: on dragging, pause updates!! */
|
|
|
|
function parseBaseData(args) {
|
|
var json = args.json;
|
|
var unit = args.unit;
|
|
|
|
var arrayFragment = [];
|
|
var resolution = json.resolution;
|
|
var firstElementTime = json.time; // unix ts of first arr element
|
|
|
|
seriesLength = json.allframespersecond.length;
|
|
|
|
var currentElementTime = firstElementTime;
|
|
var timeIncrement = 1;
|
|
if(unit == "minutes"){
|
|
timeIncrement = 60;
|
|
} else if(unit == "hours"){
|
|
timeIncrement = 60*60;
|
|
}
|
|
for(var i=0; i<seriesLength; i++){
|
|
row = [new Date(currentElementTime * 1000)];
|
|
currentElementTime+=timeIncrement;
|
|
seriesKeys.forEach(function(key){
|
|
row.push(json[key][i]);
|
|
});
|
|
arrayFragment.push(row);
|
|
}
|
|
|
|
return arrayFragment;
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|