use timestamps to display iperf data so that variability in server response time doesn't cause spurious gaps
diff --git a/web/ons-demo/js/flows.js b/web/ons-demo/js/flows.js
index ab5eddf..32f9347 100644
--- a/web/ons-demo/js/flows.js
+++ b/web/ons-demo/js/flows.js
@@ -206,20 +206,42 @@
var updateRate = 2000; // ms
var pointsToDisplay = 1000;
- function makeGraph() {
- var d = ['M0,30'];
- var i;
- for (i=0; i < pointsToDisplay; ++i) {
- var sample = flow.iperfData.samples[i];
- if (!sample) {
- sample = 0;
+ function makeGraph(iperfData) {
+ var d = 'M0,0';
+
+ if (iperfData.samples && iperfData.samples.length) {
+
+ var lastX;
+ var i = iperfData.samples.length - 1;
+ while (i) {
+ var sample = iperfData.samples[i];
+
+ var x = (1000 - (iperfData.now - sample.time)*10);
+ var y = 28 * sample.value/1000000;
+ if (y > 28) {
+ y = 28;
+ }
+ if (i == iperfData.samples.length - 1) {
+ d = 'M' + x + ',30';
+ }
+
+ // handle gaps
+ // 1.5 for rounding error
+ if (lastX && lastX - x > 1.5) {
+ d += 'L' + lastX + ',30';
+ d += 'M' + x + ',30'
+ }
+ lastX = x;
+
+ d += 'L' + x + ',' + (30-y);
+
+ i -= 1;
+
+ if (!i) {
+ d += 'L' + x + ',30';
+ }
}
- var height = 28 * sample/1000000;
- if (height > 28)
- height = 28;
- d += 'L' + i * pointsToDisplay/(pointsToDisplay-1) + ',' + (30-height);
}
- d += 'L'+pointsToDisplay + ',' + 30;
return d;
}
@@ -228,32 +250,24 @@
startIPerf(flow, duration, updateRate/interval);
flow.iperfDisplayInterval = setInterval(function () {
if (flow.iperfData) {
- while (flow.iperfData.samples.length < pointsToDisplay) {
- flow.iperfData.samples.push(null);
- }
var iperfPath = d3.select(document.getElementById(makeSelectedFlowKey(flow))).select('path');
- iperfPath.attr('d', makeGraph());
- flow.iperfData.samples.shift();
+ iperfPath.attr('d', makeGraph(flow.iperfData));
+ flow.iperfData.now += interval/1000;
}
}, interval);
var animationTimeout;
+ flow.iperfData = {
+ samples: []
+ }
+ var lastTime;
flow.iperfFetchInterval = setInterval(function () {
+ console.log('Requesting iperf data');
getIPerfData(flow, function (data) {
try {
- if (!flow.iperfData) {
- flow.iperfData = {
- samples: []
- };
- var i;
- for (i = 0; i < pointsToDisplay; ++i) {
- flow.iperfData.samples.push(0);
- }
- }
-
var iperfData = JSON.parse(data);
// console.log(iperfData.timestamp);
@@ -269,12 +283,38 @@
stopFlowAnimation(flowSelection);
}, updateRate*1.5);
+ var endTime = Math.floor(iperfData['end-time']*10)/10;
+
+ var startTime = endTime - (iperfData.samples.length * interval/1000);
+ // set now on the first buffer
+ if (!flow.iperfData.now) {
+ flow.iperfData.now = startTime;
+ }
+
+ console.log('iperf buffer start time: ' + startTime);
+ if (lastTime && (startTime - lastTime) > updateRate/1000) {
+ console.log('iperf buffer gap: ' + startTime + ',' + lastTime);
+ }
+ lastTime = startTime;
+
+ // clear out the old data
while (flow.iperfData.samples.length > pointsToDisplay + iperfData.samples.length) {
flow.iperfData.samples.shift();
}
+ // if the client gets too out of sync, resynchronize
+ if (Math.abs(flow.iperfData.now - startTime) > (updateRate/1000) * 1.25) {
+ flow.iperfData.now = startTime;
+ }
+
+ var time = startTime;
iperfData.samples.forEach(function (s) {
- flow.iperfData.samples.push(s);
+ var sample = {
+ time: time,
+ value: s
+ };
+ flow.iperfData.samples.push(sample);
+ time += interval/1000;
});
}
flow.iperfData.timestamp = iperfData.timestamp;
@@ -283,7 +323,7 @@
}
// console.log(data);
});
- }, updateRate/2); // over sample to avoid gaps
+ }, updateRate*.5); // over sample to avoid gaps
}
}