Skip to content

Commit

Permalink
Show connections in a ring graph
Browse files Browse the repository at this point in the history
  • Loading branch information
iduartgomez committed Dec 1, 2023
1 parent ba6ecce commit d683821
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 23 deletions.
6 changes: 3 additions & 3 deletions network-monitor/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ <h1 class="title">Freenet Network Monitor</h1>
<div class="container main" style="position: relative">
<div
class="box"
id="peer-connections"
id="peer-details"
style="display: none; position: absolute; z-index: 1"
>
<div id="peer-connections-h" class="block"></div>
<h2 class="title is-3">Neighbours</h2>
<div id="peer-details-h" class="block"></div>
<h2 class="title is-3" id="neighbours-t">Neighbours</h2>
<table
id="peer-connections-t"
class="table is-striped block is-bordered"
Expand Down
2 changes: 2 additions & 0 deletions network-monitor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"author": "",
"license": "MIT+APACHE-2.0",
"devDependencies": {
"@types/d3": "^7.4.3",
"@types/jest": "^29.5.10",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
Expand All @@ -25,6 +26,7 @@
"webpack-dev-server": "^4.15.1"
},
"dependencies": {
"d3": "^7.8.5",
"flatbuffers": "^23.5.26"
}
}
225 changes: 206 additions & 19 deletions network-monitor/src/topology.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as fbTopology from "./generated/topology";
import * as d3 from "d3";

export let peers: PeerList = {};

Expand Down Expand Up @@ -122,7 +123,9 @@ export function handleAddedConnection(
peers[fromFullId].currentLocation = added.fromLocation;
}

if (!peers[fromFullId].connections.some((conn) => conn.id === to)) {
if (
!peers[fromFullId].connections.some((conn) => conn.id.full === to.full)
) {
peers[fromFullId].connections.push(toConnection);
}
} else {
Expand All @@ -146,8 +149,10 @@ export function handleAddedConnection(
peers[toFullId].currentLocation = added.toLocation;
}

if (!peers[toFullId].connections.some((conn) => conn.id === from)) {
peers[toFullId].connections.push(toConnection);
if (
!peers[toFullId].connections.some((conn) => conn.id.full === from.full)
) {
peers[toFullId].connections.push(fromConnection);
}
} else {
peers[toFullId] = {
Expand Down Expand Up @@ -227,7 +232,7 @@ export function handleRemovedConnection(
}

function updateTable() {
const peerConnectionsDiv = document.getElementById("peer-connections")!;
const peerConnectionsDiv = document.getElementById("peer-details")!;

const table = document.getElementById("peers-table")!;

Expand Down Expand Up @@ -286,13 +291,12 @@ function updateTable() {
}

const modalBox = document.createElement("div");
modalBox.classList.add("box");
modalBox.classList.add("box", "column");
modalBox.style.overflowWrap = "break-word";
modalBox.style.whiteSpace = "normal";

const peerData = document.getElementById("peer-connections")!.innerHTML;
modalBox.innerHTML = peerData;

const currentDetails = document.getElementById("peer-details")!.innerHTML;
modalBox.innerHTML = currentDetails;
// Make the table width 100% to ensure it fits within the modal box
modalBox.querySelectorAll("table").forEach((table) => {
if (table) {
Expand All @@ -312,8 +316,36 @@ function updateTable() {
containerDiv.appendChild(modal);
modal.addEventListener("click", () => {
modal.style.display = "none";
const graphContainer = d3.select("#peer-conns-graph");
graphContainer.selectAll("*").remove();
modal.remove();
});

// Build the neightbours information section
const neightboursDiv = document.createElement("div");
neightboursDiv.classList.add("columns");

const graphDiv = document.createElement("div");
graphDiv.id = "peer-conns-graph";
graphDiv.classList.add("column");
graphDiv.style.display = "flex";
graphDiv.style.justifyContent = "center";
graphDiv.style.alignItems = "center";
const scaleFactor = 1.25; // Replace this with your actual scale factor
graphDiv.style.padding = `${10 * scaleFactor}px`; // Replace 10 with your actual padding
neightboursDiv.appendChild(graphDiv);

const peerConnectionsT = modalBox.querySelector("#peer-connections-t")!;
peerConnectionsT.classList.add("column");
neightboursDiv.appendChild(peerConnectionsT);

const graphContainer = d3.select(graphDiv);
ringVisualization(peerData, graphContainer, scaleFactor);

// Insert the new div before peer-connections-t in modalBox
modalBox
.querySelector("#neighbours-t")!
.insertAdjacentElement("afterend", neightboursDiv);
});

const id = document.createElement("td");
Expand Down Expand Up @@ -404,20 +436,177 @@ function tableSorting(header: HTMLTableCellElement, index: number) {

export function showPeerData(peer: Peer) {
const id = peer.id;
const connections: Connection[] = peer.connections ?? [];
peer.connections.sort((a, b) => a.location - b.location);

// Set title
const peerDataHeader = document.getElementById("peer-connections-h")!;
const peerDataHeader = document.getElementById("peer-details-h")!;
peerDataHeader.innerHTML = `
<div class="block">
<b>Peer Id</b>: ${id}</br>
<b>Peer Id</b>: ${id.full}</br>
<b>Location</b>: ${peer.currentLocation ?? ""}
</div>
`;

// Sort connections by location
connections.sort((a, b) => a.location - b.location);
listPeerConnections(peer.connections);
displayHistory(peer);
}

function ringVisualization(
peer: Peer,
graphContainer: d3.Selection<any, any, any, any>,
scaleFactor: number
) {
const width = 200;
const height = 200;

const svg = graphContainer
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%")
.attr("transform", `scale(${scaleFactor})`);

// Set up the ring
const radius = 75;
const centerX = width / 2;
const centerY = height / 2;

const referencePoint = { value: peer.currentLocation, legend: peer.id.short };
const dataPoints = peer.connections.map((conn) => {
return { value: conn.location, legend: conn.id.short };
});

svg
.append("circle")
.attr("cx", centerX)
.attr("cy", centerY)
.attr("r", radius)
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "none");

const tooltip = graphContainer
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px");

// Create points along the ring with legends
svg
.append("circle")
.attr("class", "fixed-point")
.attr("cx", centerX + radius * Math.cos(referencePoint.value * 2 * Math.PI))
.attr("cy", centerY + radius * Math.sin(referencePoint.value * 2 * Math.PI))
.attr("r", 5)
.attr("fill", "red");

function showLegend(
this: SVGCircleElement,
e: MouseEvent,
d: { value: number; legend: string }
) {
const circleRect = this.getBoundingClientRect();
const mouseX = e.clientX - circleRect.left; // Get the mouse's x position within the circle
const mouseY = e.clientY - circleRect.top; // Get the mouse's y position within the circle
tooltip
.style("visibility", "visible")
.style("position", "absolute")
.style("stroke", "black")
.style("z-index", "2")
.html(`<b>${d.legend}</b>: ${d.value}`);
const tooltipRect = tooltip.node()!.getBoundingClientRect();
tooltip
.style("left", circleRect.left + mouseX - tooltipRect.width / 1.5 + "px")
.style("top", circleRect.top + mouseY - tooltipRect.height / 1.5 + "px");
d3.select(this).style("stroke", "black");
}

function hideLegend(this: SVGCircleElement) {
tooltip.style("visibility", "hidden");
d3.select(this).style("stroke", "none");
}

function circleId(d: { value: number; legend: string }, i: number) {
return `circle-${i}-${d.legend}`;
}

svg
.selectAll(".point")
.data(dataPoints)
.enter()
.append("circle")
.attr("class", "point")
.attr("id", (d, i) => circleId(d, i))
.attr("cx", (d) => centerX + radius * Math.cos(d.value * 2 * Math.PI))
.attr("cy", (d) => centerY + radius * Math.sin(d.value * 2 * Math.PI))
.attr("r", 5)
.attr("fill", "steelblue")
.on("mouseover", showLegend)
.on("mousemove", showLegend)
.on("mouseout", hideLegend);

// Connect all points to the fixed point (0.3) with distances as tooltips

function showDistance(
this: SVGLineElement,
e: MouseEvent,
d: { value: number; legend: string }
) {
const distance = getDistance(referencePoint.value, d.value).toFixed(5);
const lineRect = this.getBoundingClientRect();
const mouseX = e.clientX - lineRect.left; // Get the mouse's x position within the line
const mouseY = e.clientY - lineRect.top; // Get the mouse's y position within the line
tooltip
.style("visibility", "visible")
.style("position", "absolute")
.style("z-index", "2")
.html(`<b>Distance</b>: ${distance}`);
const tooltipRect = tooltip.node()!.getBoundingClientRect();
tooltip
.style(
"left",
lineRect.left + window.scrollX + mouseX - tooltipRect.width + "px"
)
.style(
"top",
lineRect.top + window.scrollY + mouseY - tooltipRect.height / 1.5 + "px"
);
d3.select(this).style("stroke", "black");
}

function hideDistance(this: SVGLineElement) {
tooltip.style("visibility", "hidden");
d3.select(this).style("stroke", "gray");
}

function getDistance(point1: number, point2: number) {
const diff = Math.abs(point1 - point2);
return Math.min(diff, 1 - diff);
}

svg
.selectAll(".edge")
.data(dataPoints)
.enter()
.append("line")
.attr("class", "edge")
.attr("x1", centerX + radius * Math.cos(referencePoint.value * 2 * Math.PI))
.attr("y1", centerY + radius * Math.sin(referencePoint.value * 2 * Math.PI))
.attr("x2", (d) => centerX + radius * Math.cos(d.value * 2 * Math.PI))
.attr("y2", (d) => centerY + radius * Math.sin(d.value * 2 * Math.PI))
.attr("stroke", "gray")
.attr("stroke-width", 1.5)
.on("mouseover", showDistance)
.on("mousemove", showDistance)
.on("mouseout", hideDistance);
}

function listPeerConnections(connections: Connection[]) {
// Find the existing peer connections table
const tableBody = document.getElementById("peer-connections-b")!;

Expand All @@ -440,22 +629,20 @@ export function showPeerData(peer: Peer) {
connections.forEach((connection) => {
const row = document.createElement("tr");
const idCell = document.createElement("td");
idCell.textContent = connection.id.short;
idCell.textContent = "..." + connection.id.short;
const locationCell = document.createElement("td");
locationCell.textContent = connection.location.toString() ?? "";
row.appendChild(idCell);
row.appendChild(locationCell);
tableBody.appendChild(row);
});

displayHistory(peer);
}

function displayHistory(peer: Peer) {
const peerConnections = document.getElementById("peer-connections")!;
const peerDetails = document.getElementById("peer-details")!;

// Remove the existing table if it exists
const existingTable = peerConnections.querySelector("#connection-history");
const existingTable = peerDetails.querySelector("#connection-history");
if (existingTable) {
existingTable.remove();
}
Expand Down Expand Up @@ -517,5 +704,5 @@ function displayHistory(peer: Peer) {
table.appendChild(tbody);

// Append the new table to the peerConnections element
peerConnections.appendChild(table);
peerDetails.appendChild(table);
}
2 changes: 1 addition & 1 deletion network-monitor/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require("path");

module.exports = {
mode: "production",
mode: "development",
entry: {
app: path.resolve(__dirname, "src", "app.ts"),
},
Expand Down

0 comments on commit d683821

Please sign in to comment.