<%#
LuCI - Lua Configuration Interface
Copyright 2012 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
-%>
<% luci.http.prepare_content("text/html") %>
<%+header%>
<style>
.topoWarp{ position:relative; height:600px; width:800px;margin:20px auto 0;}
.topoWarp .tab{ position:absolute; top:20px; right:20px; z-index:200}
.topoWarp .tab li{ float:left; list-style:none; cursor:pointer; margin-left:10px; }
.topoWarp .tab li.current,.topoWarp .tab li:hover{ color:#0099CC;}
</style>
<div id="ID_DIV_TopoWarp" class="topoWarp">
<ul id="ID_UL_RateSignal" style="display:none" class="tab">
<li class="current">速率</li>
<li>信号</li>
</ul>
<div id="ID_DIV_Unlicensed" style="display:none" class="alert-message warning">
<h3>设备未激活</h3>
</div>
</div>
<script type="text/javascript" src="<%=resource%>/jquery-1.9.1.js?v=git-19.044.11045-4a8bfd0"></script>
<script type="text/javascript" src="<%=resource%>/d3.min.js"></script>
<!-- 全局变量定义 -->
<script type="text/javascript">
var g_svgHeight = $(".topoWarp").height(); //整个svg的高度
var g_svgWidth = $(".topoWarp").width();//整个svg的宽度
var g_interval;
var g_rectWidth = 100;
var g_rectHeight = 40;
</script>
<script type="text/javascript">//<![CDATA[
/***********************************************************
* 描述: 根据拓扑信息,生成坐标信息
* 入参: 无
* 出参: 无
* 返回: 无
***********************************************************/
function GeneratePosition(topoArray)
{
var r = 260;
var offset = g_svgHeight/2; //因为svg的原点在左上角,所以计算出来的坐标值要加上偏移
var x, y;
if(topoArray.length == 1)
{
topoArray[0].x = r;
topoArray[0].y = r;
topoArray[0].angle = 0;
}
else
{
var radians = (Math.PI / 180) * Math.round(360 / topoArray.length); //弧度
for(var i = 0; i < topoArray.length; i++)
{
x = offset + r * Math.cos(radians * i);
y = offset + r * Math.sin(radians * i);
topoArray[i].x = x;
topoArray[i].y = y;
topoArray[i].angle = i*Math.round(360 / topoArray.length);//角度
}
}
}
/***********************************************************
* 描述: 画节点
* 入参: svg - 用于画节点的画布
* 出参: 无
* 返回: 无
***********************************************************/
function DrawNodes(topoArray, svg)
{
var color = "SteelBlue";
for(i=0; i<topoArray.length; i++)
{
/* 画带弧度的矩形 */
svg.append("rect")
.attr("x", topoArray[i].x) //x坐标
.attr("y", topoArray[i].y) //y坐标
.attr("width", g_rectWidth) //宽
.attr("height", g_rectHeight) //宽
.attr("stroke", "black")
.attr("rx",10)
.attr("fill", color);
/* 在矩形中文字的坐标 */
var textX = topoArray[i].x + g_rectWidth/2; //文字的x坐标
var textY = topoArray[i].y;
/* 在矩形中显示ip和底噪 */
var text = svg.append("text")
.attr("text-anchor", "middle" )
.attr("x", textX)
.attr("y", textY)
.attr("font-size", "14");
text.append("tspan")
.attr("x", textX)
.attr("dy", "1.2em")//使用1.2,上面留一点
.text(topoArray[i].ip);
text.append("tspan")
.attr("x", textX)
.attr("dy", "1.2em") //使用1.2,上面留一点
.text("(" + topoArray[i].noise + "dBm)");
}
}
function AdjustCoordinate(topoArray, myTopoIndex, neighTopoIndex, x, y)
{
var topoObject = topoArray[myTopoIndex];
var retObject = new Object();
var adjust;
var neighCount = topoObject.neigh.length;
/* 将节点分成4个区域,315度-45度为第一个区域,以此类推 */
if((topoObject.angle >= 0 && topoObject.angle<45) || (topoObject.angle>=315 && topoObject.angle<360))
{
if(neighCount == 1) //如果只有1个邻居,则选矩形宽的中点
{
retObject.x = x;
retObject.y = y + g_rectHeight/2;
}
else
{
adjust = g_rectHeight/(neighCount-1);
retObject.x = x;
if(neighTopoIndex<myTopoIndex)
retObject.y = y + adjust*(neighCount - neighTopoIndex-1);
else
retObject.y = y + adjust*(neighCount - neighTopoIndex);
}
}
else if(topoObject.angle>=45 && topoObject.angle<125)
{
adjust = g_rectWidth/(neighCount-1);
if(neighTopoIndex<myTopoIndex)
retObject.x = x + adjust*(neighCount - neighTopoIndex - 1);
else
retObject.x = x + adjust*(neighTopoIndex-myTopoIndex-1);
retObject.y = y;
}
else if(topoObject.angle>=125 && topoObject.angle<215)
{
if(neighCount == 1) //如果只有1个邻居,则选矩形宽的中点
{
retObject.x = x + g_rectWidth;
retObject.y = y + g_rectHeight/2;
}
else
{
adjust = g_rectHeight/(neighCount-1);
retObject.x = x + g_rectWidth;
if(neighTopoIndex<myTopoIndex)
retObject.y = y + adjust*(neighTopoIndex+1);
else
retObject.y = y + adjust*(neighCount - neighTopoIndex);
}
}
else
{
adjust = g_rectWidth/(neighCount-1);
if(neighTopoIndex<myTopoIndex)
retObject.x = x + adjust*(myTopoIndex - neighTopoIndex-1);
else
retObject.x = x + adjust*(neighCount - neighTopoIndex);
retObject.y = y + g_rectHeight;
}
return retObject;
}
function DrawConnector(topoArray, svg, type)
{
var path;
var ipArray = new Array();
var id;
var textPathId;
var textOffset;
var pathColor = "SteelBlue";
path = "M2,2 L10,6 L2,10 L6,6 L2,2";
svg.append("marker")
.attr("id", "arrow")
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", "12")
.attr("markerHeight", "12")
.attr("viewBox", "0 0 12 12")
.attr("refX", "6")
.attr("refY", "6")
.attr("orient", "auto")
.append("path")
.attr("d", path)
.attr("fill", pathColor);
for(var i=0; i<topoArray.length; i++)
{
ipArray.push(topoArray[i].ip);
}
for(var i=0; i<topoArray.length; i++)
{
for(var j=0; j<topoArray[i].neigh.length; j++)
{
var fromIndex = ipArray.indexOf(topoArray[i].neigh[j].ip);
var adjustObject;
var fromX, fromY, toX, toY;
var adjustX, adjustY;
fromX = topoArray[fromIndex].x;
fromY = topoArray[fromIndex].y;
toX = topoArray[i].x;
toY = topoArray[i].y;
adjustObject = AdjustCoordinate(topoArray, fromIndex, i, fromX, fromY);
fromX = Math.round(adjustObject.x);//取整,避免小数不同
fromY = Math.round(adjustObject.y);//取整,避免小数不同
adjustObject = AdjustCoordinate(topoArray, i, fromIndex, toX, toY);
toX = Math.round(adjustObject.x);//取整,避免小数不同
toY = Math.round(adjustObject.y);//取整,避免小数不同
if(fromX == toX)
{
if(fromY > toY)
{
adjustX = 40;
adjustY = 0;
fromX += 10;
toX += 10;
}
else
{
adjustX = -40;
adjustY = 0;
}
}
else if(fromX < toX)
{
adjustX = 0;
adjustY = 40;
if(fromY == toY)
{
fromY += 5;
toY += 5;
}
else if(fromY > toY)
{
toX += 10;
}
else
{
fromX -= 10;
}
}
else
{
adjustX = 0;
adjustY = -40;
if(fromY == toY)
{
//什么都不做
}
else if(fromY < toY)
{
toX -= 10;
}
else
{
fromX += 10
}
}
/* 画箭头 */
path = "M" + fromX + ',' + fromY + " S" + ((fromX + toX)/2 + adjustX) + ',' + ((fromY + toY) / 2 + adjustY) + "," + toX + ',' + toY;
id = "ID_PATH_" + fromIndex + '_' + i;
svg.append("path")
.attr("id", id)
.attr("d", path)
.attr("fill", "none") //只画线,不填充
.attr("stroke", pathColor)
.attr("stroke-width", 2)
.attr("marker-end", "url(#arrow)");
if(fromX > toX)
{
textPathId = "ID_PATH_Text" + i + "_" + fromIndex;
path = "M" + toX + ',' + toY + " S" + ((fromX + toX)/2 + adjustX) + ',' + ((fromY + toY) / 2 + adjustY) + "," + fromX + ',' + fromY;
svg.append("defs")
.append("path")
.attr("id", textPathId)
.attr("d", path);
textOffset = "30%";
}
else
{
textPathId = id;
textOffset = "80%";
}
svg.append("text")
.append("textPath")
// 给textPath设置path的引用
.attr("xlink:href", d => {
return "#"+textPathId;
})
.style("text-anchor", "end")
.attr("startOffset", textOffset)
.style("fill", pathColor)
.style("font-size", "16px")
.text(text => {
if(type == "rate")
return topoArray[i].neigh[j].rate + "Mbps";
else
return topoArray[i].neigh[j].signal + "dBm";
});
}
}
}
/***********************************************************
* 描述: 根据节点的坐标和拓扑信息画图
* 入参: 无
* 出参: 无
* 返回: 无
***********************************************************/
function DrawAll(topoArray, type)
{
if ($("#ID_SVG_Topo").length)
$("#ID_SVG_Topo").remove();
var svg = d3.select('#ID_DIV_TopoWarp')
.append("svg")
.attr("id", "ID_SVG_Topo")
.attr("width", g_svgWidth) //设定宽度
.attr("height", g_svgHeight); //设定高度;
DrawNodes(topoArray, svg);
DrawConnector(topoArray, svg, type);
return;
}
/***********************************************************
* 描述: 定时器处理函数,定时获取拓扑数据并刷新拓扑图
* 入参: type - 当前显示速率还是信号强度
* 出参: 无
* 返回: 无
***********************************************************/
function IntervalHandle(type)
{
XHR.get('<%=luci.dispatcher.build_url("gettopology/topo/json")%>', null,
function(x, data)
{
var topoArray = data.results.topology;
if(data.status == "success" || data.status == "working")
{
$("#ID_DIV_Unlicensed").hide();
$("#ID_UL_RateSignal").show();
GeneratePosition(topoArray);
//quneeJson(topoArray, type);
DrawAll(topoArray, type);
}
else if(data.status == "unlicensed")
{
$("#ID_DIV_Unlicensed").show();
$("#ID_UL_RateSignal").hide();
}
}
);
}
/***********************************************************
* 描述: 速率和信号强度点击响应函数
* 入参: 无
* 出参: 无
* 返回: 无
***********************************************************/
$(".topoWarp .tab li").click(function()
{
var refreshTime = 3000; //每3秒刷新一次
$(".topoWarp .tab li").removeClass("current");
$(this).addClass("current");
clearInterval(g_interval);
if($(this).index() == 0)
{
IntervalHandle("rate");
g_interval = setInterval(function(){ IntervalHandle("rate")}, refreshTime);
}
else
{
IntervalHandle("signal");
g_interval = setInterval(function(){ IntervalHandle("signal") }, refreshTime);
}
})
/* 第一次进入页面模拟点击 */
$(".topoWarp .tab li:first").click();
//]]>
</script>
<%+footer%>