diff --git a/src/components/FlowsPanel.vue b/src/components/FlowsPanel.vue index 8611804..a836e80 100644 --- a/src/components/FlowsPanel.vue +++ b/src/components/FlowsPanel.vue @@ -7,45 +7,52 @@ const columns: any = [ key: 'flow_id', title: 'ID', dataKey: 'flow_id', - width: 60, + width: 40, align: 'center', cellRenderer: ({ rowIndex }: any) => rowIndex + 1 }, { key: 'src', - title: 'Source', - dataKey: 'src', + title: 'SRC', + dataKey: 'e2e_src', width: 40, align: 'center' }, { key: 'dst', - title: 'Dest', - dataKey: 'dst', + title: 'DST', + dataKey: 'e2e_dst', width: 40, align: 'center' }, + // { + // key: 'deadline', + // title: 'Deadline', + // dataKey: 'deadline', + // width: 50, + // align: 'center' + // }, { - key: 'deadline', - title: 'Deadline', - dataKey: 'deadline', - width: 50, + key: 'period', + title: 'PERIOD', + dataKey: 'period', + width: 40, align: 'center' }, { - key: 'period', - title: 'Period', - dataKey: 'period', - width: 60, + key: 'path', + title: 'PATH', + dataKey: 'path', + width: 120, align: 'center' }, { - key: 'payload_size', - title: 'Payload Size', - dataKey: 'payload_size', - width: 100, - align: 'center', - cellRenderer: ({ cellData: payload_size }: any) => payload_size.toString() + key: 'workload', + title: 'WORKLOAD', + dataKey: 'workload', + width: 50, + align: 'center' + // cellRenderer: ({ cellData: payload_size }: any) => payload_size.toString() } ] diff --git a/src/core/network.ts b/src/core/network.ts index 8228020..e1dce47 100644 --- a/src/core/network.ts +++ b/src/core/network.ts @@ -12,22 +12,24 @@ import { type ASNMsgPayload, LINK_TYPE, type InitMsgPayload, - PROTOCOL_TYPE + PROTOCOL_TYPE, + type RoutingGraph } from './typedefs' import { SeededRandom } from '@/utils/rand' -import presetTopos from './preset_topologies.json' +import presetTopos from './preset_topologies.json' export class NetworkHub { Config: Ref Nodes: Ref - Links = ref([]) + Links = ref<{ [uid: number]: Link }>({}) Flows = ref([]) Packets = ref([]) PacketsCurrent = ref([]) Logs = ref([]) ASN = ref(0) // absolute slot number Rand: SeededRandom + RoutingGraph = ref() // to find nearest neighbors, only network devices are inserted in KDTrees kdTreeAny: KDTree // any network devices @@ -151,12 +153,12 @@ export class NetworkHub { } } this.Nodes.value = [{ id: 0 }] // placeholder to let node_id start from 1 - this.Links.value = [] + this.Links.value = {} } LoadTopology() { this.Running.value = false clearInterval(this.asnTimer) - this.Links.value = [] + this.Links.value = {} this.Packets.value = [] this.PacketsCurrent.value = [] this.ASN.value = 0 @@ -200,7 +202,7 @@ export class NetworkHub { this.kdTreeTSCH = new KDTree() this.kdTreeTSN = new KDTree() this.kdTreeFiveGgNB = new KDTree() - this.Links.value = [] + this.Links.value = {} for (const n of this.Nodes.value) { if (n.id == 0 || n.type > 10) continue this.kdTreeAny.Insert(new KDNode(n.id, n.pos)) @@ -220,7 +222,7 @@ export class NetworkHub { } for (const n of this.Nodes.value) { - if (n.id == 0) continue + if (n.id == 0) continue let neighbors: any = [] @@ -350,6 +352,106 @@ export class NetworkHub { } } + constructRoutingGraph() { + const graph: RoutingGraph = {} + + for (const link of Object.values(this.Links.value)) { + if (link === undefined) continue + + const v1 = link.v1 + const v2 = link.v2 + + if (graph[v1]) { + graph[v1].push(v2) + } else { + graph[v1] = [v2] + } + + if (graph[v2]) { + graph[v2].push(v1) + } else { + graph[v2] = [v1] + } + } + + this.RoutingGraph.value = graph + } + + findPath(srcId: number, dstId: number) { + // Dijkstra's shortest path algorithm + const graph = this.RoutingGraph.value || {} // null check + const distances: { [nodeId: number]: number } = {} + const previous: { [nodeId: number]: number | null } = {} + + // initialize distance and previous + for (const nodeId in graph) { + distances[nodeId] = parseInt(nodeId) === srcId ? 0 : Infinity + previous[nodeId] = null + } + + const unvisited = Object.keys(graph).map((id) => parseInt(id)) + + while (unvisited.length > 0) { + // node w/ smallest distance + let smallest = 0 + for (let i = 1; i < unvisited.length; i++) { + if (distances[unvisited[i]] < distances[unvisited[smallest]]) { + smallest = i + } + } + const current = unvisited[smallest] + + // mark visited + unvisited.splice(smallest, 1) + + // update distances of neighbors + for (const neighbor of graph[current]) { + const alt = distances[current] + 1 + if (alt < distances[neighbor]) { + distances[neighbor] = alt + previous[neighbor] = current + } + } + } + + // construct path from previous + const pathIds: number[] = [] + let current: number | null = dstId + while (current != null) { + pathIds.unshift(current) + current = previous[current] + } + + return pathIds + } + + AddFlows(num_flows: number) { + + const endSystems = this.Nodes.value.filter(n => n.type >= 11) + + for (let i = 0; i < num_flows; i++) { + const src = endSystems[Math.floor(this.Rand.next() * endSystems.length)] + + let dst = src + while (dst.id === src.id) { + dst = endSystems[Math.floor(this.Rand.next() * endSystems.length)] + } + + const f = { + id: this.Flows.value.length, + e2e_src: src.id, + e2e_dst: dst.id, + period: Math.floor(this.Rand.next() * 10), // from 0 to 9 - change this later + deadline: Math.floor(this.Rand.next() * 10), // from 0 to 9 - change this later + workload: Math.floor(this.Rand.next() * 10), // from 0 to 9 - change this later + path: this.findPath(src.id, dst.id) + } + this.Flows.value.push(f) + + this.Logs.value.unshift(`New flow: ID:${f.id}, source:${f.e2e_src}, dest:${f.e2e_dst}.`) + } + } + Run = () => { this.Logs.value.unshift('Emulation started.') this.ASN.value++ diff --git a/src/core/typedefs.ts b/src/core/typedefs.ts index 72e1e19..758cc03 100644 --- a/src/core/typedefs.ts +++ b/src/core/typedefs.ts @@ -65,6 +65,7 @@ export interface Flow { deadline: number period: number workload: number + path: number[] // id's of all nodes in path } // Packet is transfered among nodes, at data-link layer @@ -124,3 +125,7 @@ export interface InitMsgPayload { export interface ASNMsgPayload { asn: number } + +export interface RoutingGraph { + [id: number]: number[] +} diff --git a/src/hooks/useDrawTopology.ts b/src/hooks/useDrawTopology.ts index dd49935..16d42a3 100644 --- a/src/hooks/useDrawTopology.ts +++ b/src/hooks/useDrawTopology.ts @@ -196,6 +196,9 @@ export async function useDrawTopology(dom: HTMLElement) { box.getSize(size) const label = createLabel(`${NODE_TYPE_DISPLAY_NAME[NODE_TYPE[node.type]]}-${node.id}`) label.position.set(model.position.x, size.y + 1, model.position.z) // Adjust the position as needed + if (node.type == NODE_TYPE.TSN) { + label.position.y = size.y + 3 + } scene.add(label) // dragbox and helper @@ -578,13 +581,16 @@ export async function useDrawTopology(dom: HTMLElement) { animate() await loadGLTFModels() + Network.LoadTopology() drawNodes() createDragControls() Network.EstablishConnection() Network.StartWebWorkers() - drawLinks() + Network.constructRoutingGraph() + Network.AddFlows(10) // specify number of flows + // ################### watch(SignalAddNode, () => {