ieeeColors = ({
navy: "#2C3E50",
teal: "#16A085",
orange: "#E67E22",
gray: "#7F8C8D",
red: "#c0392b",
blue: "#3498db",
green: "#27ae60",
purple: "#8e44ad",
yellow: "#f39c12",
darkBg: "#1a1a2e",
lightBg: "#f8f9fa"
})
// Traffic class definitions with properties
trafficClasses = [
{
id: "critical",
name: "Critical",
description: "Alarms & Alerts",
color: "#c0392b",
priority: 0,
defaultRate: 5,
defaultBurst: 2,
dscp: "EF",
minBandwidth: 10
},
{
id: "high",
name: "High",
description: "Control Commands",
color: "#E67E22",
priority: 1,
defaultRate: 15,
defaultBurst: 5,
dscp: "AF41",
minBandwidth: 20
},
{
id: "medium",
name: "Medium",
description: "Telemetry Data",
color: "#16A085",
priority: 2,
defaultRate: 40,
defaultBurst: 10,
dscp: "AF21",
minBandwidth: 30
},
{
id: "low",
name: "Low",
description: "Firmware Updates",
color: "#3498db",
priority: 3,
defaultRate: 25,
defaultBurst: 15,
dscp: "AF11",
minBandwidth: 15
},
{
id: "besteffort",
name: "Best Effort",
description: "Background Traffic",
color: "#7F8C8D",
priority: 4,
defaultRate: 15,
defaultBurst: 20,
dscp: "BE",
minBandwidth: 5
}
]
// QoS mechanism definitions
qosMechanisms = [
{
id: "pq",
name: "Priority Queuing",
abbr: "PQ",
description: "Strict priority - higher classes always served first"
},
{
id: "wfq",
name: "Weighted Fair Queuing",
abbr: "WFQ",
description: "Bandwidth allocated by weight, fair distribution"
},
{
id: "shaping",
name: "Traffic Shaping",
abbr: "TS",
description: "Smooth traffic bursts, delay excess packets"
},
{
id: "ratelimit",
name: "Rate Limiting",
abbr: "RL",
description: "Drop packets exceeding configured rate"
}
]
// Predefined scenarios
scenarios = [
{
id: "normal",
name: "Normal Operation",
bandwidth: 100,
latency: 20,
jitter: 5,
packetLoss: 0.5,
trafficMultiplier: { critical: 1, high: 1, medium: 1, low: 1, besteffort: 1 }
},
{
id: "congestion",
name: "Network Congestion",
bandwidth: 40,
latency: 150,
jitter: 50,
packetLoss: 5,
trafficMultiplier: { critical: 1, high: 1.5, medium: 2, low: 2, besteffort: 3 }
},
{
id: "critical-flood",
name: "Critical Event Flood",
bandwidth: 80,
latency: 30,
jitter: 10,
packetLoss: 1,
trafficMultiplier: { critical: 10, high: 2, medium: 1, low: 0.5, besteffort: 0.5 }
},
{
id: "bulk",
name: "Bulk Transfer",
bandwidth: 100,
latency: 50,
jitter: 15,
packetLoss: 2,
trafficMultiplier: { critical: 0.5, high: 0.5, medium: 1, low: 5, besteffort: 5 }
}
]
// Simulation constants
simConfig = ({
maxQueueSize: 100,
packetSizeMin: 64,
packetSizeMax: 1500,
updateInterval: 50,
historyLength: 60,
canvasWidth: 800,
canvasHeight: 500
})
// ----------------------------------------------------------------------------
// SECTION 2: STATE MANAGEMENT
// ----------------------------------------------------------------------------
// Mutable state for simulation
mutable simState = ({
running: false,
time: 0,
packets: [],
queues: {
critical: [],
high: [],
medium: [],
low: [],
besteffort: []
},
metrics: {
critical: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
high: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
medium: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
low: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
besteffort: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 }
},
history: [],
lastUpdate: Date.now()
})
// Current configuration state
mutable currentConfig = ({
mechanism: "pq",
scenario: "normal",
bandwidth: 100,
latency: 20,
jitter: 5,
packetLoss: 0.5,
speed: 1,
trafficRates: {
critical: 5,
high: 15,
medium: 40,
low: 25,
besteffort: 15
},
burstSizes: {
critical: 2,
high: 5,
medium: 10,
low: 15,
besteffort: 20
}
})
// ----------------------------------------------------------------------------
// SECTION 3: PACKET AND QUEUE MANAGEMENT
// ----------------------------------------------------------------------------
// Generate a new packet for a traffic class
function generatePacket(classId, config, time) {
const tc = trafficClasses.find(c => c.id === classId);
const size = Math.floor(Math.random() * (simConfig.packetSizeMax - simConfig.packetSizeMin) + simConfig.packetSizeMin);
return {
id: `${classId}-${time}-${Math.random().toString(36).substr(2, 9)}`,
classId: classId,
priority: tc.priority,
color: tc.color,
size: size,
createdAt: time,
enqueuedAt: null,
dequeuedAt: null,
deliveredAt: null,
dropped: false,
x: 0,
y: 0,
state: "generated" // generated, queued, transmitting, delivered, dropped
};
}
// Calculate queue processing based on mechanism
function processQueues(queues, mechanism, bandwidth, deltaTime) {
const results = {
processed: [],
dropped: []
};
// Available bandwidth in bytes for this time slice
const availableBytes = (bandwidth * 1000 * deltaTime) / 8; // Kbps to bytes
let remainingBytes = availableBytes;
switch (mechanism) {
case "pq":
// Priority Queuing: Process highest priority first
for (const classId of ["critical", "high", "medium", "low", "besteffort"]) {
while (queues[classId].length > 0 && remainingBytes > 0) {
const packet = queues[classId][0];
if (packet.size <= remainingBytes) {
queues[classId].shift();
packet.state = "transmitting";
results.processed.push(packet);
remainingBytes -= packet.size;
} else {
break;
}
}
}
break;
case "wfq":
// Weighted Fair Queuing: Allocate bandwidth by weight
const weights = { critical: 30, high: 25, medium: 20, low: 15, besteffort: 10 };
const totalWeight = Object.values(weights).reduce((a, b) => a + b, 0);
for (const classId of Object.keys(weights)) {
const classBytes = (availableBytes * weights[classId]) / totalWeight;
let classRemaining = classBytes;
while (queues[classId].length > 0 && classRemaining > 0) {
const packet = queues[classId][0];
if (packet.size <= classRemaining) {
queues[classId].shift();
packet.state = "transmitting";
results.processed.push(packet);
classRemaining -= packet.size;
remainingBytes -= packet.size;
} else {
break;
}
}
}
break;
case "shaping":
// Traffic Shaping: Smooth bursts with token bucket
const tokenRates = { critical: 0.15, high: 0.25, medium: 0.30, low: 0.20, besteffort: 0.10 };
for (const classId of Object.keys(tokenRates)) {
const classBytes = availableBytes * tokenRates[classId];
let classRemaining = classBytes;
// Only process one packet per class per interval (smoothing)
if (queues[classId].length > 0 && classRemaining > 0) {
const packet = queues[classId][0];
if (packet.size <= classRemaining) {
queues[classId].shift();
packet.state = "transmitting";
results.processed.push(packet);
remainingBytes -= packet.size;
}
}
}
break;
case "ratelimit":
// Rate Limiting: Drop packets exceeding rate
const rateLimits = { critical: 0.20, high: 0.25, medium: 0.25, low: 0.20, besteffort: 0.10 };
for (const classId of Object.keys(rateLimits)) {
const classBytes = availableBytes * rateLimits[classId];
let classRemaining = classBytes;
while (queues[classId].length > 0) {
const packet = queues[classId][0];
if (classRemaining > 0 && packet.size <= classRemaining) {
queues[classId].shift();
packet.state = "transmitting";
results.processed.push(packet);
classRemaining -= packet.size;
remainingBytes -= packet.size;
} else if (classRemaining <= 0) {
// Rate exceeded - drop packet
const dropped = queues[classId].shift();
dropped.state = "dropped";
dropped.dropped = true;
results.dropped.push(dropped);
} else {
break;
}
}
}
break;
}
return results;
}
// ----------------------------------------------------------------------------
// SECTION 4: SIMULATION ENGINE
// ----------------------------------------------------------------------------
// Main simulation update function
function updateSimulation(state, config) {
if (!state.running) return state;
const now = Date.now();
const deltaTime = Math.min((now - state.lastUpdate) / 1000, 0.1) * config.speed;
const newTime = state.time + deltaTime;
// Deep copy state for immutability
const newState = {
...state,
time: newTime,
lastUpdate: now,
queues: {
critical: [...state.queues.critical],
high: [...state.queues.high],
medium: [...state.queues.medium],
low: [...state.queues.low],
besteffort: [...state.queues.besteffort]
},
metrics: {
critical: { ...state.metrics.critical },
high: { ...state.metrics.high },
medium: { ...state.metrics.medium },
low: { ...state.metrics.low },
besteffort: { ...state.metrics.besteffort }
},
packets: [...state.packets],
history: [...state.history]
};
// Generate new packets based on traffic rates
const scenario = scenarios.find(s => s.id === config.scenario) || scenarios[0];
for (const tc of trafficClasses) {
const baseRate = config.trafficRates[tc.id] || tc.defaultRate;
const multiplier = scenario.trafficMultiplier[tc.id] || 1;
const effectiveRate = baseRate * multiplier;
// Poisson-like packet generation
const packetsToGenerate = Math.random() < (effectiveRate * deltaTime / 10) ? 1 : 0;
for (let i = 0; i < packetsToGenerate; i++) {
const packet = generatePacket(tc.id, config, newTime);
// Check for random packet loss
if (Math.random() * 100 < config.packetLoss) {
packet.dropped = true;
packet.state = "dropped";
newState.metrics[tc.id].sent++;
newState.metrics[tc.id].dropped++;
} else {
// Add to queue if not over capacity
if (newState.queues[tc.id].length < simConfig.maxQueueSize) {
packet.enqueuedAt = newTime;
packet.state = "queued";
newState.queues[tc.id].push(packet);
newState.metrics[tc.id].sent++;
} else {
// Queue full - drop packet
packet.dropped = true;
packet.state = "dropped";
newState.metrics[tc.id].sent++;
newState.metrics[tc.id].dropped++;
}
}
newState.packets.push(packet);
}
}
// Process queues based on selected mechanism
const processResult = processQueues(
newState.queues,
config.mechanism,
config.bandwidth,
deltaTime
);
// Update metrics for processed packets
for (const packet of processResult.processed) {
packet.dequeuedAt = newTime;
const latency = (packet.dequeuedAt - packet.enqueuedAt) * 1000 + config.latency;
const jitteredLatency = latency + (Math.random() - 0.5) * config.jitter * 2;
newState.metrics[packet.classId].received++;
newState.metrics[packet.classId].latencySum += jitteredLatency;
newState.metrics[packet.classId].latencyCount++;
packet.deliveredAt = newTime + jitteredLatency / 1000;
packet.state = "delivered";
}
// Update metrics for dropped packets
for (const packet of processResult.dropped) {
newState.metrics[packet.classId].dropped++;
}
// Remove old packets from visualization
newState.packets = newState.packets.filter(p =>
newTime - p.createdAt < 5 || p.state === "queued"
);
// Record history for graphs
if (newState.history.length === 0 || newTime - newState.history[newState.history.length - 1].time > 0.5) {
const historyEntry = {
time: newTime,
queueSizes: {},
throughput: {},
latency: {}
};
for (const tc of trafficClasses) {
historyEntry.queueSizes[tc.id] = newState.queues[tc.id].length;
historyEntry.throughput[tc.id] = newState.metrics[tc.id].received;
historyEntry.latency[tc.id] = newState.metrics[tc.id].latencyCount > 0
? newState.metrics[tc.id].latencySum / newState.metrics[tc.id].latencyCount
: 0;
}
newState.history.push(historyEntry);
// Keep history bounded
if (newState.history.length > simConfig.historyLength) {
newState.history.shift();
}
}
return newState;
}
// Reset simulation state
function resetSimulation() {
return {
running: false,
time: 0,
packets: [],
queues: {
critical: [],
high: [],
medium: [],
low: [],
besteffort: []
},
metrics: {
critical: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
high: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
medium: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
low: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 },
besteffort: { sent: 0, received: 0, dropped: 0, latencySum: 0, latencyCount: 0 }
},
history: [],
lastUpdate: Date.now()
};
}
// ----------------------------------------------------------------------------
// SECTION 5: VISUALIZATION RENDERING
// ----------------------------------------------------------------------------
// Draw the network topology and packet flow
function drawVisualization(canvas, state, config) {
if (!canvas) return;
const ctx = canvas.getContext("2d");
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.fillStyle = ieeeColors.darkBg;
ctx.fillRect(0, 0, width, height);
// Draw grid pattern
ctx.strokeStyle = "rgba(255,255,255,0.05)";
ctx.lineWidth = 1;
for (let x = 0; x < width; x += 40) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
for (let y = 0; y < height; y += 40) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
// Layout constants
const sourceX = 80;
const routerX = width / 2;
const destX = width - 80;
const queueWidth = 150;
const queueHeight = 30;
const queueSpacing = 45;
const startY = 80;
// Draw source node
drawNode(ctx, sourceX, height / 2, 35, "IoT Devices", ieeeColors.teal);
// Draw router node
drawRouter(ctx, routerX, height / 2, 45, config.mechanism, ieeeColors.navy);
// Draw destination node
drawNode(ctx, destX, height / 2, 35, "Cloud/Server", ieeeColors.orange);
// Draw connections
ctx.strokeStyle = "rgba(255,255,255,0.3)";
ctx.lineWidth = 2;
ctx.setLineDash([5, 5]);
// Source to router
ctx.beginPath();
ctx.moveTo(sourceX + 40, height / 2);
ctx.lineTo(routerX - 80, height / 2);
ctx.stroke();
// Router to destination
ctx.beginPath();
ctx.moveTo(routerX + 80, height / 2);
ctx.lineTo(destX - 40, height / 2);
ctx.stroke();
ctx.setLineDash([]);
// Draw queues
const queueStartY = startY;
for (let i = 0; i < trafficClasses.length; i++) {
const tc = trafficClasses[i];
const queueY = queueStartY + i * queueSpacing;
const queueLength = state.queues[tc.id].length;
const fillPercent = queueLength / simConfig.maxQueueSize;
drawQueue(ctx, routerX - queueWidth / 2, queueY, queueWidth, queueHeight,
tc, fillPercent, queueLength, config.mechanism);
}
// Draw packets in transit
const transitPackets = state.packets.filter(p =>
p.state === "generated" || p.state === "transmitting" || p.state === "delivered"
).slice(-50); // Limit for performance
for (const packet of transitPackets) {
const age = state.time - packet.createdAt;
let x, y, alpha;
if (packet.state === "generated" || packet.state === "queued") {
// Moving from source to router
const progress = Math.min(age * 2, 1);
x = sourceX + 40 + (routerX - sourceX - 120) * progress;
y = height / 2 + (Math.random() - 0.5) * 20;
alpha = 1 - progress * 0.3;
} else if (packet.state === "transmitting" || packet.state === "delivered") {
// Moving from router to destination
const transitTime = packet.deliveredAt ? packet.deliveredAt - packet.dequeuedAt : 0.5;
const progress = Math.min((state.time - packet.dequeuedAt) / transitTime, 1);
x = routerX + 80 + (destX - routerX - 120) * progress;
y = height / 2 + (Math.random() - 0.5) * 20;
alpha = 1 - progress * 0.5;
} else {
continue;
}
if (alpha > 0.1) {
drawPacket(ctx, x, y, packet, alpha);
}
}
// Draw dropped packets (falling animation)
const droppedPackets = state.packets.filter(p => p.dropped && state.time - p.createdAt < 2);
for (const packet of droppedPackets.slice(-20)) {
const age = state.time - packet.createdAt;
const x = routerX + (Math.random() - 0.5) * 100;
const y = height / 2 + age * 100;
const alpha = Math.max(0, 1 - age);
if (alpha > 0.1 && y < height) {
ctx.globalAlpha = alpha;
ctx.fillStyle = packet.color;
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fill();
// X mark for dropped
ctx.strokeStyle = "#fff";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(x - 4, y - 4);
ctx.lineTo(x + 4, y + 4);
ctx.moveTo(x + 4, y - 4);
ctx.lineTo(x - 4, y + 4);
ctx.stroke();
ctx.globalAlpha = 1;
}
}
// Draw bandwidth indicator
drawBandwidthMeter(ctx, width - 120, 30, config.bandwidth);
// Draw mechanism label
ctx.fillStyle = "rgba(255,255,255,0.9)";
ctx.font = "bold 14px 'Segoe UI', sans-serif";
ctx.textAlign = "center";
const mechName = qosMechanisms.find(m => m.id === config.mechanism)?.name || "Unknown";
ctx.fillText(`QoS: ${mechName}`, routerX, height - 20);
// Draw time
ctx.fillStyle = "rgba(255,255,255,0.7)";
ctx.font = "12px 'Segoe UI', sans-serif";
ctx.textAlign = "left";
ctx.fillText(`Time: ${state.time.toFixed(1)}s`, 20, 30);
}
// Draw a network node
function drawNode(ctx, x, y, radius, label, color) {
// Outer glow
const gradient = ctx.createRadialGradient(x, y, radius * 0.5, x, y, radius * 1.5);
gradient.addColorStop(0, color);
gradient.addColorStop(1, "transparent");
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(x, y, radius * 1.5, 0, Math.PI * 2);
ctx.fill();
// Main circle
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
// Border
ctx.strokeStyle = "rgba(255,255,255,0.5)";
ctx.lineWidth = 2;
ctx.stroke();
// Icon (simplified)
ctx.fillStyle = "white";
ctx.font = "bold 12px 'Segoe UI', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
if (label.includes("IoT")) {
ctx.fillText("IoT", x, y);
} else if (label.includes("Cloud")) {
ctx.fillText("Cloud", x, y);
}
// Label
ctx.fillStyle = "rgba(255,255,255,0.9)";
ctx.font = "11px 'Segoe UI', sans-serif";
ctx.fillText(label, x, y + radius + 15);
}
// Draw router with queues
function drawRouter(ctx, x, y, radius, mechanism, color) {
// Outer glow
const gradient = ctx.createRadialGradient(x, y, radius * 0.5, x, y, radius * 1.8);
gradient.addColorStop(0, color);
gradient.addColorStop(1, "transparent");
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(x, y, radius * 1.8, 0, Math.PI * 2);
ctx.fill();
// Main hexagon (router shape)
ctx.fillStyle = color;
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (i * Math.PI / 3) - Math.PI / 6;
const px = x + radius * Math.cos(angle);
const py = y + radius * Math.sin(angle);
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
ctx.fill();
// Border
ctx.strokeStyle = "rgba(255,255,255,0.6)";
ctx.lineWidth = 2;
ctx.stroke();
// Router icon/label
ctx.fillStyle = "white";
ctx.font = "bold 11px 'Segoe UI', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("QoS", x, y - 5);
ctx.fillText("Router", x, y + 8);
}
// Draw a queue visualization
function drawQueue(ctx, x, y, width, height, tc, fillPercent, count, mechanism) {
// Queue background
ctx.fillStyle = "rgba(255,255,255,0.1)";
ctx.strokeStyle = tc.color;
ctx.lineWidth = 2;
// Rounded rectangle
const radius = 5;
ctx.beginPath();
ctx.roundRect(x, y, width, height, radius);
ctx.fill();
ctx.stroke();
// Queue fill
const fillWidth = width * Math.min(fillPercent, 1);
if (fillWidth > 0) {
ctx.fillStyle = tc.color;
ctx.globalAlpha = 0.6;
ctx.beginPath();
ctx.roundRect(x, y, fillWidth, height, radius);
ctx.fill();
ctx.globalAlpha = 1;
}
// Priority indicator for PQ mode
if (mechanism === "pq") {
ctx.fillStyle = tc.color;
ctx.font = "bold 10px 'Segoe UI', sans-serif";
ctx.textAlign = "right";
ctx.fillText(`P${tc.priority}`, x - 5, y + height / 2 + 4);
}
// Queue label and count
ctx.fillStyle = "white";
ctx.font = "11px 'Segoe UI', sans-serif";
ctx.textAlign = "left";
ctx.fillText(tc.name, x + 5, y + height / 2 + 4);
ctx.textAlign = "right";
ctx.fillText(`${count}/${simConfig.maxQueueSize}`, x + width - 5, y + height / 2 + 4);
// Warning indicator if queue is getting full
if (fillPercent > 0.8) {
ctx.fillStyle = "#c0392b";
ctx.beginPath();
ctx.arc(x + width + 12, y + height / 2, 6, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "white";
ctx.font = "bold 10px 'Segoe UI', sans-serif";
ctx.textAlign = "center";
ctx.fillText("!", x + width + 12, y + height / 2 + 3);
}
}
// Draw a packet
function drawPacket(ctx, x, y, packet, alpha) {
ctx.globalAlpha = alpha;
// Packet circle
ctx.fillStyle = packet.color;
ctx.beginPath();
ctx.arc(x, y, 8, 0, Math.PI * 2);
ctx.fill();
// Border
ctx.strokeStyle = "rgba(255,255,255,0.8)";
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.globalAlpha = 1;
}
// Draw bandwidth meter
function drawBandwidthMeter(ctx, x, y, bandwidth) {
const width = 100;
const height = 15;
ctx.fillStyle = "rgba(255,255,255,0.1)";
ctx.fillRect(x, y, width, height);
// Fill based on bandwidth
const fillWidth = (bandwidth / 150) * width;
const gradient = ctx.createLinearGradient(x, y, x + width, y);
gradient.addColorStop(0, "#c0392b");
gradient.addColorStop(0.5, "#E67E22");
gradient.addColorStop(1, "#16A085");
ctx.fillStyle = gradient;
ctx.fillRect(x, y, fillWidth, height);
ctx.strokeStyle = "rgba(255,255,255,0.3)";
ctx.strokeRect(x, y, width, height);
ctx.fillStyle = "white";
ctx.font = "10px 'Segoe UI', sans-serif";
ctx.textAlign = "center";
ctx.fillText(`BW: ${bandwidth} Kbps`, x + width / 2, y + height + 12);
}
// ----------------------------------------------------------------------------
// SECTION 6: METRICS CALCULATION
// ----------------------------------------------------------------------------
// Calculate current metrics for display
function calculateDisplayMetrics(state) {
const metrics = {};
for (const tc of trafficClasses) {
const m = state.metrics[tc.id];
const queueSize = state.queues[tc.id].length;
metrics[tc.id] = {
sent: m.sent,
received: m.received,
dropped: m.dropped,
avgLatency: m.latencyCount > 0 ? m.latencySum / m.latencyCount : 0,
throughput: state.time > 0 ? (m.received / state.time).toFixed(1) : 0,
dropRate: m.sent > 0 ? ((m.dropped / m.sent) * 100).toFixed(1) : 0,
queueUtilization: ((queueSize / simConfig.maxQueueSize) * 100).toFixed(1),
queueSize: queueSize
};
}
return metrics;
}
// Calculate summary statistics
function calculateSummaryStats(state) {
let totalSent = 0;
let totalReceived = 0;
let totalDropped = 0;
let totalLatency = 0;
let latencyCount = 0;
for (const tc of trafficClasses) {
const m = state.metrics[tc.id];
totalSent += m.sent;
totalReceived += m.received;
totalDropped += m.dropped;
totalLatency += m.latencySum;
latencyCount += m.latencyCount;
}
return {
totalPackets: totalSent,
deliveredPackets: totalReceived,
droppedPackets: totalDropped,
avgLatency: latencyCount > 0 ? (totalLatency / latencyCount).toFixed(1) : 0,
deliveryRate: totalSent > 0 ? ((totalReceived / totalSent) * 100).toFixed(1) : 100,
throughput: state.time > 0 ? (totalReceived / state.time).toFixed(1) : 0
};
}
// ----------------------------------------------------------------------------
// SECTION 7: USER INTERFACE COMPONENTS
// ----------------------------------------------------------------------------
// Main container HTML
viewof qosSimulator = {
const container = html`<div class="qos-container">
<div class="qos-header">
<h2>Network QoS Simulator</h2>
<p>Explore how Quality of Service mechanisms prioritize IoT traffic under different network conditions</p>
</div>
<div class="qos-main-grid">
<!-- Left Panel: Traffic Classes & Network Conditions -->
<div class="qos-left-panel">
<div class="qos-panel" style="margin-bottom: 16px;">
<div class="qos-panel-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
</svg>
Traffic Classes
</div>
<div id="traffic-classes-container"></div>
</div>
<div class="qos-panel">
<div class="qos-panel-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<path d="M12 6v6l4 2"/>
</svg>
Network Conditions
</div>
<div id="network-conditions-container"></div>
</div>
</div>
<!-- Center Panel: Visualization -->
<div class="qos-panel qos-visualization">
<div class="qos-panel-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18M9 21V9"/>
</svg>
Network Visualization
</div>
<div class="qos-canvas-container">
<canvas id="qos-canvas" class="qos-canvas" width="800" height="500"></canvas>
</div>
<div class="qos-legend">
${trafficClasses.map(tc => `
<div class="legend-item">
<div class="legend-color" style="background: ${tc.color}"></div>
<span>${tc.name} (${tc.description})</span>
</div>
`).join('')}
</div>
<div class="sim-speed-control">
<span class="sim-speed-label">Speed:</span>
<input type="range" id="sim-speed" min="0.1" max="3" step="0.1" value="1">
<span class="sim-speed-value" id="speed-value">1x</span>
</div>
<div class="control-buttons">
<button class="control-btn start" id="start-btn">
<svg viewBox="0 0 24 24" fill="currentColor">
<polygon points="5,3 19,12 5,21"/>
</svg>
Start
</button>
<button class="control-btn pause" id="pause-btn" style="display: none;">
<svg viewBox="0 0 24 24" fill="currentColor">
<rect x="6" y="4" width="4" height="16"/>
<rect x="14" y="4" width="4" height="16"/>
</svg>
Pause
</button>
<button class="control-btn reset" id="reset-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 4v6h6M23 20v-6h-6"/>
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"/>
</svg>
Reset
</button>
</div>
</div>
<!-- Right Panel: QoS Mechanism & Metrics -->
<div class="qos-right-panel">
<div class="qos-panel" style="margin-bottom: 16px;">
<div class="qos-panel-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
QoS Mechanism
</div>
<div class="qos-mechanism-selector" id="mechanism-selector"></div>
<div id="mechanism-description" style="font-size: 0.85em; color: #666; padding: 8px; background: #f0f0f0; border-radius: 6px;"></div>
</div>
<div class="qos-panel" style="margin-bottom: 16px;">
<div class="qos-panel-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
</svg>
Scenarios
</div>
<div class="scenario-buttons" id="scenario-buttons"></div>
</div>
<div class="qos-panel">
<div class="qos-panel-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 3v18h18"/>
<path d="M18 17V9"/>
<path d="M13 17V5"/>
<path d="M8 17v-3"/>
</svg>
Per-Class Metrics
</div>
<div class="metrics-grid" id="metrics-container"></div>
</div>
</div>
</div>
<!-- Summary Stats -->
<div class="stats-summary" id="stats-summary"></div>
</div>`;
// Initialize UI components after DOM is ready
setTimeout(() => initializeUI(container), 100);
return container;
}
// Initialize all UI components
function initializeUI(container) {
const trafficContainer = container.querySelector("#traffic-classes-container");
const networkContainer = container.querySelector("#network-conditions-container");
const mechanismSelector = container.querySelector("#mechanism-selector");
const mechanismDesc = container.querySelector("#mechanism-description");
const scenarioButtons = container.querySelector("#scenario-buttons");
const metricsContainer = container.querySelector("#metrics-container");
const statsSummary = container.querySelector("#stats-summary");
const canvas = container.querySelector("#qos-canvas");
const startBtn = container.querySelector("#start-btn");
const pauseBtn = container.querySelector("#pause-btn");
const resetBtn = container.querySelector("#reset-btn");
const speedSlider = container.querySelector("#sim-speed");
const speedValue = container.querySelector("#speed-value");
// Render traffic class controls
renderTrafficClasses(trafficContainer);
// Render network conditions
renderNetworkConditions(networkContainer);
// Render mechanism selector
renderMechanismSelector(mechanismSelector, mechanismDesc);
// Render scenario buttons
renderScenarioButtons(scenarioButtons);
// Initialize metrics display
renderMetrics(metricsContainer, statsSummary);
// Set up canvas
if (canvas) {
canvas.width = canvas.parentElement.clientWidth || 800;
canvas.height = 500;
// Initial render
drawVisualization(canvas, simState, currentConfig);
}
// Control button handlers
startBtn.addEventListener("click", () => {
mutable simState = { ...simState, running: true, lastUpdate: Date.now() };
startBtn.style.display = "none";
pauseBtn.style.display = "flex";
});
pauseBtn.addEventListener("click", () => {
mutable simState = { ...simState, running: false };
pauseBtn.style.display = "none";
startBtn.style.display = "flex";
});
resetBtn.addEventListener("click", () => {
mutable simState = resetSimulation();
pauseBtn.style.display = "none";
startBtn.style.display = "flex";
renderMetrics(metricsContainer, statsSummary);
drawVisualization(canvas, simState, currentConfig);
});
// Speed control
speedSlider.addEventListener("input", (e) => {
const speed = parseFloat(e.target.value);
mutable currentConfig = { ...currentConfig, speed: speed };
speedValue.textContent = `${speed.toFixed(1)}x`;
});
// Animation loop
let animationId = null;
function animate() {
if (simState.running) {
mutable simState = updateSimulation(simState, currentConfig);
renderMetrics(metricsContainer, statsSummary);
}
drawVisualization(canvas, simState, currentConfig);
animationId = requestAnimationFrame(animate);
}
animate();
// Cleanup on container removal
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.removedNodes.length > 0) {
cancelAnimationFrame(animationId);
}
}
});
if (container.parentElement) {
observer.observe(container.parentElement, { childList: true });
}
}
// Render traffic class controls
function renderTrafficClasses(container) {
if (!container) return;
container.innerHTML = trafficClasses.map(tc => `
<div class="traffic-class-item ${tc.id}">
<div class="traffic-class-header">
<span class="traffic-class-name">${tc.name}</span>
<span class="traffic-class-badge" style="background: ${tc.color}">${tc.dscp}</span>
</div>
<div style="font-size: 0.75em; color: #666; margin-bottom: 6px;">${tc.description}</div>
<div class="traffic-class-controls">
<div class="traffic-class-control">
<label>Rate (pkt/s)</label>
<input type="range" class="traffic-rate" data-class="${tc.id}"
min="0" max="100" value="${tc.defaultRate}">
<div class="traffic-class-value">${tc.defaultRate}</div>
</div>
<div class="traffic-class-control">
<label>Burst Size</label>
<input type="range" class="traffic-burst" data-class="${tc.id}"
min="1" max="50" value="${tc.defaultBurst}">
<div class="traffic-class-value">${tc.defaultBurst}</div>
</div>
</div>
</div>
`).join('');
// Add event listeners
container.querySelectorAll(".traffic-rate").forEach(slider => {
const valueDisplay = slider.parentElement.querySelector(".traffic-class-value");
slider.addEventListener("input", (e) => {
const classId = e.target.dataset.class;
const value = parseInt(e.target.value);
valueDisplay.textContent = value;
mutable currentConfig = {
...currentConfig,
trafficRates: { ...currentConfig.trafficRates, [classId]: value }
};
});
});
container.querySelectorAll(".traffic-burst").forEach(slider => {
const valueDisplay = slider.parentElement.querySelector(".traffic-class-value");
slider.addEventListener("input", (e) => {
const classId = e.target.dataset.class;
const value = parseInt(e.target.value);
valueDisplay.textContent = value;
mutable currentConfig = {
...currentConfig,
burstSizes: { ...currentConfig.burstSizes, [classId]: value }
};
});
});
}
// Render network conditions controls
function renderNetworkConditions(container) {
if (!container) return;
const conditions = [
{ id: "bandwidth", name: "Bandwidth", unit: "Kbps", min: 10, max: 150, step: 5, default: 100, class: "bandwidth" },
{ id: "latency", name: "Base Latency", unit: "ms", min: 1, max: 500, step: 5, default: 20, class: "latency" },
{ id: "jitter", name: "Jitter", unit: "ms", min: 0, max: 100, step: 2, default: 5, class: "jitter" },
{ id: "packetLoss", name: "Packet Loss", unit: "%", min: 0, max: 20, step: 0.5, default: 0.5, class: "packet-loss" }
];
container.innerHTML = conditions.map(c => `
<div class="network-condition ${c.class}">
<label>
<span>${c.name}</span>
<span class="network-condition-value" id="${c.id}-value">${c.default} ${c.unit}</span>
</label>
<input type="range" id="${c.id}-slider" min="${c.min}" max="${c.max}"
step="${c.step}" value="${c.default}">
</div>
`).join('');
// Add event listeners
conditions.forEach(c => {
const slider = container.querySelector(`#${c.id}-slider`);
const valueDisplay = container.querySelector(`#${c.id}-value`);
slider.addEventListener("input", (e) => {
const value = parseFloat(e.target.value);
valueDisplay.textContent = `${value} ${c.unit}`;
mutable currentConfig = { ...currentConfig, [c.id]: value };
});
});
}
// Render QoS mechanism selector
function renderMechanismSelector(selector, descContainer) {
if (!selector) return;
selector.innerHTML = qosMechanisms.map(m => `
<button class="qos-mechanism-btn ${m.id === currentConfig.mechanism ? 'active' : ''}"
data-mechanism="${m.id}">
<span class="mechanism-name">${m.name}</span>
<span class="mechanism-abbr">${m.abbr}</span>
</button>
`).join('');
// Set initial description
const activeMech = qosMechanisms.find(m => m.id === currentConfig.mechanism);
if (descContainer && activeMech) {
descContainer.textContent = activeMech.description;
}
// Add event listeners
selector.querySelectorAll(".qos-mechanism-btn").forEach(btn => {
btn.addEventListener("click", (e) => {
const mechId = e.currentTarget.dataset.mechanism;
// Update active state
selector.querySelectorAll(".qos-mechanism-btn").forEach(b => b.classList.remove("active"));
e.currentTarget.classList.add("active");
// Update config
mutable currentConfig = { ...currentConfig, mechanism: mechId };
// Update description
const mech = qosMechanisms.find(m => m.id === mechId);
if (descContainer && mech) {
descContainer.textContent = mech.description;
}
});
});
}
// Render scenario buttons
function renderScenarioButtons(container) {
if (!container) return;
container.innerHTML = scenarios.map(s => `
<button class="scenario-btn ${s.id} ${s.id === currentConfig.scenario ? 'active' : ''}"
data-scenario="${s.id}">
${s.name}
</button>
`).join('');
container.querySelectorAll(".scenario-btn").forEach(btn => {
btn.addEventListener("click", (e) => {
const scenarioId = e.currentTarget.dataset.scenario;
const scenario = scenarios.find(s => s.id === scenarioId);
if (!scenario) return;
// Update active state
container.querySelectorAll(".scenario-btn").forEach(b => b.classList.remove("active"));
e.currentTarget.classList.add("active");
// Apply scenario settings
mutable currentConfig = {
...currentConfig,
scenario: scenarioId,
bandwidth: scenario.bandwidth,
latency: scenario.latency,
jitter: scenario.jitter,
packetLoss: scenario.packetLoss
};
// Update network condition sliders
const networkContainer = document.querySelector("#network-conditions-container");
if (networkContainer) {
const bwSlider = networkContainer.querySelector("#bandwidth-slider");
const latSlider = networkContainer.querySelector("#latency-slider");
const jitSlider = networkContainer.querySelector("#jitter-slider");
const plSlider = networkContainer.querySelector("#packetLoss-slider");
if (bwSlider) {
bwSlider.value = scenario.bandwidth;
networkContainer.querySelector("#bandwidth-value").textContent = `${scenario.bandwidth} Kbps`;
}
if (latSlider) {
latSlider.value = scenario.latency;
networkContainer.querySelector("#latency-value").textContent = `${scenario.latency} ms`;
}
if (jitSlider) {
jitSlider.value = scenario.jitter;
networkContainer.querySelector("#jitter-value").textContent = `${scenario.jitter} ms`;
}
if (plSlider) {
plSlider.value = scenario.packetLoss;
networkContainer.querySelector("#packetLoss-value").textContent = `${scenario.packetLoss} %`;
}
}
});
});
}
// Render metrics display
function renderMetrics(metricsContainer, statsSummary) {
if (!metricsContainer || !statsSummary) return;
const displayMetrics = calculateDisplayMetrics(simState);
const summary = calculateSummaryStats(simState);
// Per-class metrics
metricsContainer.innerHTML = `
<div class="metric-card">
<div class="metric-card-header">
<span class="metric-card-title">Latency (ms)</span>
</div>
<div class="metric-bars">
${trafficClasses.map(tc => {
const latency = displayMetrics[tc.id].avgLatency;
const latencyPercent = Math.min((latency / 500) * 100, 100);
return `
<div class="metric-bar-row">
<span class="metric-bar-label" style="color: ${tc.color}">${tc.name}</span>
<div class="metric-bar-container">
<div class="metric-bar-fill" style="width: ${latencyPercent}%; background: ${tc.color}"></div>
</div>
<span class="metric-bar-value">${latency.toFixed(0)}</span>
</div>
`;
}).join('')}
</div>
</div>
<div class="metric-card">
<div class="metric-card-header">
<span class="metric-card-title">Throughput (pkt/s)</span>
</div>
<div class="metric-bars">
${trafficClasses.map(tc => {
const throughput = parseFloat(displayMetrics[tc.id].throughput);
const throughputPercent = Math.min((throughput / 50) * 100, 100);
return `
<div class="metric-bar-row">
<span class="metric-bar-label" style="color: ${tc.color}">${tc.name}</span>
<div class="metric-bar-container">
<div class="metric-bar-fill" style="width: ${throughputPercent}%; background: ${tc.color}"></div>
</div>
<span class="metric-bar-value">${throughput}</span>
</div>
`;
}).join('')}
</div>
</div>
<div class="metric-card">
<div class="metric-card-header">
<span class="metric-card-title">Drop Rate (%)</span>
</div>
<div class="metric-bars">
${trafficClasses.map(tc => {
const dropRate = parseFloat(displayMetrics[tc.id].dropRate);
const dropPercent = Math.min(dropRate, 100);
return `
<div class="metric-bar-row">
<span class="metric-bar-label" style="color: ${tc.color}">${tc.name}</span>
<div class="metric-bar-container">
<div class="metric-bar-fill" style="width: ${dropPercent}%; background: ${dropRate > 10 ? '#c0392b' : tc.color}"></div>
</div>
<span class="metric-bar-value">${dropRate}</span>
</div>
`;
}).join('')}
</div>
</div>
<div class="metric-card">
<div class="metric-card-header">
<span class="metric-card-title">Queue Utilization (%)</span>
</div>
<div class="metric-bars">
${trafficClasses.map(tc => {
const queueUtil = parseFloat(displayMetrics[tc.id].queueUtilization);
const utilColor = queueUtil > 80 ? '#c0392b' : queueUtil > 50 ? '#E67E22' : tc.color;
return `
<div class="metric-bar-row">
<span class="metric-bar-label" style="color: ${tc.color}">${tc.name}</span>
<div class="metric-bar-container">
<div class="metric-bar-fill" style="width: ${queueUtil}%; background: ${utilColor}"></div>
</div>
<span class="metric-bar-value">${queueUtil}</span>
</div>
`;
}).join('')}
</div>
</div>
`;
// Summary statistics
statsSummary.innerHTML = `
<div class="stat-box">
<div class="stat-box-icon" style="background: ${ieeeColors.teal}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
</svg>
</div>
<div class="stat-box-value">${summary.totalPackets}</div>
<div class="stat-box-label">Total Packets</div>
</div>
<div class="stat-box">
<div class="stat-box-icon" style="background: ${ieeeColors.green}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20,6 9,17 4,12"/>
</svg>
</div>
<div class="stat-box-value">${summary.deliveredPackets}</div>
<div class="stat-box-label">Delivered</div>
</div>
<div class="stat-box">
<div class="stat-box-icon" style="background: ${ieeeColors.red}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</div>
<div class="stat-box-value">${summary.droppedPackets}</div>
<div class="stat-box-label">Dropped</div>
</div>
<div class="stat-box">
<div class="stat-box-icon" style="background: ${ieeeColors.orange}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12,6 12,12 16,14"/>
</svg>
</div>
<div class="stat-box-value">${summary.avgLatency} ms</div>
<div class="stat-box-label">Avg Latency</div>
</div>
`;
}
// ----------------------------------------------------------------------------
// SECTION 8: EDUCATIONAL CONTENT
// ----------------------------------------------------------------------------
// QoS concepts explanation
qosExplanation = md`
## Understanding Quality of Service (QoS)
Quality of Service (QoS) refers to the set of techniques and mechanisms that manage network resources to ensure predictable and reliable performance for different types of traffic. In IoT networks, QoS is critical because different data streams have vastly different requirements:
### Traffic Classification
| Class | Description | Requirements | Example |
|-------|-------------|--------------|---------|
| **Critical** | Safety-critical alarms | Ultra-low latency, zero loss | Fire alarms, emergency stops |
| **High** | Control commands | Low latency, minimal loss | Actuator controls, HVAC commands |
| **Medium** | Sensor telemetry | Moderate latency acceptable | Temperature readings, humidity data |
| **Low** | Firmware updates | High latency acceptable | OTA updates, configuration changes |
| **Best Effort** | Background traffic | No guarantees | Diagnostics, logs |
### QoS Mechanisms Explained
1. **Priority Queuing (PQ)**: Packets are served strictly by priority. Higher priority traffic always goes first.
- *Pros*: Guaranteed service for critical traffic
- *Cons*: Lower priorities may starve during congestion
2. **Weighted Fair Queuing (WFQ)**: Bandwidth is allocated proportionally based on weights assigned to each class.
- *Pros*: Fair distribution, no starvation
- *Cons*: Critical traffic may experience delays
3. **Traffic Shaping**: Smooths bursty traffic by delaying packets to conform to a traffic profile.
- *Pros*: Predictable traffic patterns
- *Cons*: Introduces additional latency
4. **Rate Limiting**: Enforces maximum rates per class; excess packets are dropped.
- *Pros*: Protects network from overload
- *Cons*: Data loss when limits exceeded
`
// Architecture diagram
qosArchitectureDiagram = md`
### QoS Architecture in IoT Networks
\`\`\`mermaid
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#16A085', 'tertiaryColor': '#f8f9fa'}}}%%
flowchart TB
subgraph Sources["IoT Device Sources"]
S1[Alarm Sensors]
S2[Control Systems]
S3[Telemetry Devices]
S4[Update Services]
end
subgraph Classifier["Traffic Classifier"]
C1[DSCP Marking]
C2[Priority Assignment]
end
subgraph Scheduler["QoS Scheduler"]
Q1[Critical Queue<br/>EF - Priority 0]
Q2[High Queue<br/>AF41 - Priority 1]
Q3[Medium Queue<br/>AF21 - Priority 2]
Q4[Low Queue<br/>AF11 - Priority 3]
Q5[Best Effort Queue<br/>BE - Priority 4]
end
subgraph Mechanism["Scheduling Mechanism"]
M1[Priority Queuing]
M2[Weighted Fair Queuing]
M3[Traffic Shaping]
M4[Rate Limiting]
end
subgraph Output["Output Interface"]
O1[Transmission Buffer]
O2[Network Link]
end
S1 --> C1
S2 --> C1
S3 --> C1
S4 --> C1
C1 --> C2
C2 --> Q1
C2 --> Q2
C2 --> Q3
C2 --> Q4
C2 --> Q5
Q1 --> M1
Q2 --> M1
Q3 --> M1
Q4 --> M1
Q5 --> M1
M1 -.-> M2
M2 -.-> M3
M3 -.-> M4
M1 --> O1
M2 --> O1
M3 --> O1
M4 --> O1
O1 --> O2
\`\`\`
`
// Key insights section
keyInsights = md`
### Key Insights from the Simulator
**Experiment 1: Normal Operation**
- All traffic classes receive adequate service
- Low queue utilization across all classes
- Minimal packet drops
**Experiment 2: Network Congestion**
- Priority Queuing: Critical traffic unaffected, best effort may starve
- WFQ: All classes share degradation proportionally
- Rate Limiting: Excess traffic dropped predictably
**Experiment 3: Critical Event Flood**
- Tests system response to alarm bursts
- Priority mechanisms ensure alarm delivery
- Other traffic experiences delays or drops
**Experiment 4: Bulk Transfer**
- Large firmware updates challenge bandwidth
- QoS prevents bulk traffic from blocking critical data
- Rate limiting protects network resources
### Design Recommendations
1. **Always classify traffic** - Use DSCP markings consistently
2. **Reserve bandwidth** - Allocate minimum guarantees for critical classes
3. **Monitor queues** - High utilization indicates capacity issues
4. **Test failure modes** - Verify behavior during congestion scenarios
`
// DSCP marking reference
dscpReference = md`
### DSCP Marking Reference
| Per-Hop Behavior | DSCP Value | Binary | Description |
|------------------|------------|--------|-------------|
| EF (Expedited Forwarding) | 46 | 101110 | Low loss, low latency |
| AF41 (Assured Forwarding) | 34 | 100010 | High priority assured |
| AF21 | 18 | 010010 | Medium priority |
| AF11 | 10 | 001010 | Low priority |
| BE (Best Effort) | 0 | 000000 | Default, no guarantee |
**Implementation Example:**
\`\`\`python
# Python example: Setting DSCP on socket
import socket
# Create UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Set DSCP for critical traffic (EF = 46)
# IP_TOS expects the full ToS byte (DSCP << 2)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, 46 << 2)
# Send critical alarm
sock.sendto(b"ALARM:FIRE_DETECTED", ("gateway", 5000))
\`\`\`
`