Skip to content

Commit 4897e28

Browse files
feat: add interface setters on edge (#203)
Co-authored-by: pgallino <[email protected]> Co-authored-by: Pedro Gallino <[email protected]>
1 parent b5bc74c commit 4897e28

File tree

8 files changed

+173
-26
lines changed

8 files changed

+173
-26
lines changed

src/graphics/basic_components/label.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export class Label {
66
constructor(label: string, tooltip?: string, className?: string) {
77
this.labelElement = document.createElement("label");
88
this.labelElement.innerHTML = `<strong>${label}</strong>`; // Use innerHTML for consistency
9+
this.labelElement.classList.add("label-dark");
910

1011
if (className) {
1112
this.labelElement.classList.add(className); // Add optional CSS class

src/graphics/renderables/edge_info.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TOOLTIP_KEYS } from "../../utils/constants/tooltips_constants";
55
import { Button } from "../basic_components/button";
66
import { CSS_CLASSES } from "../../utils/constants/css_constants";
77
import { BaseInfo } from "./base_info";
8+
import { Dropdown } from "../basic_components/dropdown";
89

910
export class EdgeInfo extends BaseInfo {
1011
readonly edge: Edge;
@@ -13,6 +14,7 @@ export class EdgeInfo extends BaseInfo {
1314
super(TOOLTIP_KEYS.EDGE_INFORMATION);
1415
this.edge = edge;
1516
this.addCommonInfoFields();
17+
this.addInterfaceDropdowns();
1618
}
1719

1820
protected addCommonInfoFields(): void {
@@ -83,4 +85,62 @@ export class EdgeInfo extends BaseInfo {
8385
// Add the button to the inputFields array
8486
this.inputFields.push(deleteEdgeButton.toHTML());
8587
}
88+
89+
protected addInterfaceDropdowns(): void {
90+
const from = this.edge.data.from.id;
91+
const to = this.edge.data.to.id;
92+
93+
// Dropdown for selecting the interface for "from" device
94+
const fromIfaceDropdown = new Dropdown({
95+
label: `Interface (Device ${from})`,
96+
tooltip: TOOLTIP_KEYS.IFACE_EDITOR,
97+
options: [
98+
{
99+
value: this.edge.data.from.iface.toString(),
100+
text: `eth${this.edge.data.from.iface}`,
101+
},
102+
...this.edge
103+
.getDeviceFreeIfaces(from)
104+
.filter((iface) => iface !== this.edge.data.from.iface)
105+
.map((iface) => ({ value: iface.toString(), text: `eth${iface}` })),
106+
],
107+
default_text: "Iface",
108+
onchange: (value) => {
109+
const iface = parseInt(value, 10);
110+
this.edge.setInterface(from, iface);
111+
console.log(`Updated interface for device ${from}: eth${iface}`);
112+
this.edge.showInfo();
113+
},
114+
});
115+
116+
fromIfaceDropdown.setValue(this.edge.data.from.iface.toString());
117+
118+
// Dropdown for selecting the interface for "to" device
119+
const toIfaceDropdown = new Dropdown({
120+
label: `Interface (Device ${to})`,
121+
tooltip: TOOLTIP_KEYS.IFACE_EDITOR,
122+
options: [
123+
{
124+
value: this.edge.data.to.iface.toString(),
125+
text: `eth${this.edge.data.to.iface}`,
126+
},
127+
...this.edge
128+
.getDeviceFreeIfaces(to)
129+
.filter((iface) => iface !== this.edge.data.to.iface)
130+
.map((iface) => ({ value: iface.toString(), text: `eth${iface}` })),
131+
],
132+
default_text: "Iface",
133+
onchange: (value) => {
134+
const iface = parseInt(value, 10);
135+
this.edge.setInterface(to, iface);
136+
console.log(`Updated interface for device ${to}: eth${iface}`);
137+
this.edge.showInfo();
138+
},
139+
});
140+
141+
toIfaceDropdown.setValue(this.edge.data.to.iface.toString());
142+
143+
// Add the dropdowns to the inputFields array
144+
this.inputFields.push(fromIfaceDropdown.toHTML(), toIfaceDropdown.toHTML());
145+
}
86146
}

src/styles/label.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88
text-align: center; /* Aligns the label text to the center */
99
overflow: hidden; /* Hides text that doesn't fit */
1010
}
11+
12+
.label-dark {
13+
color: #2b2b2b;
14+
}

src/types/edge.ts

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,25 @@ import { Colors, ZIndexLevels } from "../utils/utils";
77
import { Packet } from "./packet";
88
import { EdgeInfo } from "../graphics/renderables/edge_info";
99
import { DataEdge, DeviceId } from "./graphs/datagraph";
10+
import { MacAddress } from "../packets/ethernet";
1011

1112
export class Edge extends Graphics {
12-
data: DataEdge;
13+
private _data: DataEdge;
1314
private startPos: Point;
1415
private endPos: Point;
1516

1617
viewgraph: ViewGraph;
1718

19+
// This is to always have the same data as the datagraph
20+
get data(): DataEdge {
21+
return this.viewgraph
22+
.getDataGraph()
23+
.getConnection(this._data.from.id, this._data.to.id);
24+
}
25+
1826
constructor(viewgraph: ViewGraph, edgeData: DataEdge) {
1927
super();
20-
this.data = edgeData;
28+
this._data = edgeData;
2129
this.viewgraph = viewgraph;
2230

2331
this.eventMode = "static";
@@ -105,20 +113,9 @@ export class Edge extends Graphics {
105113
RightBar.getInstance().renderInfo(edgeInfo);
106114
}
107115

108-
// Method to delete the edge
109-
delete() {
110-
// Remove the edge from the viewgraph and datagraph
111-
const n1 = this.data.from.id;
112-
const n2 = this.data.to.id;
113-
this.viewgraph.removeEdge(n1, n2);
114-
console.log(`Edge ${n1},${n2} deleted.`);
115-
this.destroy();
116-
}
117-
118-
destroy(): DataEdge {
116+
destroy(): void {
119117
deselectElement();
120118
super.destroy();
121-
return this.data;
122119
}
123120

124121
makeVisible() {
@@ -191,4 +188,39 @@ export class Edge extends Graphics {
191188

192189
this.drawEdge(newStartPos, newEndPos, Colors.Lightblue);
193190
}
191+
192+
setInterface(deviceId: DeviceId, iface: number) {
193+
if (this.data.from.id === deviceId) {
194+
this.data.from.iface = iface;
195+
} else {
196+
this.data.to.iface = iface;
197+
}
198+
this.viewgraph.getDataGraph().regenerateAllRoutingTables();
199+
}
200+
201+
setInterfaceMac(deviceId: DeviceId, mac: string): void {
202+
let iface;
203+
if (this.data.from.id === deviceId) {
204+
iface = this.data.from.iface;
205+
} else {
206+
iface = this.data.to.iface;
207+
}
208+
const device = this.viewgraph.getDataGraph().getDevice(deviceId);
209+
210+
if (!device) {
211+
console.error(`Device with ID ${deviceId} not found.`);
212+
return;
213+
}
214+
device.interfaces[iface].mac = MacAddress.parse(mac);
215+
console.log(
216+
`Updated MAC address for device ${deviceId}, interface ${iface}: ${mac}`,
217+
);
218+
}
219+
220+
getDeviceFreeIfaces(deviceId: DeviceId): number[] {
221+
const freeIfaces = this.viewgraph
222+
.getDataGraph()
223+
.getFreeInterfaces(deviceId);
224+
return freeIfaces;
225+
}
194226
}

src/types/graphs/datagraph.ts

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export class DataGraph {
165165
const { id, vertex, edges } = removedData;
166166
this.deviceGraph.setVertex(id, vertex);
167167
edges.forEach((edge) => {
168-
this.deviceGraph.setEdge(edge.from.id, edge.to.id, edge);
168+
this.reAddEdge(edge);
169169
});
170170
this.notifyChanges();
171171
return id;
@@ -199,31 +199,60 @@ export class DataGraph {
199199
const { from, to } = edgeData;
200200
const n1Id = from.id;
201201
const n2Id = to.id;
202+
202203
if (n1Id === n2Id) {
203204
console.warn(
204205
`Cannot create a connection between the same device (ID ${n1Id}).`,
205206
);
206207
return null;
207208
}
208-
if (!this.deviceGraph.hasVertex(n1Id)) {
209-
console.warn(`Device with ID ${n1Id} does not exist.`);
209+
210+
const device1 = this.getDevice(n1Id);
211+
const device2 = this.getDevice(n2Id);
212+
213+
if (!device1 || !device2) {
214+
console.warn(`One or both devices do not exist: ${n1Id}, ${n2Id}`);
210215
return null;
211216
}
212-
if (!this.deviceGraph.hasVertex(n2Id)) {
213-
console.warn(`Device with ID ${n2Id} does not exist.`);
214-
return null;
217+
218+
// Check if the interfaces are already in use
219+
const isIface1InUse =
220+
this.getConnectionsInInterface(n1Id, from.iface)?.length > 0;
221+
const isIface2InUse =
222+
this.getConnectionsInInterface(n2Id, to.iface)?.length > 0;
223+
224+
// If interface 1 is in use, find the next free interface
225+
if (isIface1InUse) {
226+
const nextFreeIface1 = this.getNextFreeInterfaceNumber(device1);
227+
if (nextFreeIface1 !== null) {
228+
edgeData.from.iface = nextFreeIface1;
229+
} else {
230+
console.error(`No free interfaces available for device ${n1Id}.`);
231+
return null;
232+
}
233+
}
234+
235+
// If interface 2 is in use, find the next free interface
236+
if (isIface2InUse) {
237+
const nextFreeIface2 = this.getNextFreeInterfaceNumber(device2);
238+
if (nextFreeIface2 !== null) {
239+
edgeData.to.iface = nextFreeIface2;
240+
} else {
241+
console.error(`No free interfaces available for device ${n2Id}.`);
242+
return null;
243+
}
215244
}
245+
246+
// Add the edge to the graph
216247
if (this.deviceGraph.hasEdge(n1Id, n2Id)) {
217248
console.warn(
218249
`Connection between ID ${n1Id} and ID ${n2Id} already exists.`,
219250
);
220251
return null;
221252
}
253+
222254
this.deviceGraph.setEdge(n1Id, n2Id, edgeData);
223255

224-
console.log(
225-
`Connection created between devices ID: ${n1Id} and ID: ${n2Id}`,
226-
);
227256
this.notifyChanges();
228257
this.regenerateAllRoutingTables();
229258
return edgeData;
@@ -279,6 +308,23 @@ export class DataGraph {
279308
return null; // Return null if no free interface is found
280309
}
281310

311+
getFreeInterfaces(deviceId: DeviceId): number[] {
312+
const device = this.getDevice(deviceId);
313+
if (!device) {
314+
console.warn(`Device with ID ${deviceId} not found.`);
315+
return [];
316+
}
317+
const numberOfInterfaces = getNumberOfInterfaces(device.getType());
318+
const freeInterfaces: number[] = [];
319+
for (let i = 0; i < numberOfInterfaces; i++) {
320+
const connections = this.getConnectionsInInterface(deviceId, i);
321+
if (!connections || connections.length === 0) {
322+
freeInterfaces.push(i); // Add the free interface to the list
323+
}
324+
}
325+
return freeInterfaces; // Return the list of free interfaces
326+
}
327+
282328
updateDevicePosition(id: DeviceId, newValues: { x?: number; y?: number }) {
283329
const deviceDataNode = this.deviceGraph.getVertex(id);
284330
if (!deviceDataNode) {

src/types/graphs/viewgraph.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ export class ViewGraph {
361361
// Remove connection in DataGraph
362362
this.datagraph.removeConnection(n1Id, n2Id);
363363

364-
return this._removeEdge(n1Id, n2Id);
364+
this._removeEdge(n1Id, n2Id);
365+
return datagraphEdge;
365366
}
366367

367368
/**
@@ -383,7 +384,7 @@ export class ViewGraph {
383384
console.log(
384385
`Edge with ID ${n1Id},${n2Id} successfully removed from ViewGraph.`,
385386
);
386-
return edge.destroy();
387+
edge.destroy();
387388
}
388389

389390
getViewport() {

src/types/undo-redo/moves/addRemoveEdge.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export abstract class AddRemoveEdgeMove extends BaseMove {
7979
const n1 = this.state.connectedNodes.n1;
8080
const n2 = this.state.connectedNodes.n2;
8181
const edgeData = viewgraph.removeEdge(n1, n2);
82-
82+
console.log("Edge data", edgeData);
8383
if (!edgeData) {
8484
return false;
8585
}

src/utils/constants/tooltips_constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const TOOLTIP_KEYS = {
5959
PROGRAM_RUNNER: "Program Runner",
6060
EDGE_INFORMATION: "Edge Information",
6161
PACKET_INFORMATION: "Packet Information",
62+
IFACE_EDITOR: "iface-editor",
6263
} as const;
6364

6465
// Tooltip Content
@@ -169,4 +170,6 @@ export const TOOLTIP_CONTENT = {
169170
"An edge represents a connection between two devices in the network. It provides information about the devices and interfaces directly connected by this link. Edges can represent physical connections, such as cables, or logical connections in a virtualized environment.",
170171
[TOOLTIP_KEYS.PACKET_INFORMATION]:
171172
"A packet is a unit of data transmitted across the network. This field provides detailed information about the packet, including its source, destination, and the protocol used for communication.",
173+
[TOOLTIP_KEYS.IFACE_EDITOR]:
174+
"Modify the network interface used by this device.",
172175
} as const;

0 commit comments

Comments
 (0)