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