mirror of
https://github.com/wisplite/BLE-mesh-sim.git
synced 2026-10-30 13:32:21 -05:00
HUGE update. New settings location, new styles, better dark mode, console, and ROUTING!!!
This commit is contained in:
+37
-14
@@ -43,30 +43,44 @@
|
|||||||
<input type="number" id="simSpeed" value="1" min="0.1" step="0.1" autocomplete="off">
|
<input type="number" id="simSpeed" value="1" min="0.1" step="0.1" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
<details id="visualizationSettings">
|
||||||
|
<summary>Visualization Settings</summary>
|
||||||
|
<div id="visualizationSettingsContent">
|
||||||
|
<div id="visualizationSettingsToggles">
|
||||||
|
<input type="checkbox" id="showRanges">
|
||||||
|
<label for="showRanges">Show Ranges</label>
|
||||||
|
</div>
|
||||||
|
<div id="visualizationSettingsToggles">
|
||||||
|
<input type="checkbox" id="showTTL" checked>
|
||||||
|
<label for="showTTL">Show TTL Colors</label>
|
||||||
|
</div>
|
||||||
|
<div id="visualizationSettingsToggles">
|
||||||
|
<input type="checkbox" id="darkMode" checked>
|
||||||
|
<label for="darkMode">Dark Mode</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
<div style="display: flex; flex-grow: 1; height: auto;"></div>
|
<div style="display: flex; flex-grow: 1; height: auto;"></div>
|
||||||
<hr style="width: 100%;"/>
|
<hr style="width: 100%;"/>
|
||||||
<div id="darkModeContainer">
|
<div id="darkModeContainer">
|
||||||
<input type="checkbox" id="darkMode" class="simToggle">
|
<input type="checkbox" id="quickPlace" class="simToggle">
|
||||||
<label for="darkMode">Dark Mode</label>
|
<label for="quickPlace">Quick Place</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="centerColumn" style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 70%; height: 100%; margin: 0 auto;">
|
<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="simContainer"></div>
|
||||||
|
|
||||||
|
<div id="routingProgressContainer" style="display: none;">
|
||||||
|
<progress id="routingProgress" value="0" max="100"></progress>
|
||||||
|
</div>
|
||||||
<div id="controlContainer">
|
<div id="controlContainer">
|
||||||
<div id="controlButtons">
|
<div id="controlButtons">
|
||||||
<button id="addNode" class="controlButton">Add Node</button>
|
<button id="addNode" class="controlButton">Add Node</button>
|
||||||
<button id="sendPacket" class="controlButton">Send Packet</button>
|
<button id="sendPacket" class="controlButton">Send Flood Packet</button>
|
||||||
<button id="quickPacket" class="controlButton">Quick Packet</button>
|
<button id="quickPacket" class="controlButton">Quick Flood Packet</button>
|
||||||
<button id="resetTTLColors" class="controlButton">Reset TTL Colors</button>
|
<button id="resetTTLColors" class="controlButton">Reset TTL Colors</button>
|
||||||
<button id="createRandomGraph" class="controlButton">Create Random Network</button>
|
<button id="createRoutingTables" class="controlButton">Create Routing Tables</button>
|
||||||
</div>
|
<button id="createRandomGraph" class="controlButton">Create Network</button>
|
||||||
<div id="simToggles">
|
|
||||||
<input type="checkbox" id="showRanges" class="simToggle">
|
|
||||||
<label for="showRanges">Show Ranges</label>
|
|
||||||
<input type="checkbox" id="showTTL" class="simToggle" checked>
|
|
||||||
<label for="showTTL">Show TTL Colors</label>
|
|
||||||
<input type="checkbox" id="quickPlace" class="simToggle">
|
|
||||||
<label for="quickPlace">Quick Place</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +92,10 @@
|
|||||||
<div id="randomGraphModalContent">
|
<div id="randomGraphModalContent">
|
||||||
<label for="randomGraphNodes">Number of Nodes</label>
|
<label for="randomGraphNodes">Number of Nodes</label>
|
||||||
<input type="number" id="randomGraphNodes" value="10" autocomplete="off">
|
<input type="number" id="randomGraphNodes" value="10" autocomplete="off">
|
||||||
<button id="createRandomGraphConfirm" class="controlButton">Create</button>
|
<div id="randomGraphModalButtons" style="display: flex; flex-direction: row; gap: 10px; justify-content: end; align-items: center;">
|
||||||
|
<button id="createRandomGraphCancel" class="controlButton" onclick="document.getElementById('randomGraphModal').style.display = 'none';">Cancel</button>
|
||||||
|
<button id="createRandomGraphConfirm" class="controlButton">Create</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="warningModal">
|
<div id="warningModal">
|
||||||
@@ -88,6 +105,12 @@
|
|||||||
<button id="warningModalConfirm" class="controlButton" onclick="document.getElementById('warningModal').style.display = 'none';">OK</button>
|
<button id="warningModalConfirm" class="controlButton" onclick="document.getElementById('warningModal').style.display = 'none';">OK</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="successModal">
|
||||||
|
<div id="successModalContent">
|
||||||
|
<p id="successModalMessage"></p>
|
||||||
|
<button id="successModalConfirm" class="controlButton" onclick="document.getElementById('successModal').style.display = 'none';">OK</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="script.js"></script>
|
<script type="text/javascript" src="script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ var TTL = 5;
|
|||||||
var MAX_OUTGOING_CONNECTIONS = 3;
|
var MAX_OUTGOING_CONNECTIONS = 3;
|
||||||
var MAX_INCOMING_CONNECTIONS = 3;
|
var MAX_INCOMING_CONNECTIONS = 3;
|
||||||
var warningModalShown = false;
|
var warningModalShown = false;
|
||||||
|
var displayWarningWhenDone = false;
|
||||||
|
var stillRouting = false;
|
||||||
|
|
||||||
function smoothColorTransition(color1, color2, min, max, current) {
|
function smoothColorTransition(color1, color2, min, max, current) {
|
||||||
// Clamp current between min and max
|
// Clamp current between min and max
|
||||||
@@ -101,6 +103,10 @@ onEdgeEngine.createEdgesTable();
|
|||||||
onEdgeEngine.initMovement();
|
onEdgeEngine.initMovement();
|
||||||
onEdgeEngine.setArrivalCallback(({ from, to, dot }) => {
|
onEdgeEngine.setArrivalCallback(({ from, to, dot }) => {
|
||||||
var ttl = parseInt(dot.id.split('-')[2]);
|
var ttl = parseInt(dot.id.split('-')[2]);
|
||||||
|
if (stillRouting) {
|
||||||
|
routePacket(to, dot.id.split('-')[3], dot.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (document.getElementById('showTTL').checked) {
|
if (document.getElementById('showTTL').checked) {
|
||||||
edges.update({id: `${from}->${to}`, color: {color: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1), highlight: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1)}});
|
edges.update({id: `${from}->${to}`, color: {color: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1), highlight: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1)}});
|
||||||
} else {
|
} else {
|
||||||
@@ -184,6 +190,97 @@ function quickPacket(startNode, packetInfo, fromNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a modified version of quickPacket that contains some extra logic to save routing tables.
|
||||||
|
// It also disables the TTL colors for unreachable nodes to save performance.
|
||||||
|
function quickRoute(startNode, packetInfo, fromNode) {
|
||||||
|
|
||||||
|
const packetId = packetInfo.split('-')[0];
|
||||||
|
const ttlStart = parseInt(packetInfo.split('-')[2]);
|
||||||
|
const originNode = packetInfo.split('-')[3];
|
||||||
|
|
||||||
|
const queue = [{ nodeId: startNode, ttl: ttlStart, from: fromNode }];
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const { nodeId, ttl, from } = queue.shift();
|
||||||
|
|
||||||
|
if (ttl < 0) {
|
||||||
|
displayWarningWhenDone = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nodeTable[nodeId]?.packetCache?.includes(packetId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeTable[nodeId].packetCache.push(packetId);
|
||||||
|
nodeTable[nodeId].routingTable[originNode] = from;
|
||||||
|
|
||||||
|
if (document.getElementById('showTTL').checked) {
|
||||||
|
nodes.update({id: nodeId, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
|
||||||
|
edges.update({id: `${from}->${nodeId}`, color: {color: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl+1), highlight: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
|
||||||
|
} else {
|
||||||
|
nodes.update({id: nodeId, color: {background: '#97c2fc'}});
|
||||||
|
edges.update({id: `${from}->${nodeId}`, color: {color: '#2b7ce9', highlight: '#2b7ce9'}});
|
||||||
|
}
|
||||||
|
|
||||||
|
const neighborsSnapshot = (nodeTable[nodeId]?.outgoing?.slice()) || [];
|
||||||
|
|
||||||
|
for (let neighbor of neighborsSnapshot) {
|
||||||
|
if (neighbor === originNode || neighbor === from) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
queue.push({ nodeId: neighbor, ttl: ttl - 1, from: nodeId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function routePacket(currentNode, goalNode, packetInfo, first=false) {
|
||||||
|
stillRouting = true;
|
||||||
|
const packetId = packetInfo.split('-')[0];
|
||||||
|
var packetShift = generateRealisticLabel();
|
||||||
|
let ttl = parseInt(packetInfo.split('-')[2]);
|
||||||
|
nodes.update({id: currentNode, color: {background: '#97c2fc'}});
|
||||||
|
nodes.update({id: goalNode, color: {background: '#ff00d9'}});
|
||||||
|
if (ttl <= 0) {
|
||||||
|
consoleLog(`Packet ${packetId} TTL expired. Dropping packet.`);
|
||||||
|
stillRouting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentNode === goalNode) {
|
||||||
|
consoleLog(`Packet ${packetId} reached ${goalNode}.`);
|
||||||
|
stillRouting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (document.getElementById('showTTL').checked) {
|
||||||
|
nodes.update({id: currentNode, color: {background: smoothColorTransition('#eb4034', '#40eb34', 0, TTL, ttl)}});
|
||||||
|
} else {
|
||||||
|
if (first) {
|
||||||
|
nodes.update({id: currentNode, color: {background: '#ff00d9'}});
|
||||||
|
} else {
|
||||||
|
nodes.update({id: currentNode, color: {background: 'yellow'}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ttl--;
|
||||||
|
const nextNode = nodeTable[currentNode].routingTable[goalNode];
|
||||||
|
if (nextNode) {
|
||||||
|
var movingNode = {
|
||||||
|
id: `${packetId}-${packetShift}-${ttl}-${goalNode}`,
|
||||||
|
label: packetId + `-${ttl}`,
|
||||||
|
shape: 'dot',
|
||||||
|
size: 10,
|
||||||
|
color: {
|
||||||
|
background: 'white',
|
||||||
|
border: 'black'
|
||||||
|
},
|
||||||
|
font: {color: document.getElementById('darkMode').checked ? '#e0e0e0' : '#000'}
|
||||||
|
};
|
||||||
|
onEdgeEngine.createDotNode(movingNode, currentNode, nextNode);
|
||||||
|
} else {
|
||||||
|
consoleLog(`No route found from ${currentNode} to ${goalNode}. Dropping packet.`);
|
||||||
|
console.log(nodeTable[currentNode].routingTable);
|
||||||
|
stillRouting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const markNeighborsAsFailed = (nodeId, from, packetId, visited = new Set()) => {
|
const markNeighborsAsFailed = (nodeId, from, packetId, visited = new Set()) => {
|
||||||
if (visited.has(nodeId)) {
|
if (visited.has(nodeId)) {
|
||||||
return;
|
return;
|
||||||
@@ -492,7 +589,7 @@ function onNodeAdd() {
|
|||||||
document.getElementById('addNode').addEventListener('click', function() {
|
document.getElementById('addNode').addEventListener('click', function() {
|
||||||
const nodeId = generateRealisticLabel();
|
const nodeId = generateRealisticLabel();
|
||||||
nodes.add({id: nodeId, label: nodeId});
|
nodes.add({id: nodeId, label: nodeId});
|
||||||
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
|
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': [], 'routingTable': {}};
|
||||||
onNodeAdd();
|
onNodeAdd();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -503,7 +600,7 @@ network.on('click', function(properties) {
|
|||||||
if (document.getElementById('quickPlace').checked) {
|
if (document.getElementById('quickPlace').checked) {
|
||||||
const nodeId = generateRealisticLabel();
|
const nodeId = generateRealisticLabel();
|
||||||
nodes.add({id: nodeId, label: nodeId, x: properties.pointer.canvas.x, y: properties.pointer.canvas.y});
|
nodes.add({id: nodeId, label: nodeId, x: properties.pointer.canvas.x, y: properties.pointer.canvas.y});
|
||||||
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
|
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': [], 'routingTable': {}};
|
||||||
onNodeAdd();
|
onNodeAdd();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -513,11 +610,12 @@ network.on('doubleClick', function(properties) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (properties.nodes.length > 0) {
|
if (properties.nodes.length > 0) {
|
||||||
|
navigator.clipboard.writeText(properties.nodes[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nodeId = generateRealisticLabel();
|
const nodeId = generateRealisticLabel();
|
||||||
nodes.add({id: nodeId, label: nodeId, x: properties.pointer.canvas.x, y: properties.pointer.canvas.y});
|
nodes.add({id: nodeId, label: nodeId, x: properties.pointer.canvas.x, y: properties.pointer.canvas.y});
|
||||||
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
|
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': [], 'routingTable': {}};
|
||||||
onNodeAdd();
|
onNodeAdd();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -607,8 +705,8 @@ document.getElementById('createRandomGraphConfirm').addEventListener('click', fu
|
|||||||
let nodeCounter = 0;
|
let nodeCounter = 0;
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
const nodeId = generateRealisticLabel();
|
const nodeId = generateRealisticLabel();
|
||||||
nodes.add({id: nodeId, label: nodeId, x: Math.random() * 100, y: Math.random() * 100});
|
nodes.add({id: nodeId, label: nodeId, x: Math.random() * 1000, y: Math.random() * 1000});
|
||||||
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': []};
|
nodeTable[nodeId] = {'incoming': [], 'outgoing': [], 'packetCache': [], 'routingTable': {}};
|
||||||
onNodeAdd();
|
onNodeAdd();
|
||||||
nodeCounter++;
|
nodeCounter++;
|
||||||
if (nodeCounter >= nodeCount) {
|
if (nodeCounter >= nodeCount) {
|
||||||
@@ -646,6 +744,36 @@ document.getElementById('quickPacket').addEventListener('click', async function(
|
|||||||
quickPacket(selectedNode, `${packetId}-${packetShift}-${ttl}-${selectedNode}`, selectedNode);
|
quickPacket(selectedNode, `${packetId}-${packetShift}-${ttl}-${selectedNode}`, selectedNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('createRoutingTables').addEventListener('click', function() {
|
||||||
|
let nodeCounter = 0;
|
||||||
|
document.getElementById('routingProgressContainer').style.display = 'flex';
|
||||||
|
document.getElementById('routingProgress').value = 0;
|
||||||
|
document.getElementById('routingProgress').max = Object.keys(nodeTable).length;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const packetId = generateRealisticLabel();
|
||||||
|
var packetShift = generateRealisticLabel();
|
||||||
|
let ttl = TTL;
|
||||||
|
const nodeId = Object.keys(nodeTable)[nodeCounter];
|
||||||
|
quickRoute(nodeId, `${packetId}-${packetShift}-${ttl}-${nodeId}`, nodeId);
|
||||||
|
nodeCounter++;
|
||||||
|
document.getElementById('routingProgress').value = nodeCounter;
|
||||||
|
if (nodeCounter >= Object.keys(nodeTable).length) {
|
||||||
|
clearInterval(interval);
|
||||||
|
document.getElementById('routingProgressContainer').style.display = 'none';
|
||||||
|
document.getElementById('routingProgress').value = 0;
|
||||||
|
if (displayWarningWhenDone) {
|
||||||
|
displayWarningWhenDone = false;
|
||||||
|
document.getElementById('warningModal').style.display = 'block';
|
||||||
|
document.getElementById('warningModalMessage').innerHTML = 'TTL hit zero while routing, some routes may be missing. If you experience issues, try increasing the TTL and routing again.';
|
||||||
|
document.getElementById('warningModalCustomContent').innerHTML = '';
|
||||||
|
} else {
|
||||||
|
document.getElementById('successModal').style.display = 'block';
|
||||||
|
document.getElementById('successModalMessage').innerHTML = 'Routing tables created successfully.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
if (localStorage.getItem('darkMode') === 'true') {
|
if (localStorage.getItem('darkMode') === 'true') {
|
||||||
document.getElementById('darkMode').checked = true;
|
document.getElementById('darkMode').checked = true;
|
||||||
@@ -654,4 +782,115 @@ window.onload = function() {
|
|||||||
document.getElementById('darkMode').checked = false;
|
document.getElementById('darkMode').checked = false;
|
||||||
document.getElementById('styleSheet').href = 'style.css';
|
document.getElementById('styleSheet').href = 'style.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let consoleContainer = document.createElement('div');
|
||||||
|
consoleContainer.id = 'consoleContainer';
|
||||||
|
document.getElementById('simContainer').appendChild(consoleContainer);
|
||||||
|
|
||||||
|
let openConsoleButton = document.createElement('button');
|
||||||
|
openConsoleButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-terminal-icon lucide-terminal"><path d="M12 19h8"/><path d="m4 17 6-6-6-6"/></svg>';
|
||||||
|
openConsoleButton.id = 'openConsole';
|
||||||
|
openConsoleButton.addEventListener('click', function() {
|
||||||
|
const consoleElement = document.getElementById('consoleContainer');
|
||||||
|
const currentDisplay = window.getComputedStyle(consoleElement).display;
|
||||||
|
consoleElement.style.display = currentDisplay === 'none' ? 'flex' : 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
let consoleInputContainer = document.createElement('div');
|
||||||
|
consoleInputContainer.id = 'consoleInputContainer';
|
||||||
|
consoleContainer.appendChild(consoleInputContainer);
|
||||||
|
|
||||||
|
let consoleCarrot = document.createElement('pre');
|
||||||
|
consoleCarrot.innerHTML = '>';
|
||||||
|
consoleInputContainer.appendChild(consoleCarrot);
|
||||||
|
|
||||||
|
let consoleInput = document.createElement('input');
|
||||||
|
consoleInput.id = 'consoleInput';
|
||||||
|
consoleInput.placeholder = 'Enter command';
|
||||||
|
consoleInputContainer.appendChild(consoleInput);
|
||||||
|
let consoleInputHistory = [];
|
||||||
|
let consoleInputHistoryIndex = 0;
|
||||||
|
// Handle Enter key for command execution
|
||||||
|
consoleInput.addEventListener('keypress', function(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
const command = consoleInput.value.trim();
|
||||||
|
if (command) { // Only add non-empty commands to history
|
||||||
|
consoleInputHistory.push(command);
|
||||||
|
}
|
||||||
|
consoleInputHistoryIndex = consoleInputHistory.length;
|
||||||
|
|
||||||
|
const rootCommand = command.split(' ')[0];
|
||||||
|
const args = command.split(' ')[1];
|
||||||
|
|
||||||
|
switch (rootCommand) {
|
||||||
|
case 'help':
|
||||||
|
switch (args) {
|
||||||
|
case 'routePacket':
|
||||||
|
consoleLog('Sends a packet from one specific node to another. Usage: routePacket [startNode]->[endNode]');
|
||||||
|
break;
|
||||||
|
case 'exit':
|
||||||
|
consoleLog('Exits the console');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
consoleLog('Available commands: help, routePacket, exit');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'routePacket':
|
||||||
|
if (!args) {
|
||||||
|
consoleLog('Usage: routePacket [startNode]->[endNode]');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
document.getElementById('resetTTLColors').click();
|
||||||
|
const packetInfo = `${generateRealisticLabel()}-${generateRealisticLabel()}-${TTL}-${args.split('->')[1]}`;
|
||||||
|
nodes.update({id: args.split('->')[0], color: {background: '#ff00d9'}});
|
||||||
|
nodes.update({id: args.split('->')[1], color: {background: '#ff00d9'}});
|
||||||
|
routePacket(args.split('->')[0], args.split('->')[1], packetInfo, true);
|
||||||
|
break;
|
||||||
|
case 'exit':
|
||||||
|
document.getElementById('consoleContainer').style.display = 'none';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
consoleLog(`Unknown command "${rootCommand}"`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
consoleInput.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle arrow keys for history navigation
|
||||||
|
consoleInput.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'ArrowUp') {
|
||||||
|
event.preventDefault(); // Prevent cursor movement
|
||||||
|
if (consoleInputHistory.length === 0) return;
|
||||||
|
|
||||||
|
if (consoleInputHistoryIndex > 0) {
|
||||||
|
consoleInputHistoryIndex--;
|
||||||
|
}
|
||||||
|
consoleInput.value = consoleInputHistory[consoleInputHistoryIndex] || '';
|
||||||
|
} else if (event.key === 'ArrowDown') {
|
||||||
|
event.preventDefault(); // Prevent cursor movement
|
||||||
|
if (consoleInputHistory.length === 0) return;
|
||||||
|
|
||||||
|
consoleInputHistoryIndex++;
|
||||||
|
if (consoleInputHistoryIndex >= consoleInputHistory.length) {
|
||||||
|
consoleInputHistoryIndex = consoleInputHistory.length;
|
||||||
|
consoleInput.value = ''; // Clear input when going beyond history
|
||||||
|
} else {
|
||||||
|
consoleInput.value = consoleInputHistory[consoleInputHistoryIndex] || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let consoleOutput = document.createElement('pre');
|
||||||
|
consoleOutput.id = 'consoleOutput';
|
||||||
|
consoleContainer.appendChild(consoleOutput);
|
||||||
|
|
||||||
|
document.getElementById('simContainer').appendChild(openConsoleButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
function consoleLog(message) {
|
||||||
|
const consoleOutput = document.getElementById('consoleOutput');
|
||||||
|
consoleOutput.innerHTML = `${message}`;
|
||||||
|
consoleOutput.scrollTop = consoleOutput.scrollHeight;
|
||||||
}
|
}
|
||||||
+102
-6
@@ -93,7 +93,7 @@ html, body {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#simpleSettingsContent, #engineSettingsContent, #hardwareConstraintsContent {
|
#simpleSettingsContent, #engineSettingsContent, #hardwareConstraintsContent, #visualizationSettingsContent {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -101,21 +101,21 @@ html, body {
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#simpleSettings, #hardwareConstraints, #engineSettings {
|
#simpleSettings, #hardwareConstraints, #engineSettings, #visualizationSettings {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#randomGraphModal, #warningModal {
|
#randomGraphModal, #warningModal, #successModal {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0%;
|
top: 0%;
|
||||||
left: 0%;
|
left: 0%;
|
||||||
transform: translate(0%, 0%);
|
transform: translate(0%, 0%);
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
padding: 20px;
|
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
#warningModalContent {
|
#warningModalContent {
|
||||||
@@ -131,6 +131,19 @@ html, body {
|
|||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#successModalContent {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
border: 2px solid #00ff00;
|
||||||
|
border-radius: 8px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: #e0e0e0;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
#randomGraphModalContent {
|
#randomGraphModalContent {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #2d2d2d;
|
background-color: #2d2d2d;
|
||||||
@@ -144,6 +157,16 @@ html, body {
|
|||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#createRandomGraphCancel {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
#createRandomGraphConfirm {
|
#createRandomGraphConfirm {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
background-color: #4a9eff;
|
background-color: #4a9eff;
|
||||||
@@ -205,4 +228,77 @@ button:active {
|
|||||||
/* Checkbox and radio button styling */
|
/* Checkbox and radio button styling */
|
||||||
input[type="checkbox"], input[type="radio"] {
|
input[type="checkbox"], input[type="radio"] {
|
||||||
accent-color: #4a9eff;
|
accent-color: #4a9eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#routingProgressContainer {
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#routingProgress {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openConsole {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleContainer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 10;
|
||||||
|
width: 100%;
|
||||||
|
height: 10%;
|
||||||
|
background-color: #000000;
|
||||||
|
border-top: 1px solid #ffffff;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleInput {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #e0e0e0;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleInput:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleInputContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
gap: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#consoleOutput {
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #e0e0e0;
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ html, body {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 1000;
|
z-index: 10;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
background-color: red;
|
background-color: red;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -73,7 +73,7 @@ html, body {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#simpleSettingsContent, #engineSettingsContent, #hardwareConstraintsContent {
|
#simpleSettingsContent, #engineSettingsContent, #hardwareConstraintsContent, #visualizationSettingsContent {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -81,21 +81,21 @@ html, body {
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#simpleSettings, #hardwareConstraints, #engineSettings {
|
#simpleSettings, #hardwareConstraints, #engineSettings, #visualizationSettings {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#randomGraphModal, #warningModal {
|
#randomGraphModal, #warningModal, #successModal {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0%;
|
top: 0%;
|
||||||
left: 0%;
|
left: 0%;
|
||||||
transform: translate(0%, 0%);
|
transform: translate(0%, 0%);
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
#warningModalContent {
|
#warningModalContent {
|
||||||
@@ -108,6 +108,16 @@ html, body {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#successModalContent {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #00ff00;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
#randomGraphModalContent {
|
#randomGraphModalContent {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@@ -118,6 +128,79 @@ html, body {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
#createRandomGraphConfirm {
|
#createRandomGraphConfirm, #createRandomGraphCancel {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#routingProgressContainer {
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#routingProgress {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openConsole {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleContainer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 10;
|
||||||
|
width: 100%;
|
||||||
|
height: 10%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-top: 1px solid #000000;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleInput {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleInput:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#consoleInputContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
gap: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#consoleOutput {
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user