blob: c2abaa7824f2fb4a0167125c4a0368123cbbe9e3 [file] [log] [blame]
Paul Greyson17f84472013-04-12 14:39:21 -07001var enableIPerfLog = false;
2
3function iperfLog(message) {
4 if (enableIPerfLog) {
5 console.log(message);
6 }
7}
8
9function hasIPerf(flow) {
10 return flow && flow.iperfFetchTimeout;
11}
12
13function clearIPerf(flow) {
14 iperfLog('clearing iperf interval for: ' + flow.flowId);
15 clearTimeout(flow.iperfFetchTimeout);
16 delete flow.iperfFetchTimeout;
17 clearInterval(flow.iperfDisplayInterval);
18 delete flow.iperfDisplayInterval;
19 delete flow.iperfData;
20}
21
22function startIPerfForFlow(flow) {
23 var duration = 10000; // seconds
24 var interval = 100; // ms. this is defined by the server
25 var updateRate = 3000; // ms
26 var pointsToDisplay = 1000;
27
28 function makeGraph(iperfData) {
29 var d = 'M0,0';
30
31 var now = flow.iperfData.startTime + (Date.now() - flow.iperfData.localNow)/1000;
32
33 if (iperfData.samples && iperfData.samples.length) {
34
35 var lastX;
36 var i = iperfData.samples.length - 1;
37 while (i) {
38 var sample = iperfData.samples[i];
39
40 var x = (1000 - (now - sample.time)*10);
41 // workaround for discontinuity in iperf data
42 if (x < 0) {
43 i -= 1;
44 continue;
45 }
46
47 var y = 28 * sample.value/1000000;
48 if (y > 28) {
49 y = 28;
50 }
51 if (i == iperfData.samples.length - 1) {
52 d = 'M' + x + ',30';
53 }
54
55 // handle gaps
56 // 1.5 for rounding error
57 if (lastX && lastX - x > 1.5) {
58 d += 'L' + lastX + ',30';
59 d += 'M' + x + ',30'
60 }
61 lastX = x;
62
63 d += 'L' + x + ',' + (30-y);
64
65 i -= 1;
66 }
67 d += 'L' + lastX + ',30';
68 }
69 return d;
70 }
71
72 if (flow.flowId) {
73 iperfLog('starting iperf for: ' + flow.flowId);
74 startIPerf(flow, duration, updateRate/interval);
75 flow.iperfDisplayInterval = setInterval(function () {
76 if (flow.iperfData) {
77 var iperfPath = d3.select(document.getElementById(makeSelectedFlowKey(flow))).select('path');
78 iperfPath.attr('d', makeGraph(flow.iperfData));
79 }
80
81
82 }, interval);
83
84 var animationTimeout;
85 flow.iperfData = {
86 samples: []
87 }
88
89 var lastTime;
90 function fetchData() {
91 iperfLog('Requesting iperf data');
92 var fetchTime = Date.now();
93 getIPerfData(flow, function (data) {
94 var requestTime = Date.now() - fetchTime;
95 var requestTimeMessage = 'iperf request completed in: ' + requestTime + 'ms';
96 if (requestTime > 1000) {
97 requestTimeMessage = requestTimeMessage.toUpperCase();
98 }
99 iperfLog(requestTimeMessage);
100
101 if (!flow.iperfData) {
102 iperfLog('iperf session closed for flow: ' + flow.id);
103 return;
104 }
105
106 try {
107 var iperfData = JSON.parse(data);
108
109// iperfLog(iperfData.timestamp);
110
111 // if the data is fresh
112 if (!(flow.iperfData.timestamp && iperfData.timestamp != flow.iperfData.timestamp)) {
113 if (!flow.iperfData.timestamp) {
114 iperfLog('received first iperf buffer');
115 } else {
116 iperfLog('received duplicate iperf buffer with timestamp: ' + iperfData.timestamp);
117 }
118 } else {
119 iperfLog('received new iperf buffer with timstamp: ' + iperfData.timestamp);
120
121 var flowSelection = d3.select(document.getElementById(makeFlowKey(flow)));
122 startFlowAnimation(flowSelection);
123 clearTimeout(animationTimeout);
124 // kill the animation if iperfdata stops flowing
125 animationTimeout = setTimeout(function () {
126 stopFlowAnimation(flowSelection);
127 }, updateRate*1.5);
128
129 var endTime = Math.floor(iperfData['end-time']*10)/10;
130
131 var startTime = endTime - (iperfData.samples.length * interval/1000);
132 // set now on the first buffer
133 if (!flow.iperfData.startTime) {
134 flow.iperfData.startTime = startTime;
135 flow.iperfData.localNow = Date.now();
136 }
137
138 iperfLog('iperf buffer start time: ' + startTime);
139 if (lastTime && (startTime - lastTime) > updateRate/1000) {
140 iperfLog('iperf buffer gap: ' + (startTime - lastTime) );
141 }
142 lastTime = startTime;
143
144 // clear out the old data
145 while (flow.iperfData.samples.length > pointsToDisplay + iperfData.samples.length) {
146 flow.iperfData.samples.shift();
147 }
148
149 // if the client gets too out of sync, resynchronize
150 var clientNow = flow.iperfData.startTime + (Date.now() - flow.iperfData.localNow)/1000;
151 if (Math.abs(clientNow - startTime) > (updateRate/1000) * 2) {
152 iperfLog('resynchronizing now: ' + clientNow + ' => ' + startTime);
153 flow.iperfData.startTime = startTime;
154 flow.iperfData.localNow = Date.now();
155 }
156
157 var time = startTime;
158 iperfData.samples.forEach(function (s) {
159 var sample = {
160 time: time,
161 value: s
162 };
163 flow.iperfData.samples.push(sample);
164 time += interval/1000;
165 });
166 }
167 flow.iperfData.timestamp = iperfData.timestamp;
168 } catch (e) {
169 iperfLog('bad iperf data: ' + data);
170 }
171 flow.iperfFetchTimeout = setTimeout(fetchData, updateRate*.25); // over sample to avoid gaps
172// iperfLog(data);
173 });
174 }
175 fetchData();
176
177 }
178}