Skip to content

Commit 513dacd

Browse files
authored
feat: Make packet queue parameters configurable by the user (#182)
1 parent fa4bbbe commit 513dacd

File tree

12 files changed

+373
-8
lines changed

12 files changed

+373
-8
lines changed

src/graphics/renderables/device_info.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createRightBarButton, createRoutingTable } from "../right_bar";
99
import { ProgramInfo } from "./program_info";
1010
import { ProgramRunnerInfo } from "./program_runner_info";
1111
import { StyledInfo } from "./styled_info";
12+
import { createParameterGroup } from "./parameter_editor";
1213

1314
export { ProgramInfo } from "./program_info";
1415

@@ -86,6 +87,23 @@ export class DeviceInfo extends StyledInfo {
8687
toHTML(): Node[] {
8788
return super.toHTML().concat(this.inputFields);
8889
}
90+
91+
addParameterGroup(
92+
groupName: string,
93+
parameters: {
94+
label: string;
95+
initialValue: number | string;
96+
onChange: (newValue: number | string) => void;
97+
}[],
98+
) {
99+
const { toggleButton, borderedContainer } = createParameterGroup(
100+
groupName,
101+
parameters,
102+
);
103+
104+
this.inputFields.push(toggleButton);
105+
this.inputFields.push(borderedContainer);
106+
}
89107
}
90108

91109
function getTypeName(device: ViewDevice): string {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { TooltipManager } from "./tooltip_manager";
2+
3+
/**
4+
* Creates a parameter editor with a label and an input.
5+
* @param label - The text of the label.
6+
* @param initialValue - The initial value of the input.
7+
* @param onChange - Function that is executed when the value changes.
8+
* @returns An HTML element representing the parameter editor.
9+
*/
10+
export function createParameterEditor(
11+
label: string,
12+
initialValue: number | string,
13+
onChange: (newValue: number | string) => void,
14+
): HTMLElement {
15+
// Create the editor container
16+
const container = document.createElement("div");
17+
container.className = "parameter-editor";
18+
19+
// Create the label
20+
const labelElement = document.createElement("label");
21+
labelElement.textContent = label;
22+
labelElement.className = "parameter-editor-label";
23+
TooltipManager.getInstance().attachTooltip(labelElement, label);
24+
25+
// Create the input
26+
const input = document.createElement("input");
27+
input.type = typeof initialValue === "number" ? "number" : "text";
28+
input.value = initialValue.toString();
29+
input.className = "parameter-editor-input";
30+
31+
// Add the change event
32+
let previousValue = initialValue;
33+
34+
input.addEventListener("change", () => {
35+
const newValue =
36+
input.type === "number"
37+
? (parseFloat(input.value) as number)
38+
: (input.value as string);
39+
40+
if (input.value === "") {
41+
input.value = previousValue.toString();
42+
} else {
43+
previousValue = newValue;
44+
onChange(newValue);
45+
}
46+
});
47+
48+
// Add the label and input to the container
49+
container.appendChild(labelElement);
50+
container.appendChild(input);
51+
52+
return container;
53+
}
54+
55+
/**
56+
* Creates a parameter group with a toggle button.
57+
* @param groupName - The name of the group.
58+
* @param parameters - List of parameters with label, initial value, and onChange function.
59+
* @returns An HTML container that includes the toggle button and the parameters.
60+
*/
61+
export function createParameterGroup(
62+
groupName: string,
63+
parameters: {
64+
label: string;
65+
initialValue: number | string;
66+
onChange: (newValue: number | string) => void;
67+
}[],
68+
): { toggleButton: HTMLElement; borderedContainer: HTMLElement } {
69+
// Create a bordered container for the parameter group
70+
const borderedContainer = document.createElement("div");
71+
borderedContainer.className = "parameter-group";
72+
73+
// Create a container for the parameters
74+
const parametersContainer = document.createElement("div");
75+
parametersContainer.className = "parameter-group-parameters";
76+
77+
// Add each parameter to the container
78+
parameters.forEach(({ label, initialValue, onChange }) => {
79+
const parameterEditor = createParameterEditor(
80+
label,
81+
initialValue,
82+
onChange,
83+
);
84+
parametersContainer.appendChild(parameterEditor);
85+
});
86+
87+
// Create a toggle button
88+
const toggleButton = document.createElement("button");
89+
toggleButton.className = "right-bar-toggle-button";
90+
TooltipManager.getInstance().attachTooltip(toggleButton, groupName);
91+
toggleButton.textContent = groupName;
92+
toggleButton.addEventListener("click", () => {
93+
const isHidden = borderedContainer.style.display === "none";
94+
borderedContainer.style.display = isHidden ? "block" : "none";
95+
});
96+
97+
// Hide the parameters by default
98+
borderedContainer.style.display = "none";
99+
100+
// Add the parameter container to the bordered container
101+
borderedContainer.appendChild(parametersContainer);
102+
103+
return { toggleButton, borderedContainer };
104+
}

src/styles/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ import "./help.css";
1515
import "./config.css";
1616
import "./tooltips.css";
1717
import "./dropdown.css";
18+
import "./parameter_editor.css";

src/styles/parameter_editor.css

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
.parameter-editor {
2+
display: flex;
3+
align-items: center;
4+
justify-content: space-between; /* Aligns the label and the input */
5+
padding-bottom: 10px; /* Internal spacing below the parameter */
6+
margin-bottom: 10px; /* External spacing between parameters */
7+
border-bottom: 1px solid #ccc; /* Separator line */
8+
flex-wrap: nowrap; /* Prevents elements from wrapping to a new line */
9+
}
10+
11+
.parameter-editor:last-child {
12+
border-bottom: none; /* Removes the separator line for the last parameter */
13+
margin-bottom: 0; /* Ensures no extra margin for the last parameter */
14+
padding-bottom: 0; /* Removes internal spacing for the last parameter */
15+
}
16+
17+
.parameter-editor-label {
18+
font-weight: bold;
19+
color: #000000; /* Changes the text color to black */
20+
margin-right: 10px; /* Spacing between the label and the input */
21+
flex: 1; /* The label takes up the remaining space */
22+
text-align: left; /* Aligns the label text to the left */
23+
overflow: hidden; /* Hides text that doesn't fit */
24+
}
25+
26+
.parameter-editor-input {
27+
background-color: #2b2b2b;
28+
border: 1px solid #444;
29+
color: white;
30+
padding: 0.5vh 1vw; /* More compact internal spacing */
31+
text-align: center;
32+
font-size: 0.9rem; /* Smaller font size */
33+
border-radius: 0.5rem;
34+
width: 80px; /* Fixed width for the input */
35+
box-sizing: border-box;
36+
transition:
37+
background-color 0.3s ease,
38+
transform 0.2s ease;
39+
font-family: Arial, sans-serif;
40+
flex-shrink: 0; /* Prevents the input from shrinking */
41+
}
42+
43+
/* Removes the arrows from number input */
44+
.parameter-editor-input[type="number"]::-webkit-inner-spin-button,
45+
.parameter-editor-input[type="number"]::-webkit-outer-spin-button {
46+
-webkit-appearance: none;
47+
margin: 0;
48+
}
49+
50+
.parameter-editor-input[type="number"] {
51+
-moz-appearance: textfield; /* Removes the arrows in Firefox */
52+
}
53+
54+
.parameter-editor-input:focus {
55+
outline: none;
56+
background-color: #3a3a3a;
57+
border-color: #007bff;
58+
transform: scale(1.02);
59+
}
60+
61+
.parameter-group {
62+
background-color: #f9f9f9; /* Light background for the group */
63+
border: 1px solid #ccc; /* Group border */
64+
border-radius: 5px; /* Rounded corners */
65+
padding: 10px; /* Internal spacing */
66+
margin-bottom: 20px; /* External spacing between groups */
67+
}
68+
69+
.parameter-group-title {
70+
font-weight: bold;
71+
font-size: 1rem;
72+
color: #333; /* Text color for the title */
73+
margin-bottom: 10px; /* Spacing below the title */
74+
}

src/styles/right-bar.css

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
background-color: #f1f1f1; /* Light gray background for contrast */
55
box-shadow: -0.2vw 0 0.5vw rgba(0, 0, 0, 0.2); /* Adds subtle shadow for depth */
66
padding: 3vh; /* Adds internal spacing */
7-
overflow-y: auto; /* Enables vertical scrolling if needed */
7+
overflow-y: scroll; /* Enables vertical scrolling if needed */
88
max-height: 90vh; /* Limits the maximum height */
99
}
1010

@@ -24,3 +24,14 @@
2424
font-weight: bold; /* Makes text stand out */
2525
color: #333; /* Dark gray text */
2626
}
27+
28+
/* Hide the scrollbar in WebKit-based browsers */
29+
.right-bar::-webkit-scrollbar {
30+
display: none; /* Hides the scrollbar */
31+
}
32+
33+
/* Ensures the content remains scrollable */
34+
.right-bar {
35+
-ms-overflow-style: none; /* Hides the scrollbar in IE and Edge */
36+
scrollbar-width: none; /* Hides the scrollbar in Firefox */
37+
}

src/types/data-devices/dRouter.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,47 @@ import {
1010
import { sendDataPacket } from "../packet";
1111
import { DataNetworkDevice } from "./dNetworkDevice";
1212
import { EthernetFrame } from "../../packets/ethernet";
13+
import { ROUTER_CONSTANTS } from "../../utils/constants/router_constants";
1314

1415
export class DataRouter extends DataNetworkDevice {
15-
private packetQueue = new PacketQueue(1024);
16+
packetQueueSize: number;
17+
private packetQueue: PacketQueue;
1618
// Time in ms to process a single byte
17-
private timePerByte = 8;
19+
timePerByte: number;
1820
// Number of bytes processed
1921
private processingProgress = 0;
2022
routingTable: RoutingTableEntry[];
2123

2224
constructor(graphData: RouterDataNode, datagraph: DataGraph) {
2325
super(graphData, datagraph);
26+
this.packetQueueSize =
27+
graphData.packetQueueSize ?? ROUTER_CONSTANTS.PACKET_QUEUE_MAX_SIZE;
28+
this.packetQueue = new PacketQueue(this.packetQueueSize);
29+
this.timePerByte =
30+
graphData.timePerByte ?? ROUTER_CONSTANTS.PROCESSING_SPEED;
2431
this.routingTable = graphData.routingTable ?? [];
32+
console.log("packetQueueSize Dr", this.packetQueueSize);
33+
console.log("processingSpeed Dr", this.timePerByte);
34+
}
35+
36+
setMaxQueueSize(newSize: number) {
37+
this.packetQueue.setMaxQueueSize(newSize);
38+
console.log("Max queue size set to Dr", newSize);
39+
this.packetQueueSize = newSize;
40+
}
41+
42+
setTimePerByte(newTime: number) {
43+
this.timePerByte = newTime;
44+
console.log("Time per byte set to Dr", newTime);
2545
}
2646

2747
getDataNode(): RouterDataNode {
2848
return {
2949
...super.getDataNode(),
3050
type: DeviceType.Router,
3151
routingTable: this.routingTable,
52+
packetQueueSize: this.packetQueue.getMaxQueueSize(),
53+
timePerByte: this.timePerByte,
3254
};
3355
}
3456

@@ -146,6 +168,14 @@ class PacketQueue {
146168
this.maxQueueSizeBytes = maxQueueSizeBytes;
147169
}
148170

171+
setMaxQueueSize(newSize: number) {
172+
this.maxQueueSizeBytes = newSize;
173+
}
174+
175+
getMaxQueueSize(): number {
176+
return this.maxQueueSizeBytes;
177+
}
178+
149179
enqueue(packet: IPv4Packet) {
150180
if (this.queueSizeBytes >= this.maxQueueSizeBytes) {
151181
return false;

src/types/graphs/datagraph.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export interface NetworkDataNode extends CommonDataNode {
3535
export interface RouterDataNode extends NetworkDataNode {
3636
type: DeviceType.Router;
3737
routingTable?: RoutingTableEntry[];
38+
packetQueueSize: number;
39+
timePerByte: number;
3840
}
3941

4042
export interface RoutingTableEntry {
@@ -146,6 +148,8 @@ export class DataGraph {
146148
dataNode = {
147149
...dataNode,
148150
routingTable: device.routingTable,
151+
packetQueueSize: device.packetQueueSize,
152+
timePerByte: device.timePerByte,
149153
};
150154
} else if (device instanceof DataHost) {
151155
dataNode = {

src/types/graphs/graph.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export class Graph<Vertex, Edge> {
2626
}
2727

2828
getVertex(id: VertexId): Vertex | undefined {
29-
console.log("Vertices in the graph:", this.vertices);
3029
return this.vertices.get(id);
3130
}
3231

src/types/view-devices/utils.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { GlobalContext } from "../../context";
22
import { MacAddress } from "../../packets/ethernet";
33
import { IpAddress } from "../../packets/ip";
44
import { Position } from "../common";
5-
import { DataNode, isNetworkNode } from "../graphs/datagraph";
5+
import { DataNode, isNetworkNode, isRouter } from "../graphs/datagraph";
66
import { ViewGraph } from "../graphs/viewgraph";
77
import { ViewDevice, DeviceType } from "./vDevice";
88
import { ViewHost } from "./vHost";
@@ -25,6 +25,14 @@ export function createViewDevice(
2525
ip = IpAddress.parse(deviceInfo.ip);
2626
mask = IpAddress.parse(deviceInfo.mask);
2727
}
28+
29+
let packetQueueSize: number;
30+
let timePerByte: number;
31+
32+
if (isRouter(deviceInfo)) {
33+
packetQueueSize = deviceInfo.packetQueueSize;
34+
timePerByte = deviceInfo.timePerByte;
35+
}
2836
switch (deviceInfo.type) {
2937
case DeviceType.Router:
3038
return new ViewRouter(
@@ -35,6 +43,8 @@ export function createViewDevice(
3543
mac,
3644
ip,
3745
mask,
46+
packetQueueSize,
47+
timePerByte,
3848
);
3949
case DeviceType.Host:
4050
return new ViewHost(

0 commit comments

Comments
 (0)