mirror of
https://github.com/wisplite/BLE-mesh-sim.git
synced 2026-10-30 05:22:23 -05:00
proper connection routing!
This commit is contained in:
+18
-7
@@ -9,14 +9,25 @@
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="simContainer"></div>
|
||||
<div id="controlContainer">
|
||||
<div id="controlButtons">
|
||||
<button id="addNode" class="controlButton">Add Node</button>
|
||||
<div id="rowFlex" style="display: flex; flex-direction: row; align-items: start; justify-content: center; width: 100%; height: 100%;">
|
||||
<div id="propertyViewer">
|
||||
|
||||
</div>
|
||||
<div id="simToggles">
|
||||
<input type="checkbox" id="showRanges" class="simToggle">
|
||||
<label for="showRanges">Show Ranges</label>
|
||||
<div id="centerColumn" style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 70%; height: 100%; margin: 0 auto;">
|
||||
<div id="simContainer"></div>
|
||||
<div id="controlContainer">
|
||||
<div id="controlButtons">
|
||||
<button id="addNode" class="controlButton">Add Node</button>
|
||||
</div>
|
||||
<div id="simToggles">
|
||||
<input type="checkbox" id="showRanges" class="simToggle">
|
||||
<label for="showRanges">Show Ranges</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="connectionViewer">
|
||||
<details id="incomingConnections"><summary>Incoming Connections</summary></details>
|
||||
<details id="outgoingConnections"><summary>Outgoing Connections</summary></details>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="script.js"></script>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var UPDATE_INTERVAL = 10;
|
||||
var CONNECTION_DISTANCE = 250;
|
||||
var DROP_PENTALTY = 1.2;
|
||||
|
||||
var nodes = new vis.DataSet([
|
||||
]);
|
||||
@@ -41,9 +42,42 @@ var options = {
|
||||
var network = new vis.Network(container, data, options);
|
||||
let selectedNode = null;
|
||||
|
||||
function nodeSelectUpdateHandler(properties) {
|
||||
// get containers
|
||||
const incomingTable = document.getElementById('incomingConnections');
|
||||
const outgoingTable = document.getElementById('outgoingConnections');
|
||||
|
||||
// clear containers
|
||||
let connectionElements = incomingTable.querySelectorAll('.connectionElement');
|
||||
for (let connection of connectionElements) {
|
||||
connection.remove();
|
||||
}
|
||||
connectionElements = outgoingTable.querySelectorAll('.connectionElement');
|
||||
for (let connection of connectionElements) {
|
||||
connection.remove();
|
||||
}
|
||||
|
||||
try {
|
||||
for (let connection of nodeTable[properties.nodes[0]].outgoing) {
|
||||
const connectionElement = document.createElement('div');
|
||||
connectionElement.classList.add('connectionElement');
|
||||
connectionElement.innerHTML = connection;
|
||||
outgoingTable.appendChild(connectionElement);
|
||||
}
|
||||
for (let connection of nodeTable[properties.nodes[0]].incoming) {
|
||||
const connectionElement = document.createElement('div');
|
||||
connectionElement.classList.add('connectionElement');
|
||||
connectionElement.innerHTML = connection;
|
||||
incomingTable.appendChild(connectionElement);
|
||||
}
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
network.on('selectNode', function(properties) {
|
||||
selectedNode = properties.nodes[0];
|
||||
if (document.getElementById('showRanges').checked) {
|
||||
selectedNode = properties.nodes[0];
|
||||
const centerPoint = network.getPosition(properties.nodes[0]);
|
||||
const centerPointDOM = network.canvasToDOM({x: centerPoint.x, y: centerPoint.y});
|
||||
const edgePointDOM = network.canvasToDOM({x: centerPoint.x + CONNECTION_DISTANCE, y: centerPoint.y});
|
||||
@@ -56,15 +90,21 @@ network.on('selectNode', function(properties) {
|
||||
rangeVis.style.height = radius * 2 + 'px';
|
||||
rangeVis.classList.add('rangeVis');
|
||||
document.getElementById('simContainer').appendChild(rangeVis);
|
||||
} else {
|
||||
const rangeVis = document.querySelector('.rangeVis');
|
||||
if (rangeVis) {
|
||||
rangeVis.remove();
|
||||
}
|
||||
}
|
||||
nodeSelectUpdateHandler(properties);
|
||||
});
|
||||
|
||||
network.on('dragging', function(properties) {
|
||||
if (properties.nodes.length > 0) {
|
||||
selectedNode = properties.nodes[0];
|
||||
}
|
||||
if (document.getElementById('showRanges').checked) {
|
||||
if (selectedNode != null) {
|
||||
if (properties.nodes.length > 0) {
|
||||
selectedNode = properties.nodes[0];
|
||||
}
|
||||
const centerPoint = network.getPosition(selectedNode);
|
||||
const centerPointDOM = network.canvasToDOM({x: centerPoint.x, y: centerPoint.y});
|
||||
const edgePointDOM = network.canvasToDOM({x: centerPoint.x + CONNECTION_DISTANCE, y: centerPoint.y});
|
||||
@@ -100,6 +140,7 @@ network.on('dragging', function(properties) {
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeSelectUpdateHandler(properties);
|
||||
});
|
||||
|
||||
network.on('zoom', function(properties) {
|
||||
@@ -118,24 +159,24 @@ network.on('zoom', function(properties) {
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeSelectUpdateHandler(properties);
|
||||
});
|
||||
|
||||
network.on('deselectNode', function(properties) {
|
||||
selectedNode = null;
|
||||
if (document.getElementById('showRanges').checked) {
|
||||
const rangeVis = document.querySelector('.rangeVis');
|
||||
if (rangeVis) {
|
||||
rangeVis.remove();
|
||||
}
|
||||
const rangeVis = document.querySelector('.rangeVis');
|
||||
if (rangeVis) {
|
||||
rangeVis.remove();
|
||||
}
|
||||
nodeSelectUpdateHandler(properties);
|
||||
});
|
||||
|
||||
// main event loop
|
||||
function main() {
|
||||
currentNodes = network.getPositions();
|
||||
currentNodesKeys = Object.keys(currentNodes);
|
||||
const currentNodes = network.getPositions();
|
||||
const currentNodesKeys = Object.keys(currentNodes);
|
||||
for (let node1 of currentNodesKeys) {
|
||||
neighbors = [];
|
||||
let neighbors = [];
|
||||
for (let node2 of currentNodesKeys) {
|
||||
if (node1 !== node2) {
|
||||
const node1data = currentNodes[node1];
|
||||
@@ -145,30 +186,84 @@ function main() {
|
||||
neighbors.push({'node': node2, 'distance': distanceToRSSI(distance), });
|
||||
} else {
|
||||
edges.remove(`${node1}->${node2}`);
|
||||
nodeTable[node1].connections.splice(nodeTable[node1].connections.indexOf(node2), 1);
|
||||
const outIdx = nodeTable[node1].outgoing.indexOf(node2);
|
||||
if (outIdx !== -1) {
|
||||
nodeTable[node1].outgoing.splice(outIdx, 1);
|
||||
}
|
||||
const inIdx = nodeTable[node2].incoming.indexOf(node1);
|
||||
if (inIdx !== -1) {
|
||||
nodeTable[node2].incoming.splice(inIdx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const scores = [];
|
||||
for (let neighbor of neighbors) {
|
||||
let connectionScore = 0;
|
||||
let dropScore = 0;
|
||||
connectionScore += (100 + neighbor.distance);
|
||||
connectionScore += 100/nodeTable[neighbor.node].connections.length;
|
||||
if (nodeTable[neighbor.node].connections.length == 0) {
|
||||
connectionScore += 100;
|
||||
connectionScore += 100 / (nodeTable[neighbor.node].incoming.length + 1);
|
||||
if (nodeTable[neighbor.node].incoming.length == 0) {
|
||||
connectionScore += 10000;
|
||||
}
|
||||
if (nodeTable[neighbor.node].connections.length > 3) {
|
||||
if (nodeTable[neighbor.node].incoming.length >= 3) {
|
||||
continue;
|
||||
}
|
||||
scores.push({'node': neighbor.node, 'score': connectionScore});
|
||||
if (nodeTable[node1].outgoing.includes(neighbor.node)) {
|
||||
dropScore = connectionScore;
|
||||
}
|
||||
scores.push({'node': neighbor.node, 'score': connectionScore, 'dropScore': dropScore});
|
||||
}
|
||||
scores.sort((a, b) => b.score - a.score);
|
||||
top3 = scores.slice(0, 3);
|
||||
const neighborMap = new Map(neighbors.map(n => [n.node, n]));
|
||||
const top3 = scores.slice(0, 3);
|
||||
for (let score of top3) {
|
||||
if (nodeTable[node1].connections.includes(score.node)) {
|
||||
if (nodeTable[node1].outgoing.length >= 3) {
|
||||
let dropped = false;
|
||||
if (nodeTable[node1].outgoing.includes(score.node)) {
|
||||
//console.log(`${score.node} is already in outgoing; skipping drop attempt`);
|
||||
continue;
|
||||
}
|
||||
for (let existingNode of [...nodeTable[node1].outgoing]) {
|
||||
const neighborInfo = neighborMap.get(existingNode);
|
||||
if (!neighborInfo) {
|
||||
//console.log(`${existingNode} not a neighbor; skipping`);
|
||||
continue;
|
||||
}
|
||||
let existingScore = 0;
|
||||
existingScore += (100 + neighborInfo.distance);
|
||||
existingScore += 100 / (nodeTable[existingNode].incoming.length + 1);
|
||||
if (nodeTable[existingNode].incoming.length == 0) {
|
||||
existingScore += 10000;
|
||||
}
|
||||
if (existingScore * DROP_PENTALTY < score.score) {
|
||||
//console.log(`Dropping ${existingNode} from ${node1} for better ${score.node}`);
|
||||
const outIdx = nodeTable[node1].outgoing.indexOf(existingNode);
|
||||
if (outIdx !== -1) {
|
||||
nodeTable[node1].outgoing.splice(outIdx, 1);
|
||||
}
|
||||
const inIdx = nodeTable[existingNode].incoming.indexOf(node1);
|
||||
if (inIdx !== -1) {
|
||||
nodeTable[existingNode].incoming.splice(inIdx, 1);
|
||||
}
|
||||
edges.remove(`${node1}->${existingNode}`);
|
||||
dropped = true;
|
||||
break;
|
||||
} else {
|
||||
//console.log(`Not dropping ${existingNode} from ${node1}; score ${existingScore.toFixed(2)} >= candidate ${score.score.toFixed(2)}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// still at capacity and nothing was dropped — skip adding this candidate
|
||||
if (nodeTable[node1].outgoing.length >= 3 && !dropped) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (nodeTable[node1].outgoing.includes(score.node) || nodeTable[score.node].incoming.includes(node1)) {
|
||||
continue;
|
||||
}
|
||||
nodeTable[node1].connections.push(score.node);
|
||||
nodeTable[node1].outgoing.push(score.node);
|
||||
nodeTable[score.node].incoming.push(node1);
|
||||
connectNodes(node1, score.node);
|
||||
}
|
||||
}
|
||||
@@ -179,5 +274,5 @@ main();
|
||||
document.getElementById('addNode').addEventListener('click', function() {
|
||||
const nodeId = generateRealisticLabel();
|
||||
nodes.add({id: nodeId, label: nodeId});
|
||||
nodeTable[nodeId] = {'connections': []};
|
||||
nodeTable[nodeId] = {'incoming': [], 'outgoing': []};
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
#simContainer {
|
||||
width: 80%;
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #000;
|
||||
@@ -10,7 +10,7 @@
|
||||
}
|
||||
|
||||
#controlContainer {
|
||||
width: 80%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #000;
|
||||
@@ -23,6 +23,14 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#connectionViewer, #propertyViewer {
|
||||
width: 15%;
|
||||
height: 90%;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #000;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.controlButton {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
Reference in New Issue
Block a user