fix bug in iperf update and improve logging
diff --git a/web/ons-demo/js/iperf.js b/web/ons-demo/js/iperf.js
new file mode 100644
index 0000000..c2abaa7
--- /dev/null
+++ b/web/ons-demo/js/iperf.js
@@ -0,0 +1,178 @@
+var enableIPerfLog = false;
+
+function iperfLog(message) {
+ if (enableIPerfLog) {
+ console.log(message);
+ }
+}
+
+function hasIPerf(flow) {
+ return flow && flow.iperfFetchTimeout;
+}
+
+function clearIPerf(flow) {
+ iperfLog('clearing iperf interval for: ' + flow.flowId);
+ clearTimeout(flow.iperfFetchTimeout);
+ delete flow.iperfFetchTimeout;
+ clearInterval(flow.iperfDisplayInterval);
+ delete flow.iperfDisplayInterval;
+ delete flow.iperfData;
+}
+
+function startIPerfForFlow(flow) {
+ var duration = 10000; // seconds
+ var interval = 100; // ms. this is defined by the server
+ var updateRate = 3000; // ms
+ var pointsToDisplay = 1000;
+
+ function makeGraph(iperfData) {
+ var d = 'M0,0';
+
+ var now = flow.iperfData.startTime + (Date.now() - flow.iperfData.localNow)/1000;
+
+ if (iperfData.samples && iperfData.samples.length) {
+
+ var lastX;
+ var i = iperfData.samples.length - 1;
+ while (i) {
+ var sample = iperfData.samples[i];
+
+ var x = (1000 - (now - sample.time)*10);
+ // workaround for discontinuity in iperf data
+ if (x < 0) {
+ i -= 1;
+ continue;
+ }
+
+ 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;
+ }
+ d += 'L' + lastX + ',30';
+ }
+ return d;
+ }
+
+ if (flow.flowId) {
+ iperfLog('starting iperf for: ' + flow.flowId);
+ startIPerf(flow, duration, updateRate/interval);
+ flow.iperfDisplayInterval = setInterval(function () {
+ if (flow.iperfData) {
+ var iperfPath = d3.select(document.getElementById(makeSelectedFlowKey(flow))).select('path');
+ iperfPath.attr('d', makeGraph(flow.iperfData));
+ }
+
+
+ }, interval);
+
+ var animationTimeout;
+ flow.iperfData = {
+ samples: []
+ }
+
+ var lastTime;
+ function fetchData() {
+ iperfLog('Requesting iperf data');
+ var fetchTime = Date.now();
+ getIPerfData(flow, function (data) {
+ var requestTime = Date.now() - fetchTime;
+ var requestTimeMessage = 'iperf request completed in: ' + requestTime + 'ms';
+ if (requestTime > 1000) {
+ requestTimeMessage = requestTimeMessage.toUpperCase();
+ }
+ iperfLog(requestTimeMessage);
+
+ if (!flow.iperfData) {
+ iperfLog('iperf session closed for flow: ' + flow.id);
+ return;
+ }
+
+ try {
+ var iperfData = JSON.parse(data);
+
+// iperfLog(iperfData.timestamp);
+
+ // if the data is fresh
+ if (!(flow.iperfData.timestamp && iperfData.timestamp != flow.iperfData.timestamp)) {
+ if (!flow.iperfData.timestamp) {
+ iperfLog('received first iperf buffer');
+ } else {
+ iperfLog('received duplicate iperf buffer with timestamp: ' + iperfData.timestamp);
+ }
+ } else {
+ iperfLog('received new iperf buffer with timstamp: ' + iperfData.timestamp);
+
+ var flowSelection = d3.select(document.getElementById(makeFlowKey(flow)));
+ startFlowAnimation(flowSelection);
+ clearTimeout(animationTimeout);
+ // kill the animation if iperfdata stops flowing
+ animationTimeout = setTimeout(function () {
+ 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.startTime) {
+ flow.iperfData.startTime = startTime;
+ flow.iperfData.localNow = Date.now();
+ }
+
+ iperfLog('iperf buffer start time: ' + startTime);
+ if (lastTime && (startTime - lastTime) > updateRate/1000) {
+ iperfLog('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
+ var clientNow = flow.iperfData.startTime + (Date.now() - flow.iperfData.localNow)/1000;
+ if (Math.abs(clientNow - startTime) > (updateRate/1000) * 2) {
+ iperfLog('resynchronizing now: ' + clientNow + ' => ' + startTime);
+ flow.iperfData.startTime = startTime;
+ flow.iperfData.localNow = Date.now();
+ }
+
+ var time = startTime;
+ iperfData.samples.forEach(function (s) {
+ var sample = {
+ time: time,
+ value: s
+ };
+ flow.iperfData.samples.push(sample);
+ time += interval/1000;
+ });
+ }
+ flow.iperfData.timestamp = iperfData.timestamp;
+ } catch (e) {
+ iperfLog('bad iperf data: ' + data);
+ }
+ flow.iperfFetchTimeout = setTimeout(fetchData, updateRate*.25); // over sample to avoid gaps
+// iperfLog(data);
+ });
+ }
+ fetchData();
+
+ }
+}
\ No newline at end of file