From 31299b08b7240a901b424a70c5d2b18c857bb027 Mon Sep 17 00:00:00 2001 From: AmyangXYZ Date: Sat, 30 Dec 2023 12:52:19 -0500 Subject: [PATCH 1/4] adding united network --- src/components/PacketSniffer.vue | 21 +- src/components/SettingsPanel.vue | 12 +- src/core/network.ts | 146 ++++++++++++++ src/core/node.ts | 41 ++++ src/core/node_end_system.ts | 25 +++ src/core/node_five_g_bs.ts | 25 +++ src/core/node_five_g_ue.ts | 26 +++ src/core/node_tsch.ts | 25 +++ src/core/node_tsn.ts | 25 +++ src/core/typedefs.ts | 99 ++++++++++ src/hooks/useDrawMiniMap.ts | 67 +++---- src/hooks/useDrawTopology.ts | 155 +++++++-------- src/hooks/useStates.ts | 13 +- src/networks/5G/network.ts | 126 ------------ src/networks/5G/node.ts | 11 -- src/networks/5G/typedefs.ts | 12 -- src/networks/TSCH/network.ts | 308 ----------------------------- src/networks/TSCH/node.ts | 323 ------------------------------- src/networks/TSCH/typedefs.ts | 64 ------ src/networks/TSN/es.ts | 57 ------ src/networks/TSN/network.ts | 150 -------------- src/networks/TSN/node.ts | 77 -------- src/networks/TSN/typedefs.ts | 16 -- src/networks/common.ts | 185 ------------------ src/views/HomeView.vue | 6 +- 25 files changed, 532 insertions(+), 1483 deletions(-) create mode 100644 src/core/network.ts create mode 100644 src/core/node.ts create mode 100644 src/core/node_end_system.ts create mode 100644 src/core/node_five_g_bs.ts create mode 100644 src/core/node_five_g_ue.ts create mode 100644 src/core/node_tsch.ts create mode 100644 src/core/node_tsn.ts create mode 100644 src/core/typedefs.ts delete mode 100644 src/networks/5G/network.ts delete mode 100644 src/networks/5G/node.ts delete mode 100644 src/networks/5G/typedefs.ts delete mode 100644 src/networks/TSCH/network.ts delete mode 100644 src/networks/TSCH/node.ts delete mode 100644 src/networks/TSCH/typedefs.ts delete mode 100644 src/networks/TSN/es.ts delete mode 100644 src/networks/TSN/network.ts delete mode 100644 src/networks/TSN/node.ts delete mode 100644 src/networks/TSN/typedefs.ts delete mode 100644 src/networks/common.ts diff --git a/src/components/PacketSniffer.vue b/src/components/PacketSniffer.vue index 925919c..02385ed 100644 --- a/src/components/PacketSniffer.vue +++ b/src/components/PacketSniffer.vue @@ -3,10 +3,7 @@ import ChannelChart from '@/components/ChannelChart.vue' import { ref, watch, nextTick } from 'vue' import { Network } from '@/hooks/useStates' -import { NETWORK_TYPE, type Packet } from '@/networks/common' -import { TSCH_PKT_TYPE } from '@/networks/TSCH/typedefs' -import { TSN_PKT_TYPE } from '@/networks/TSN/typedefs' -import { FIVE_G_PKT_TYPE } from '@/networks/5G/typedefs' +import { type Packet } from '@/core/typedefs' import { Filter } from '@element-plus/icons-vue' @@ -41,16 +38,16 @@ const columns: any = [ align: 'center' }, { - key: 'src', + key: 'mac_src', title: 'SRC', - dataKey: 'src', + dataKey: 'mac_src', width: 30, align: 'center' }, { - key: 'dst', + key: 'mac_dst', title: 'DST', - dataKey: 'dst', + dataKey: 'mac_dst', width: 30, align: 'center' }, @@ -69,9 +66,7 @@ const columns: any = [ width: 80, align: 'center', cellRenderer: ({ cellData: type }: any) => { - if (Network.Type == NETWORK_TYPE.TSCH) return TSCH_PKT_TYPE[type] - if (Network.Type == NETWORK_TYPE.TSN) return TSN_PKT_TYPE[type] - if (Network.Type == NETWORK_TYPE.FIVE_G) return FIVE_G_PKT_TYPE[type] + type } }, // { @@ -130,7 +125,7 @@ Row.inheritAttrs = false :columns="columns" :data="Network.Packets.value.filter(filterFunc)" :width="360" - :height="180" + :height="370" :expand-column-key="columns[7].key" :estimated-row-height="16" :header-height="18" @@ -139,7 +134,7 @@ Row.inheritAttrs = false - + diff --git a/src/components/SettingsPanel.vue b/src/components/SettingsPanel.vue index 636afd0..1aa2b52 100644 --- a/src/components/SettingsPanel.vue +++ b/src/components/SettingsPanel.vue @@ -3,7 +3,7 @@ import { watch } from 'vue' import { Network, SignalShowSettings } from '@/hooks/useStates' watch( - [Network.TopoConfig, Network.SchConfig], + [Network.Config], () => { Network.Reset() }, @@ -13,13 +13,13 @@ watch( diff --git a/src/core/network.ts b/src/core/network.ts new file mode 100644 index 0000000..153a462 --- /dev/null +++ b/src/core/network.ts @@ -0,0 +1,146 @@ +import { ref, type Ref, watch } from 'vue' +import { KDTree } from '@/utils/kdtree' +import { + NODE_TYPE, + type Config, + type Flow, + type Link, + type Node, + type Packet, + type Message, + MSG_TYPE, + type ASNMsgPayload +} from './typedefs' +import { SeededRandom } from '@/utils/rand' + +export class NetworkHub { + Config: Ref + Nodes: Ref + Links = ref([]) + Flows = ref([]) + Packets = ref([]) + PacketsCurrent = ref([]) + ASN = ref(0) // absolute slot number + Rand: SeededRandom + KDTree = new KDTree() // to find nearest neighbors + + asnTimer: any + SignalReset = ref(0) + SlotDone = ref(false) + doneCnt: number = 0 + Running = ref(false) + SlotDuration = ref(750) + + constructor(config: Config) { + this.Config = ref(config) + this.Nodes = ref([{ id: 0 }]) // placeholder to let node_id start from 1 + this.Rand = new SeededRandom(this.Config.value.seed) + + this.createNodes() + + watch(this.ASN, () => { + this.doneCnt = 0 + this.PacketsCurrent.value = [] + if (this.Nodes.value.length > 1) { + for (const n of this.Nodes.value) { + if (n.w != undefined) { + n.w.postMessage({ + type: MSG_TYPE.ASN, + id: n.id, + payload: { asn: this.ASN.value } + }) + } + } + } + }) + } + + createNodes() { + for (let i = 1; i <= this.Config.value.num_nodes; i++) { + const n = { + id: i, + type: Math.floor((this.Rand.next() * Object.values(NODE_TYPE).length) / 2), + pos: [ + Math.floor(this.Rand.next() * this.Config.value.grid_size) - + this.Config.value.grid_size / 2, + Math.floor(this.Rand.next() * this.Config.value.grid_size) - + this.Config.value.grid_size / 2 + ], + neighbors: [], + tx_cnt: 0, + rx_cnt: 0, + // w: new Worker(new URL('@/networks/TSN/node.ts', import.meta.url), { type: 'module' }) + w: undefined + } + + const worker_path = `./node_${NODE_TYPE[n.type].toLowerCase()}.ts` + n.w = new Worker(new URL(worker_path, import.meta.url), { type: 'module' }) + + n!.w.onmessage = (e: any) => { + if ('uid' in e.data) { + this.handlePkt(e.data) + } else { + this.handleMsg(e.data) + } + } + this.Nodes.value.push(n) + } + } + + AddNode(id: number, type: number) { + const n = { id: id, type: type } + this.Nodes.value.push(n) + } + + AddLink(v1: number, v2: number, type: number) { + if (v1 > v2) { + ;[v1, v2] = [v2, v1] + } + // Cantor pairing + const uid = 0.5 * (v1 + v2) * (v1 + v2 + 1) + v2 + if (this.Links.value[uid] == undefined) { + this.Links.value[uid] = { uid, v1, v2, type } + } + } + + // handle control plane msg from each node + handleMsg = (msg: Message) => { + switch (msg.type) { + case MSG_TYPE.DONE: + if (++this.doneCnt == this.Config.value.num_nodes) { + this.SlotDone.value = true + } + console.log(this.doneCnt, this.SlotDone.value) + break + } + } + + // forward physical layer pkt from each node + handlePkt = (pkt: Packet) => { + this.Nodes.value[pkt.mac_dst].w!.postMessage(pkt) + this.Packets.value.push(pkt) + this.PacketsCurrent.value.push(pkt) + } + + Run = () => { + this.Step() + this.Running.value = true + this.asnTimer = setInterval(() => { + this.ASN.value++ + this.SlotDone.value = false + }, this.SlotDuration.value) + } + Step = () => { + this.ASN.value++ + this.SlotDone.value = false + } + Pause = () => { + this.Running.value = false + clearInterval(this.asnTimer) + } + Reset = () => { + this.Running.value = false + clearInterval(this.asnTimer) + this.SignalReset.value++ + } +} diff --git a/src/core/node.ts b/src/core/node.ts new file mode 100644 index 0000000..a4a5d7e --- /dev/null +++ b/src/core/node.ts @@ -0,0 +1,41 @@ +import { type Message, type MsgHandler, type Packet, type PktHandler } from './typedefs' + +export class Node { + id: number = 0 + pkt_seq: number = 0 + tx_cnt: number = 0 + rx_cnt: number = 0 + ASN: number = 0 + + msgHandlers: { [type: number]: MsgHandler } = {} + pktHandlers: { [type: number]: PktHandler } = {} + + constructor() {} + + Run() { + onmessage = (e: any) => { + if ('uid' in e.data) { + const pkt: Packet = e.data + if (this.pktHandlers[pkt.type] != undefined) { + this.pktHandlers[pkt.type](pkt) + } else { + console.log('!! undefined packet type:', pkt.type) + } + } else { + const msg: Message = e.data + if (this.msgHandlers[msg.type] != undefined) { + this.msgHandlers[msg.type](msg) + } else { + console.log('!! undefined message type:', msg.type) + } + } + } + } + + registerMsgHandler(type: number, handler: MsgHandler) { + this.msgHandlers[type] = handler + } + registerPktHandler(type: number, handler: PktHandler) { + this.pktHandlers[type] = handler + } +} diff --git a/src/core/node_end_system.ts b/src/core/node_end_system.ts new file mode 100644 index 0000000..5700522 --- /dev/null +++ b/src/core/node_end_system.ts @@ -0,0 +1,25 @@ +import { Node } from './node' +import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' + +class EndSystem extends Node { + constructor() { + super() + this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) + this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) + } + asnMsgHandler = (msg: Message) => { + const payload: ASNMsgPayload = msg.payload + this.ASN = payload.asn + + // do something + + postMessage({ + type: MSG_TYPE.DONE + }) + } + dataPktHandler = (pkt: Packet) => { + // console.log('tsn', pkt) + } +} + +new EndSystem().Run() diff --git a/src/core/node_five_g_bs.ts b/src/core/node_five_g_bs.ts new file mode 100644 index 0000000..4c41f86 --- /dev/null +++ b/src/core/node_five_g_bs.ts @@ -0,0 +1,25 @@ +import { Node } from './node' +import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' + +class FiveGBS extends Node { + constructor() { + super() + this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) + this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) + } + asnMsgHandler = (msg: Message) => { + const payload: ASNMsgPayload = msg.payload + this.ASN = payload.asn + + // do something + + postMessage({ + type: MSG_TYPE.DONE + }) + } + dataPktHandler = (pkt: Packet) => { + // console.log('tsn', pkt) + } +} + +new FiveGBS().Run() diff --git a/src/core/node_five_g_ue.ts b/src/core/node_five_g_ue.ts new file mode 100644 index 0000000..b68883d --- /dev/null +++ b/src/core/node_five_g_ue.ts @@ -0,0 +1,26 @@ +import { Node } from './node' +import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' + +class FiveGUE extends Node { + constructor() { + super() + this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) + } + asnMsgHandler = (msg: Message) => { + const payload: ASNMsgPayload = msg.payload + this.ASN = payload.asn + + // do something + postMessage({ + uid: 11, + type: PKT_TYPE.DATA, + mac_src: 12, + mac_dst: 2 + }) + + postMessage({ + type: MSG_TYPE.DONE + }) + } +} +new FiveGUE().Run() diff --git a/src/core/node_tsch.ts b/src/core/node_tsch.ts new file mode 100644 index 0000000..35ee4a3 --- /dev/null +++ b/src/core/node_tsch.ts @@ -0,0 +1,25 @@ +import { Node } from './node' +import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' + +class TSCHNode extends Node { + constructor() { + super() + this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) + this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) + } + asnMsgHandler = (msg: Message) => { + const payload: ASNMsgPayload = msg.payload + this.ASN = payload.asn + + // do something + + postMessage({ + type: MSG_TYPE.DONE + }) + } + dataPktHandler = (pkt: Packet) => { + // console.log('tsn', pkt) + } +} + +new TSCHNode().Run() diff --git a/src/core/node_tsn.ts b/src/core/node_tsn.ts new file mode 100644 index 0000000..224e4d2 --- /dev/null +++ b/src/core/node_tsn.ts @@ -0,0 +1,25 @@ +import { Node } from './node' +import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' + +class TSNNode extends Node { + constructor() { + super() + this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) + this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) + } + asnMsgHandler = (msg: Message) => { + const payload: ASNMsgPayload = msg.payload + this.ASN = payload.asn + + // do something + + postMessage({ + type: MSG_TYPE.DONE + }) + } + dataPktHandler = (pkt: Packet) => { + // console.log('tsn', pkt) + } +} + +new TSNNode().Run() diff --git a/src/core/typedefs.ts b/src/core/typedefs.ts new file mode 100644 index 0000000..a04ceac --- /dev/null +++ b/src/core/typedefs.ts @@ -0,0 +1,99 @@ +// enums are all captital and underscore, types are camel case + +export interface Config { + seed: number + num_nodes: number + grid_size: number +} + +export enum NODE_TYPE { + TSCH, + TSN, + FIVE_G_BS, + FIVE_G_UE, + END_SYSTEM +} + +export interface Node { + id: number + type: number + pos: [number, number] + neighbors: number[] + tx_cnt: number + rx_cnt: number + w: Worker | undefined +} + +export interface Link { + // undirected for visualization + uid: number // cantor pairing, uid=0.5*(v1+v2)*(v1+v2+1)+v2 + v1: number + v2: number + type: number +} + +export enum LINK_TYPE { + WIRED, + WIRELESS +} + +export interface Flow { + id: number + e2e_src: number + e2e_dst: number + deadline: number + period: number + workload: number +} + +// Packet is transfered among nodes, at data-link layer +export interface Packet { + uid: number + type: number + e2e_src: number + e2e_dst: number + mac_src: number + mac_dst: number + seq: number + asn: number + len: number + payload: any + + // for display on packet sniffer + id: number + children: any +} + +export enum PKT_TYPE { + DATA +} + +export enum ADDR { + BROADCAST = -1 +} + +// Message is used for direct communication (debug, cmd, stats) between nodes and controller +export interface Message { + type: number + id: number // node id + payload: any +} + +export enum MSG_TYPE { + INIT, + ASN, + DONE, // finished all activities of the current slot + FLOW, // install periodic flow + STAT +} + +export type MsgHandler = (msg: Message) => void +export type PktHandler = (pkt: Packet) => void + +export interface InitMsgPayload { + id: number + neighbors: number[] +} +export interface ASNMsgPayload { + asn: number +} diff --git a/src/hooks/useDrawMiniMap.ts b/src/hooks/useDrawMiniMap.ts index 3bba297..e921efe 100644 --- a/src/hooks/useDrawMiniMap.ts +++ b/src/hooks/useDrawMiniMap.ts @@ -3,8 +3,7 @@ import * as echarts from 'echarts' import { Network } from './useStates' import { MiniMapMode } from './useStates' -import { LINK_TYPE, NETWORK_TYPE } from '@/networks/common' -import type { TSCHNodeMeta } from '@/networks/TSCH/typedefs' +import { LINK_TYPE, NODE_TYPE } from '@/core/typedefs' export function useDrawMiniMap(chartDom: HTMLElement) { const chart = echarts.init(chartDom, { useDirtyRect: true }) @@ -19,8 +18,8 @@ export function useDrawMiniMap(chartDom: HTMLElement) { axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: false }, - min: -(Network.TopoConfig.value.grid_size + 10) / 2, - max: (Network.TopoConfig.value.grid_size + 10) / 2, + min: -(Network.Config.value.grid_size + 10) / 2, + max: (Network.Config.value.grid_size + 10) / 2, zlevel: -4 }, yAxis: { @@ -30,8 +29,8 @@ export function useDrawMiniMap(chartDom: HTMLElement) { axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: false }, - min: -(Network.TopoConfig.value.grid_size + 10) / 2, - max: (Network.TopoConfig.value.grid_size + 10) / 2, + min: -(Network.Config.value.grid_size + 10) / 2, + max: (Network.Config.value.grid_size + 10) / 2, zlevel: -4, inverse: true }, @@ -75,9 +74,6 @@ export function useDrawMiniMap(chartDom: HTMLElement) { initialTreeDepth: -1, edgeShape: 'polyline', label: { show: false }, - itemStyle: { - color: 'royalblue' - }, lineStyle: { width: 0.8, color: 'royalblue' @@ -98,14 +94,8 @@ export function useDrawMiniMap(chartDom: HTMLElement) { if (n.id == 0) continue option.series[0].data.push({ name: n.id, - value: n.pos - }) - } - for (const e of Network.EndSystems.value) { - option.series[0].data.push({ - name: e.id, - value: e.pos, - itemStyle: { color: 'green' } + value: n.pos, + itemStyle: { color: n.type == NODE_TYPE.END_SYSTEM ? 'green' : 'royalblue' } }) } for (const l of Object.values(Network.Links.value)) { @@ -133,23 +123,23 @@ export function useDrawMiniMap(chartDom: HTMLElement) { } } function drawMinimapTree() { - for (const n of Network.Nodes.value) { - if ( - n.type == NETWORK_TYPE.TSCH && - (n).joined && - (n).parent != 0 - ) { - if (treeNodes[n.id] == undefined) { - treeNodes[n.id] = { name: n.id, children: [] } - if (treeNodes[(n).parent] != undefined) { - treeNodes[(n).parent].children.push(treeNodes[n.id]) - } else { - treeNodes[(n).parent] = { name: n.id, children: [treeNodes[n.id]] } - } - } - } - } - option.series[1].data = [treeNodes[1]] + // for (const n of Network.Nodes.value) { + // if ( + // n.type == NETWORK_TYPE.TSCH && + // (n).joined && + // (n).parent != 0 + // ) { + // if (treeNodes[n.id] == undefined) { + // treeNodes[n.id] = { name: n.id, children: [] } + // if (treeNodes[(n).parent] != undefined) { + // treeNodes[(n).parent].children.push(treeNodes[n.id]) + // } else { + // treeNodes[(n).parent] = { name: n.id, children: [treeNodes[n.id]] } + // } + // } + // } + // } + // option.series[1].data = [treeNodes[1]] } drawMinimapScatter() @@ -166,15 +156,6 @@ export function useDrawMiniMap(chartDom: HTMLElement) { }, { deep: true } ) - watch( - Network.EndSystems, - () => { - drawMinimapScatter() - drawMinimapTree() - chart.setOption(option) - }, - { deep: true } - ) watch(MiniMapMode, () => { if (MiniMapMode.value == 'scatter') { diff --git a/src/hooks/useDrawTopology.ts b/src/hooks/useDrawTopology.ts index 823f984..7bc667e 100644 --- a/src/hooks/useDrawTopology.ts +++ b/src/hooks/useDrawTopology.ts @@ -1,6 +1,5 @@ import { watch } from 'vue' import { Network, SelectedNode, SignalEditTopology, SignalResetCamera } from './useStates' -import { TSCH_PKT_TYPE } from '@/networks/TSCH/typedefs' import * as THREE from 'three' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' @@ -8,14 +7,7 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import { DragControls } from 'three/examples/jsm/controls/DragControls.js' import Stats from 'three/examples/jsm/libs/stats.module' import * as TWEEN from '@tweenjs/tween.js' -import { - NODE_TYPE, - NETWORK_TYPE, - ADDR, - type Packet, - type LinkMeta, - LINK_TYPE -} from '@/networks/common' +import { NODE_TYPE, ADDR, type Packet, type Link, LINK_TYPE } from '@/core/typedefs' export function useDrawTopology(dom: HTMLElement) { const scene = new THREE.Scene() @@ -56,8 +48,8 @@ export function useDrawTopology(dom: HTMLElement) { const drawGround = () => { const geometry = new THREE.PlaneGeometry( - Network.TopoConfig.value.grid_size + 50, - Network.TopoConfig.value.grid_size + 50, + Network.Config.value.grid_size + 50, + Network.Config.value.grid_size + 50, 64, 64 ) @@ -117,18 +109,12 @@ export function useDrawTopology(dom: HTMLElement) { let drawnNodes: { [name: string]: any } = {} const drawNodes = () => { - switch (Network.Type) { - case NETWORK_TYPE.TSCH: - drawTSCHDevices() - break - case NETWORK_TYPE.TSN: - drawTSNDevices() - break - case NETWORK_TYPE.FIVE_G: - drawFiveGBS() - drawFiveGUE() - break - } + drawTSCHDevices() + + drawTSNDevices() + + drawFiveGBS() + drawFiveGUE() drawEndSystems() } @@ -251,47 +237,49 @@ export function useDrawTopology(dom: HTMLElement) { if (Network.Nodes.value.length == 0) { return } - const node = Network.Nodes.value[0] - // GLTF Loader - const loader = new GLTFLoader() - loader.load('/models/5_five_g_tower/scene.gltf', function (gltf: any) { - let modelGroup: any = {} - const model = gltf.scene - model.scale.set(6, 6, 6) - model.traverse(function (object: any) { - if (object instanceof THREE.Group) { - modelGroup = object - } - if (object.isMesh) { - object.castShadow = true // enable shadow casting - object.receiveShadow = true - object.material.color = new THREE.Color('#666') - object.userData.type = NODE_TYPE[node.type] - object.userData.node_id = 0 - } - }) - model.position.x = node.pos[0] - model.position.z = node.pos[1] - scene.add(model) + for (const node of Network.Nodes.value) { + if (node.type != NODE_TYPE.FIVE_G_BS) continue + // GLTF Loader + const loader = new GLTFLoader() + loader.load('/models/5_five_g_tower/scene.gltf', function (gltf: any) { + let modelGroup: any = {} + const model = gltf.scene + model.scale.set(6, 6, 6) + model.traverse(function (object: any) { + if (object instanceof THREE.Group) { + modelGroup = object + } + if (object.isMesh) { + object.castShadow = true // enable shadow casting + object.receiveShadow = true + object.material.color = new THREE.Color('#666') + object.userData.type = NODE_TYPE[node.type] + object.userData.node_id = 0 + } + }) + model.position.x = node.pos[0] + model.position.z = node.pos[1] + scene.add(model) - const box = new THREE.Box3().setFromObject(model) - const size = new THREE.Vector3() - box.getSize(size) + const box = new THREE.Box3().setFromObject(model) + const size = new THREE.Vector3() + box.getSize(size) - const label = createLabel(`5G-BS-1`) - label.position.set(model.position.x, size.y + 1, model.position.z) // Adjust the position as needed - scene.add(label) + const label = createLabel(`${NODE_TYPE[node.type]}-${node.id}`) + label.position.set(model.position.x, size.y + 1, model.position.z) // Adjust the position as needed + scene.add(label) - const { dragBox, dragBoxHelper } = createDragBox(node, model) + const { dragBox, dragBoxHelper } = createDragBox(node, model) - drawnNodes[`${NODE_TYPE[node.type]}-${node.id}`] = { - model, - label, - modelGroup, - dragBox, - dragBoxHelper - } - }) + drawnNodes[`${NODE_TYPE[node.type]}-${node.id}`] = { + model, + label, + modelGroup, + dragBox, + dragBoxHelper + } + }) + } } const drawFiveGUE = () => { // GLTF Loader @@ -404,23 +392,23 @@ export function useDrawTopology(dom: HTMLElement) { } // Load and place models - loadAndPlaceModel( - '/models/server/scene.gltf', - [0.08, 0.08, 0.08], - -Math.PI / 2, - 1.9, - 7, - NODE_TYPE.SERVER - ) + // loadAndPlaceModel( + // '/models/server/scene.gltf', + // [0.08, 0.08, 0.08], + // -Math.PI / 2, + // 1.9, + // 7, + // NODE_TYPE.END_SYSTEM + // ) loadAndPlaceModel( '/models/robotic_arm/scene.gltf', [0.004, 0.004, 0.004], -Math.PI / 2, 0, 7, - NODE_TYPE.ROBOT + NODE_TYPE.END_SYSTEM ) - loadAndPlaceModel('/models/sensor/scene.gltf', [2, 2, 2], -Math.PI / 2, 0, 5, NODE_TYPE.SENSOR) + // loadAndPlaceModel('/models/sensor/scene.gltf', [2, 2, 2], -Math.PI / 2, 0, 5, NODE_TYPE.END_SYSTEM) } const createDragBox = (node: any, model: any): any => { @@ -447,7 +435,7 @@ export function useDrawTopology(dom: HTMLElement) { return { dragBox, dragBoxHelper } } - let drawnLinks: { [uid: number]: { mesh: any; link: LinkMeta } } = {} + let drawnLinks: { [uid: number]: { mesh: any; link: Link } } = {} const drawLinks = () => { for (const l of Object.values(Network.Links.value)) { if (drawnLinks[l.uid] == undefined) { @@ -455,7 +443,7 @@ export function useDrawTopology(dom: HTMLElement) { } } } - const drawLink = (l: LinkMeta) => { + const drawLink = (l: Link) => { const p1 = new THREE.Vector3( Network.Nodes.value[l.v1].pos[0], 1.6, @@ -517,11 +505,12 @@ export function useDrawTopology(dom: HTMLElement) { let drawnBeaconPackets: { [uid: number]: any } = {} const drawPackets = () => { time = 0 // reset animation timer + console.log(Network.PacketsCurrent.value) for (const pkt of Network.PacketsCurrent.value) { - if (Network.Type == NETWORK_TYPE.TSCH && pkt.type == TSCH_PKT_TYPE.ACK) continue if (pkt.mac_dst != ADDR.BROADCAST) { drawUnicastPacket(pkt) - } else if (Network.Type == NETWORK_TYPE.TSCH && pkt.type == TSCH_PKT_TYPE.BEACON) { + } else { + //if (Network.Type == NETWORK_TYPE.TSCH && pkt.type == TSCH_PKT_TYPE.BEACON) { drawBeaconPacket(pkt) } } @@ -587,7 +576,8 @@ export function useDrawTopology(dom: HTMLElement) { } const drawBeaconPacket = (pkt: Packet) => { const geometry = new THREE.SphereGeometry( - Network.TopoConfig.value.tx_range, + // Network.Config.value.tx_range, + 10, 32, 32, 0, @@ -815,17 +805,10 @@ export function useDrawTopology(dom: HTMLElement) { event.object.position.y = 0 if (NODE_TYPE[event.object.userData.type] != undefined) { - if (event.object.userData.node_id <= Network.TopoConfig.value.num_nodes) { - Network.Nodes.value[event.object.userData.node_id].pos = [ - event.object.position.x, - event.object.position.z - ] - } else { - // is an end system - Network.EndSystems.value[ - event.object.userData.node_id - Network.TopoConfig.value.num_nodes - 1 - ].pos = [event.object.position.x, event.object.position.z] - } + Network.Nodes.value[event.object.userData.node_id].pos = [ + event.object.position.x, + event.object.position.z + ] for (const l of relatedLinks) { clearLink(l.link.uid) diff --git a/src/hooks/useStates.ts b/src/hooks/useStates.ts index 63fd629..f865ace 100644 --- a/src/hooks/useStates.ts +++ b/src/hooks/useStates.ts @@ -1,16 +1,23 @@ // global states, variables and configs import { ref } from 'vue' - +import { type Config } from '@/core/typedefs' // import { TSCHNetwork } from '@/networks/TSCH/network' // export const Network = new TSCHNetwork() -import { TSNNetwork } from '@/networks/TSN/network' -export const Network = new TSNNetwork() +// import { TSNNetwork } from '@/networks/TSN/network' +// export const Network = new TSNNetwork() // import { FiveGNetwork } from '@/networks/5G/network' // export const Network = new FiveGNetwork() +import { NetworkHub } from '@/core/network' +export const Network = new NetworkHub({ + seed: 123, + num_nodes: 15, + grid_size: 80 +}) + export const SelectedNode = ref(1) export const MiniMapMode = ref('scatter') export const SignalResetCamera = ref(1) diff --git a/src/networks/5G/network.ts b/src/networks/5G/network.ts deleted file mode 100644 index d23279a..0000000 --- a/src/networks/5G/network.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { ref } from 'vue' -import { Network, LINK_TYPE, NETWORK_TYPE, NODE_TYPE } from '../common' -import type { ScheduleConfig, FiveGNodeMeta } from './typedefs' -import { KDNode } from '../../utils/kdtree' - -export class FiveGNetwork extends Network { - constructor() { - super() - this.Type = NETWORK_TYPE.FIVE_G - // this.Schedule = ref([]) - this.SchConfig = ref({ - num_slots: 40 - }) - this.createNodes() - } - createNodes = () => { - this.Nodes = ref([]) - - // clear old nodes and webworkers - if (this.Nodes.value.length > 1) { - for (const n of this.Nodes.value) { - if (n.w != undefined) { - n.w.terminate() - } - } - } - const bs = { - id: 0, - type: NODE_TYPE.FIVE_G_BS, - pos: [0, 0], - neighbors: [], - queueLen: 0, - tx_cnt: 0, - rx_cnt: 0, - w: undefined - } - this.NetworkDevices.value.push(bs) - this.Nodes.value.push(bs) - - for (let i = 1; i <= this.TopoConfig.value.num_nodes; i++) { - const n = { - id: i, - type: NODE_TYPE.FIVE_G_UE, - pos: [ - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2, - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2 - ], - neighbors: [], - tx_cnt: 0, - rx_cnt: 0, - w: new Worker(new URL('@/networks/5G/node.ts', import.meta.url), { type: 'module' }) - } - this.KDTree.Insert(new KDNode(i, n.pos)) - - // add links - super.addLink(0, i, LINK_TYPE.WIRELESS) - - // send init msg - // n.w!.postMessage({ - // type: MSG_TYPE.INIT, - // payload: { - // id: n.id, - // pos: toRaw(n.pos), - // sch_config: toRaw(this.SchConfig.value) - // } - // }) - // handle msg/pkt from nodes - // n.w!.onmessage = (e: any) => { - // if ('uid' in e.data == false) { - // const msg: Message = e.data - // if (this.msgHandlers[msg.type] != undefined) { - // this.msgHandlers[msg.type](msg) - // } else { - // console.log('!! undefined message type:', msg.type) - // } - // } else { - // const pkt: Packet = e.data - // // check channel interference, only one packet can be transmitted on each channel in a slot - // if ( - // this.PacketsCurrent.value.filter((p) => p.ch == pkt.ch).length == 0 || - // pkt.type == PKT_TYPE.ACK - // ) { - // // must use this format for the detailedView function of el-table-v2 - // pkt.id = this.Packets.value.length - // pkt.children = [ - // { - // id: `${this.Packets.value.length}-detail-content`, - // detail: JSON.stringify(pkt.payload).replace(/"/g, '') - // } - // ] - - // this.Packets.value.push(pkt) - // this.PacketsCurrent.value.push(pkt) - - // if (pkt.dst == ADDR.BROADCAST) { - // for (const nn of this.Nodes.value) { - // // check if in tx_range - // const distance = Math.sqrt( - // Math.pow(n.pos[0] - nn.pos[0], 2) + Math.pow(n.pos[1] - nn.pos[1], 2) - // ) - // if (nn.id > 0 && nn.id != n.id && distance <= this.TopoConfig.value.tx_range) { - // nn.w!.postMessage(pkt) - // } - // } - // } else { - // const nn = this.Nodes.value[pkt.dst] - // if (nn != undefined) { - // // check if in tx_range - // const distance = Math.sqrt( - // Math.pow(n.pos[0] - nn.pos[0], 2) + Math.pow(n.pos[1] - nn.pos[1], 2) - // ) - // if (distance <= this.TopoConfig.value.tx_range) { - // nn.w!.postMessage(pkt) - // } - // } - // } - // } - // } - // } - this.Nodes.value.push(n) - this.NetworkDevices.value.push(n) - } - } -} diff --git a/src/networks/5G/node.ts b/src/networks/5G/node.ts deleted file mode 100644 index a582796..0000000 --- a/src/networks/5G/node.ts +++ /dev/null @@ -1,11 +0,0 @@ -class FiveGNode { - id: number = 0 - ports: { [p: number]: number } = {} - constructor() {} - - Run() { - // console.log(this.id) - } -} - -new FiveGNode().Run() diff --git a/src/networks/5G/typedefs.ts b/src/networks/5G/typedefs.ts deleted file mode 100644 index ba4bc4e..0000000 --- a/src/networks/5G/typedefs.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { NodeMeta } from '../common' - -export interface ScheduleConfig { - num_slots: number -} - -export interface FiveGNodeMeta extends NodeMeta {} - -export enum FIVE_G_PKT_TYPE { - ACK, - DATA -} diff --git a/src/networks/TSCH/network.ts b/src/networks/TSCH/network.ts deleted file mode 100644 index 12e8017..0000000 --- a/src/networks/TSCH/network.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { ref, watch, toRaw } from 'vue' -import type { - Cell, - TSCHNodeMeta, - ScheduleConfig, - TSCH_INIT_MSG_PAYLOAD, - ASSOC_RSP_PKT_PAYLOAD -} from './typedefs' -import { TSCH_PKT_TYPE, CELL_TYPES } from './typedefs' -import { Network, ADDR, MSG_TYPE, LINK_TYPE, NETWORK_TYPE, NODE_TYPE } from '../common' -import type { Packet, Message, ASN_MSG_PAYLOAD, MsgHandler } from '../common' -import { KDNode } from '../../utils/kdtree' - -export class TSCHNetwork extends Network { - doneCnt = 0 - - msgHandlers: { [type: number]: MsgHandler } = {} - - constructor() { - super() - this.Type = NETWORK_TYPE.TSCH - this.Schedule = ref([]) - this.SchConfig = ref({ - num_slots: 40, - num_channels: 8, - beacon_channel: 1, - beacon_period: 1, - shared_channel: 2, - num_shared_slots: 8 - }) - - this.registerMsgHandler(MSG_TYPE.DONE, this.doneMsgHandler) - this.registerMsgHandler(MSG_TYPE.STAT, this.statMsgHandler) - this.registerMsgHandler(MSG_TYPE.ASSOC_REQ, this.assocReqMsgHandler) - - this.createNodes() - this.createSchedule() - - watch(this.ASN, () => { - this.doneCnt = 0 - this.PacketsCurrent.value = [] - if (this.NetworkDevices.value.length > 1) { - for (const n of this.NetworkDevices.value) { - if (n.w != undefined) { - n.w.postMessage({ - type: MSG_TYPE.ASN, - id: n.id, - payload: { asn: this.ASN.value } - }) - } - } - } - }) - watch(this.SignalReset, () => { - this.ASN.value = 0 - this.Packets.value = [] - this.PacketsCurrent.value = [] - this.createNodes() - this.createSchedule() - }) - } - - registerMsgHandler(type: number, handler: MsgHandler) { - this.msgHandlers[type] = handler - } - doneMsgHandler = () => { - if (++this.doneCnt == this.TopoConfig.value.num_nodes) { - this.SlotDone.value = true - } - } - statMsgHandler = (msg: Message) => { - const payload = msg.payload - const node = msg.id - - if (!this.NetworkDevices.value[node].joined && payload.joined) { - // join succeed - // console.log(node, 'joined') - this.NetworkDevices.value[node].joined = payload.joined - this.NetworkDevices.value[node].parent = payload.parent - this.NetworkDevices.value[node].neighbors.push(payload.parent) - this.NetworkDevices.value[payload.parent].neighbors.push(node) - super.addLink(node, payload.parent, LINK_TYPE.WIRELESS) - } - - this.NetworkDevices.value[node].queueLen = payload.queue.length - this.NetworkDevices.value[node].tx_cnt = payload.tx_cnt - this.NetworkDevices.value[node].rx_cnt = payload.rx_cnt - this.NetworkDevices.value[node].rank = payload.rank - } - assocReqMsgHandler = (msg: Message) => { - const new_node: number = msg.payload.id - const parent: number = msg.payload.parent - - // to improve - const topo_check = !this.NetworkDevices.value[new_node].joined - - if (topo_check) { - const p = { - type: TSCH_PKT_TYPE.ASSOC_RSP, - uid: Math.floor(Math.random() * 0xffff), - ch: 2, - mac_src: 0, - mac_dst: ADDR.ROOT, - seq: 0, - len: 7, - payload: { - permit: true, - id: new_node, - parent: parent, - cell_list: this.assignMgmtCells(new_node, parent) - } - } - this.NetworkDevices.value[ADDR.ROOT].w!.postMessage(p) - } - } - - createNodes = () => { - this.NetworkDevices = ref([{}]) - console.log(this.NetworkDevices.value[0].id == undefined) - // clear old nodes - if (this.NetworkDevices.value.length > 1) { - for (const n of this.NetworkDevices.value) { - if (n.w != undefined) { - n.w.terminate() - } - } - } - - for (let i = 1; i <= this.TopoConfig.value.num_nodes; i++) { - const n = { - id: i, - type: NODE_TYPE.TSCH, - pos: <[number, number]>[ - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2, - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2 - ], - joined: i == ADDR.ROOT, - parent: 0, - neighbors: [], - queueLen: 0, - tx_cnt: 0, - rx_cnt: 0, - rank: 0, - w: new Worker(new URL('@/networks/TSCH/node.ts', import.meta.url), { type: 'module' }) - } - this.KDTree.Insert(new KDNode(i, n.pos)) - - this.Nodes.value.push(n) - this.NetworkDevices.value.push(n) - - // send init msg - n.w!.postMessage({ - type: MSG_TYPE.INIT, - payload: { - id: n.id, - pos: toRaw(n.pos), - sch_config: toRaw(this.SchConfig.value) - } - }) - // handle msg/pkt from nodes - n.w!.onmessage = (e: any) => { - if ('uid' in e.data == false) { - const msg: Message = e.data - if (this.msgHandlers[msg.type] != undefined) { - this.msgHandlers[msg.type](msg) - } else { - console.log('!! undefined message type:', msg.type) - } - } else { - const pkt: Packet = e.data - // check channel interference, only one packet can be transmitted on each channel in a slot - if ( - this.PacketsCurrent.value.filter((p) => p.ch == pkt.ch).length == 0 || - pkt.type == TSCH_PKT_TYPE.ACK - ) { - // must use this format for the detailedView function of el-table-v2 - pkt.id = this.Packets.value.length - pkt.children = [ - { - id: `${this.Packets.value.length}-detail-content`, - detail: JSON.stringify(pkt.payload).replace(/"/g, '') - } - ] - - this.Packets.value.push(pkt) - this.PacketsCurrent.value.push(pkt) - - if (pkt.mac_dst == ADDR.BROADCAST) { - for (const nn of this.NetworkDevices.value) { - if (nn.id == undefined) continue - // check if in tx_range - const distance = Math.sqrt( - Math.pow(n.pos[0] - nn.pos[0], 2) + Math.pow(n.pos[1] - nn.pos[1], 2) - ) - if (nn.id > 0 && nn.id != n.id && distance <= this.TopoConfig.value.tx_range) { - nn.w!.postMessage(pkt) - } - } - } else { - const nn = this.NetworkDevices.value[pkt.mac_dst] - if (nn != undefined) { - // check if in tx_range - const distance = Math.sqrt( - Math.pow(n.pos[0] - nn.pos[0], 2) + Math.pow(n.pos[1] - nn.pos[1], 2) - ) - if (distance <= this.TopoConfig.value.tx_range) { - nn.w!.postMessage(pkt) - } - } - } - } - } - } - } - } - - createSchedule = () => { - this.Schedule.value = new Array(this.SchConfig.value.num_slots + 1) - for (let s = 1; s <= this.SchConfig.value.num_slots; s++) { - this.Schedule.value[s] = new Array(this.SchConfig.value.num_channels + 1) - } - - // initial cells - this.Schedule.value[1][this.SchConfig.value.beacon_channel] = { - type: CELL_TYPES.MGMT, - slot: 1, - ch: this.SchConfig.value.beacon_channel, - mac_src: ADDR.ROOT, - mac_dst: ADDR.BROADCAST - } - for (let slot = 2; slot < 2 + this.SchConfig.value.num_shared_slots; slot++) { - this.Schedule.value[slot][this.SchConfig.value.shared_channel] = { - type: CELL_TYPES.SHARED, - slot: slot, - ch: this.SchConfig.value.shared_channel, - mac_src: ADDR.ANY, - mac_dst: ADDR.ANY - } - } - } - assignMgmtCells = (node: number, parent: number): Cell[] => { - const cells: Cell[] = [] - // beacon - const beacon_cell = this.findIdleCell(CELL_TYPES.MGMT, node, ADDR.BROADCAST) - const tx_cell = this.findIdleCell(CELL_TYPES.MGMT, node, parent) - const tx_cell2 = this.findIdleCell(CELL_TYPES.MGMT, node, parent) - const rx_cell = this.findIdleCell(CELL_TYPES.MGMT, parent, node) - const rx_cell2 = this.findIdleCell(CELL_TYPES.MGMT, parent, node) - - if ( - beacon_cell != undefined && - tx_cell != undefined && - tx_cell2 != undefined && - rx_cell != undefined && - rx_cell2 != undefined - ) { - cells.push(beacon_cell, tx_cell, tx_cell2, rx_cell, rx_cell2) - } - return cells - } - findIdleCell = (type: number, src: number, dst: number): Cell | undefined => { - for (let slot = 2; slot <= this.SchConfig.value.num_slots; slot++) { - // check conflict - if ( - this.Schedule.value[slot].filter( - (x: any) => - x.src == src || - x.src == dst || - x.mac_dst == src || - x.mac_dst == dst || - x.type == CELL_TYPES.SHARED - ).length > 0 - ) { - continue - } - if (dst == ADDR.BROADCAST) { - if (this.Schedule.value[slot][this.SchConfig.value.beacon_channel] == undefined) { - const cell = { - type: type, - slot: slot, - ch: this.SchConfig.value.beacon_channel, - mac_src: src, - mac_dst: dst - } - this.Schedule.value[slot][this.SchConfig.value.beacon_channel] = cell - return cell - } - } else { - for (let ch = 2; ch <= this.SchConfig.value.num_channels; ch++) { - if (this.Schedule.value[slot][ch] == undefined) { - const cell = { - type: type, - slot: slot, - ch: ch, - mac_src: src, - mac_dst: dst - } - this.Schedule.value[slot][ch] = cell - return cell - } - } - } - } - return undefined - } -} diff --git a/src/networks/TSCH/node.ts b/src/networks/TSCH/node.ts deleted file mode 100644 index 1d40b62..0000000 --- a/src/networks/TSCH/node.ts +++ /dev/null @@ -1,323 +0,0 @@ -// run as a webworker -import type { - ScheduleConfig, - Cell, - TSCH_INIT_MSG_PAYLOAD, - BEACON_PKT_PAYLOAD, - ASSOC_REQ_PKT_PAYLOAD, - ASSOC_RSP_PKT_PAYLOAD -} from './typedefs' -import { CELL_TYPES, TSCH_PKT_TYPE } from './typedefs' -import { - type Packet, - type Message, - type MsgHandler, - type PktHandler, - ADDR, - MSG_TYPE, - type ASN_MSG_PAYLOAD -} from '../common' - -class TSCHNode { - id: number = 0 - joined: boolean = false - joining: boolean = false - parent: number = 0 - rank: number = 0 - children: number[] = [] - sch_config: ScheduleConfig = {} - schedule: Cell[][] = [] - queue: Packet[] = [] - joinedNeighbors: { [id: number]: boolean } = {} - routingTable: { [mac_dst: number]: number } = {} - pkt_seq: number = 0 - tx_cnt: number = 0 - rx_cnt: number = 0 - ASN: number = 0 - msgHandlers: { [type: number]: MsgHandler } = {} - pktHandlers: { [type: number]: PktHandler } = {} - - constructor() { - this.joinedNeighbors[ADDR.BROADCAST] = true - - this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) - this.registerMsgHandler(MSG_TYPE.INIT, this.initMsgHandler) - - this.registerPktHandler(TSCH_PKT_TYPE.ACK, this.ackPktHandler) - this.registerPktHandler(TSCH_PKT_TYPE.BEACON, this.beaconPktHandler) - this.registerPktHandler(TSCH_PKT_TYPE.ASSOC_REQ, this.assocReqPktHandler) - this.registerPktHandler(TSCH_PKT_TYPE.ASSOC_RSP, this.assocRspPktHandler) - } - - registerMsgHandler(type: number, handler: MsgHandler) { - this.msgHandlers[type] = handler - } - registerPktHandler(type: number, handler: PktHandler) { - this.pktHandlers[type] = handler - } - - run() { - onmessage = (e: any) => { - if ('uid' in e.data == false) { - const msg: Message = e.data - if (this.msgHandlers[msg.type] != undefined) { - this.msgHandlers[msg.type](msg) - } else { - console.log('!! undefined message type:', msg.type) - } - } else { - const pkt: Packet = e.data - if (this.checkSchRx(pkt)) { - this.respondAck(pkt) - - // update routing table - this.routingTable[pkt.mac_src] = pkt.mac_src - - if (this.pktHandlers[pkt.type] != undefined) { - this.pktHandlers[pkt.type](pkt) - } else { - console.log('!! undefined packet type:', pkt.type) - } - } - } - } - } - - respondAck(pkt: Packet) { - if ( - pkt.mac_dst != ADDR.BROADCAST && - pkt.mac_src != ADDR.CONTROLLER && - pkt.type != TSCH_PKT_TYPE.ACK - ) { - const ack: Packet = { ...pkt } - ack.type = TSCH_PKT_TYPE.ACK - ack.mac_src = this.id - ack.mac_dst = pkt.mac_src - ack.len = 0 - ack.payload = {} - // send immediately, in the same time slot - postMessage(ack) - } - } - checkSchTx() { - const slot = this.ASN % this.sch_config.num_slots || this.sch_config.num_slots - if (this.queue.length > 0) { - const pkt = this.queue[0] - if (this.joined && this.joinedNeighbors[pkt.mac_dst]) { - // use dedicate cells - for (let ch = 1; ch <= this.schedule[slot].length; ch++) { - const cell = this.schedule[slot][ch] - if (cell != undefined && cell.mac_dst == pkt.mac_dst) { - pkt.ch = ch - pkt.asn = this.ASN - pkt.len = JSON.stringify(pkt.payload).length - postMessage(pkt) - this.tx_cnt++ - - // no need of ack, transmission has finished - if (pkt.mac_dst == ADDR.BROADCAST) { - this.queue.shift() - } - break - } - } - } else { - // use shared cells - const cell = this.schedule[slot][this.sch_config.shared_channel] - if (cell != undefined && cell.type == CELL_TYPES.SHARED) { - pkt.ch = this.sch_config.shared_channel - pkt.asn = this.ASN - pkt.len = JSON.stringify(pkt.payload).length - postMessage(pkt) - this.tx_cnt++ - } - } - } - } - checkSchRx(pkt: Packet): boolean { - if (!this.joined || pkt.type == TSCH_PKT_TYPE.ACK || pkt.mac_src == ADDR.CONTROLLER) { - return true - } - const slot = this.ASN % this.sch_config.num_slots || this.sch_config.num_slots - for (const cell of this.schedule[slot]) { - if ( - cell != undefined && - (cell.mac_src == pkt.mac_src || - pkt.mac_dst == ADDR.BROADCAST || - cell.type == CELL_TYPES.SHARED) - ) { - this.rx_cnt++ - return true - } - } - return false - } - - asnMsgHandler = (msg: Message) => { - const payload: ASN_MSG_PAYLOAD = msg.payload - this.ASN = payload.asn - if ( - this.joined && - this.ASN % (this.sch_config.beacon_period * this.sch_config.num_slots) == 1 - ) { - this.queue.push({ - uid: Math.floor(Math.random() * 0xffff), - type: TSCH_PKT_TYPE.BEACON, - mac_src: this.id, - mac_dst: -1, - seq: ++this.pkt_seq, - len: 1, - payload: { pan_id: 1, rank: this.rank } - }) - } - this.checkSchTx() - - // done - postMessage({ - type: MSG_TYPE.DONE - }) - postMessage({ - type: MSG_TYPE.STAT, - id: this.id, - payload: JSON.parse(JSON.stringify(this)) - }) - } - initMsgHandler = (msg: Message) => { - const payload: TSCH_INIT_MSG_PAYLOAD = msg.payload - this.id = payload.id - this.joined = this.id == ADDR.ROOT - this.sch_config = payload.sch_config - this.schedule = new Array(this.sch_config!.num_slots + 1) - for (let s = 1; s <= this.sch_config!.num_slots; s++) { - this.schedule[s] = new Array(this.sch_config!.num_channels + 1) - } - // init schedule - if (this.id == ADDR.ROOT) { - this.schedule[1][this.sch_config.beacon_channel] = { - slot: 1, - ch: this.sch_config.beacon_channel, - mac_src: this.id, - mac_dst: ADDR.BROADCAST - } - } - // shared slots - for (let s = 2; s < 2 + this.sch_config.num_shared_slots; s++) { - this.schedule[s][this.sch_config.shared_channel] = { - type: CELL_TYPES.SHARED, - slot: s, - ch: this.sch_config.shared_channel, - mac_src: this.id, - mac_dst: ADDR.ANY - } - } - } - - ackPktHandler = (ack: Packet) => { - if (this.queue[0] != undefined && this.queue[0].uid == ack.uid) { - if ( - this.queue[0].type == TSCH_PKT_TYPE.ASSOC_RSP && - this.queue[0].payload.parent == this.id && - this.queue[0].payload.permit - ) { - this.joinedNeighbors[this.queue[0].payload.id] = true - } - this.queue.shift() - } - } - beaconPktHandler = (pkt: Packet) => { - if (!this.joined && !this.joining) { - const payload: BEACON_PKT_PAYLOAD = pkt.payload - this.joining = true - this.rank = payload.rank + 1 - const assoc_req = { - uid: Math.floor(Math.random() * 0xffff), - type: TSCH_PKT_TYPE.ASSOC_REQ, - mac_src: this.id, - mac_dst: pkt.mac_src, - seq: ++this.pkt_seq, - payload: { id: this.id, parent: pkt.mac_src } - } - this.queue.push(assoc_req) - } - } - assocReqPktHandler = (pkt: Packet) => { - const payload: ASSOC_REQ_PKT_PAYLOAD = pkt.payload - // update routing table - if (this.routingTable[payload.id] == undefined) { - this.routingTable[payload.id] = pkt.mac_src - } - if (this.id != ADDR.ROOT) { - // forward to parent - pkt.mac_src = this.id - pkt.mac_dst = this.parent - pkt.seq = ++this.pkt_seq - this.queue.push(pkt) - } else { - // send to controller - postMessage({ - type: MSG_TYPE.ASSOC_REQ, - id: this.id, - payload: pkt.payload - }) - } - } - assocRspPktHandler = (pkt: Packet) => { - const payload: ASSOC_RSP_PKT_PAYLOAD = pkt.payload - if (payload.id == this.id) { - if (payload.permit) { - this.joined = true - this.joining = false - this.parent = payload.parent - this.joinedNeighbors[this.parent] = true - this.routingTable[ADDR.CONTROLLER] = this.parent - - for (const cell of payload.cell_list) { - this.schedule[cell.slot][cell.ch] = cell - } - - postMessage({ - type: MSG_TYPE.STAT, - id: this.id, - payload: JSON.parse(JSON.stringify(this)) - }) - - this.queue.push({ - uid: Math.floor(Math.random() * 0xffff), - type: TSCH_PKT_TYPE.BEACON, - mac_src: this.id, - mac_dst: -1, - seq: ++this.pkt_seq, - len: 1, - payload: { pan_id: 1, rank: this.rank } - }) - } else { - this.joining = false - } - } else if (payload.parent == this.id) { - if (payload.permit) { - this.children.push(payload.id) - for (const cell of payload.cell_list) { - this.schedule[cell.slot][cell.ch] = cell - } - } - } - // need forwarding - if (payload.id != this.id) { - pkt.mac_src = this.id - if (this.children.indexOf(pkt.payload.id) > -1) { - pkt.mac_dst = pkt.payload.id - pkt.seq = ++this.pkt_seq - this.queue.push(pkt) - } else { - // not related to this - if (this.routingTable[pkt.payload.id] != undefined) { - pkt.mac_dst = this.routingTable[pkt.payload.id] - pkt.seq = ++this.pkt_seq - this.queue.push(pkt) - } - } - } - } -} - -new TSCHNode().run() diff --git a/src/networks/TSCH/typedefs.ts b/src/networks/TSCH/typedefs.ts deleted file mode 100644 index cd36411..0000000 --- a/src/networks/TSCH/typedefs.ts +++ /dev/null @@ -1,64 +0,0 @@ -// all types and enums here - -import type { INIT_MSG_PAYLOAD, NodeMeta } from '../common' - -export interface TSCHNodeMeta extends NodeMeta { - joined: boolean - parent: number - queueLen: number - rank: number -} - -export interface ScheduleConfig { - num_slots: number - num_channels: number - beacon_period: number // every {} slotframes - beacon_channel: number - num_shared_slots: number - shared_channel: number -} - -// basic unit of the communication schedule -export interface Cell { - type: number - slot: number - ch: number - mac_src: number - mac_dst: number -} - -export enum CELL_TYPES { - SHARED, - MGMT, - DATA -} - -export interface TSCH_INIT_MSG_PAYLOAD extends INIT_MSG_PAYLOAD { - sch_config: ScheduleConfig -} - -export enum TSCH_PKT_TYPE { - ACK, - BEACON, - ASSOC_REQ, - ASSOC_RSP, - SCH_UPDATE, - DATA -} - -export interface BEACON_PKT_PAYLOAD { - pan_id: number - rank: number -} - -export interface ASSOC_REQ_PKT_PAYLOAD { - id: number - parent: number -} - -export interface ASSOC_RSP_PKT_PAYLOAD { - permit: boolean - id: number - parent: number - cell_list: Cell[] -} diff --git a/src/networks/TSN/es.ts b/src/networks/TSN/es.ts deleted file mode 100644 index 5690967..0000000 --- a/src/networks/TSN/es.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { TSN_PKT_TYPE } from './typedefs' -import { MSG_TYPE, type Message, type MsgHandler, type Packet, type PktHandler } from '../common' - -// end system, run as a webworker -class EndSystem { - id: number = 0 - neighbor: number = -1 - - msgHandlers: { [type: number]: MsgHandler } = {} - pktHandlers: { [type: number]: PktHandler } = {} - - constructor() { - this.registerMsgHandler(MSG_TYPE.INIT, this.initMsgHandler) - this.registerMsgHandler(MSG_TYPE.FLOW, this.flowMsgHandler) - - this.registerPktHandler(TSN_PKT_TYPE.DATA, this.dataPktHandler) - } - - registerMsgHandler(type: number, handler: MsgHandler) { - this.msgHandlers[type] = handler - } - registerPktHandler(type: number, handler: PktHandler) { - this.pktHandlers[type] = handler - } - run() { - onmessage = (e: any) => { - if ('uid' in e.data == false) { - const msg: Message = e.data - if (this.msgHandlers[msg.type] != undefined) { - this.msgHandlers[msg.type](msg) - // test - postMessage({ - uid: 11, - type: 0, - mac_src: 11, - mac_dst: 3 - }) - } else { - // console.log('!! undefined message type:', msg.type) - } - } else { - const pkt: Packet = e.data - - if (this.pktHandlers[pkt.type] != undefined) { - this.pktHandlers[pkt.type](pkt) - } else { - // console.log('!! undefined packet type:', pkt.type) - } - } - } - } - initMsgHandler = () => {} - flowMsgHandler = () => {} - dataPktHandler = () => {} -} - -new EndSystem().run() diff --git a/src/networks/TSN/network.ts b/src/networks/TSN/network.ts deleted file mode 100644 index 991f659..0000000 --- a/src/networks/TSN/network.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { ref, toRaw } from 'vue' -import { - Network, - NETWORK_TYPE, - NODE_TYPE, - type Message, - LINK_TYPE, - MSG_TYPE, - type NodeMeta, - type INIT_MSG_PAYLOAD, - type Packet -} from '../common' -import { type ScheduleConfig, type TSNNodeMeta, type TSN_INIT_MSG_PAYLOAD } from './typedefs' -import { KDNode } from '../../utils/kdtree' - -export class TSNNetwork extends Network { - InPorts: any - OutPorts: any - - constructor() { - super() - this.Type = NETWORK_TYPE.TSN - // this.Schedule = ref([]) - this.SchConfig = ref({ - num_slots: 40 - }) - this.createNodes() - this.createEndSystems() - } - createNodes = () => { - this.NetworkDevices = ref([]) - - // clear old nodes and webworkers - if (this.Nodes.value.length > 1) { - for (const n of this.Nodes.value) { - if (n.w != undefined) { - n.w.terminate() - } - } - } - this.Nodes.value = [ - { - id: 0, - type: NODE_TYPE.TSN, - pos: [0, 0], - neighbors: [], - queueLen: 0, - tx_cnt: 0, - rx_cnt: 0, - w: undefined - } - ] // placeholder - - for (let i = 1; i <= this.TopoConfig.value.num_nodes; i++) { - const n = { - id: i, - type: NODE_TYPE.TSN, - pos: [ - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2, - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2 - ], - neighbors: [], - tx_cnt: 0, - rx_cnt: 0, - w: new Worker(new URL('@/networks/TSN/node.ts', import.meta.url), { type: 'module' }) - } - // send init msg - n.w!.postMessage({ - type: MSG_TYPE.INIT, - payload: { - id: n.id, - pos: toRaw(n.pos), - neighbors: [], - sch_config: toRaw(this.SchConfig.value) - } - }) - - this.NetworkDevices.value.push(n) - this.Nodes.value.push(n) - this.KDTree.Insert(new KDNode(i, n.pos)) - } - - for (let i = 1; i <= this.TopoConfig.value.num_nodes; i++) { - this.Nodes.value[i].neighbors = this.KDTree.FindKNearest( - this.Nodes.value[i].pos, - 3, - this.TopoConfig.value.tx_range - ) - this.Nodes.value[i].neighbors.forEach((n: number) => { - super.addLink(i, n, LINK_TYPE.WIRED) - }) - } - } - createEndSystems = () => { - // initialize ref array if it does not already exist - this.EndSystems = ref([]) - - for ( - let i = 1 + this.TopoConfig.value.num_nodes; - i <= this.TopoConfig.value.num_es + this.TopoConfig.value.num_nodes; - i++ - ) { - const es = { - id: i, - type: Math.floor(4 + this.Rand.next() * 3), - pos: [ - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2, - Math.floor(this.Rand.next() * this.TopoConfig.value.grid_size) - - this.TopoConfig.value.grid_size / 2 - ], - tx_cnt: 0, - rx_cnt: 0, - neighbors: [], - w: new Worker(new URL('@/networks/es.ts', import.meta.url), { type: 'module' }) - } - console.log(i) - es.neighbors = this.KDTree.FindKNearest(es.pos, 1, this.TopoConfig.value.grid_size) - if (es.neighbors.length > 0) { - this.addLink(es.id, es.neighbors[0], LINK_TYPE.WIRED) - } - - es.w!.postMessage({ - type: MSG_TYPE.INIT, - payload: { - id: es.id, - pos: toRaw(es.pos) - } - }) - // handle msg/pkt from end systems - es.w!.onmessage = (e: any) => { - if ('uid' in e.data == false) { - const msg: Message = e.data - console.log(msg) - } else { - const pkt: Packet = e.data - this.Nodes.value[pkt.mac_dst].w!.postMessage(pkt) - this.Packets.value.push(pkt) - this.PacketsCurrent.value.push(pkt) - this.SlotDone.value = true - } - } - - this.EndSystems.value.push(es) - this.Nodes.value.push(es) - } - } -} diff --git a/src/networks/TSN/node.ts b/src/networks/TSN/node.ts deleted file mode 100644 index c1194d9..0000000 --- a/src/networks/TSN/node.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - type Packet, - type Message, - type MsgHandler, - type PktHandler, - MSG_TYPE, - type ASN_MSG_PAYLOAD -} from '../common' -import { TSN_PKT_TYPE, type TSN_INIT_MSG_PAYLOAD } from './typedefs' - -class TSNNode { - id: number = 0 - ASN: number = 0 - ports: { [p: number]: number } = {} - msgHandlers: { [type: number]: MsgHandler } = {} - pktHandlers: { [type: number]: PktHandler } = {} - - constructor() { - this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) - this.registerMsgHandler(MSG_TYPE.INIT, this.initMsgHandler) - - this.registerPktHandler(TSN_PKT_TYPE.DATA, this.dataPktHandler) - } - registerMsgHandler(type: number, handler: MsgHandler) { - this.msgHandlers[type] = handler - } - registerPktHandler(type: number, handler: PktHandler) { - this.pktHandlers[type] = handler - } - - run() { - onmessage = (e: any) => { - if ('uid' in e.data == false) { - const msg: Message = e.data - if (this.msgHandlers[msg.type] != undefined) { - this.msgHandlers[msg.type](msg) - } else { - // console.log('!! undefined message type:', msg.type) - } - } else { - const pkt: Packet = e.data - - if (this.pktHandlers[pkt.type] != undefined) { - this.pktHandlers[pkt.type](pkt) - } else { - // console.log('!! undefined packet type:', pkt.type) - } - } - } - } - - initMsgHandler = (msg: Message) => { - const payload: TSN_INIT_MSG_PAYLOAD = msg.payload - this.id = payload.id - } - asnMsgHandler = (msg: Message) => { - const payload: ASN_MSG_PAYLOAD = msg.payload - this.ASN = payload.asn - - // this.checkSchTx() - - // done - postMessage({ - type: MSG_TYPE.DONE - }) - postMessage({ - type: MSG_TYPE.STAT, - id: this.id, - payload: JSON.parse(JSON.stringify(this)) - }) - } - dataPktHandler = (pkt: Packet) => { - console.log(pkt) - } -} - -new TSNNode().run() diff --git a/src/networks/TSN/typedefs.ts b/src/networks/TSN/typedefs.ts deleted file mode 100644 index 6758360..0000000 --- a/src/networks/TSN/typedefs.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { INIT_MSG_PAYLOAD, NodeMeta } from '../common' - -export interface ScheduleConfig { - num_slots: number -} - -export interface TSNNodeMeta extends NodeMeta {} - -export enum TSN_PKT_TYPE { - DATA -} - -export interface TSN_INIT_MSG_PAYLOAD extends INIT_MSG_PAYLOAD { - neighbors: number[] - sch_config: ScheduleConfig -} diff --git a/src/networks/common.ts b/src/networks/common.ts deleted file mode 100644 index fda9212..0000000 --- a/src/networks/common.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { ref, type Ref } from 'vue' -import { SeededRandom } from '@/utils/rand' -import { KDTree } from '@/utils/kdtree' - -export enum NETWORK_TYPE { - TSCH, - TSN, - FIVE_G -} - -export enum NODE_TYPE { - TSCH, - TSN, - FIVE_G_BS, - FIVE_G_UE, - SERVER, - ROBOT, - SENSOR -} - -export interface NodeMeta { - id: number - type: number - pos: [number, number] - neighbors: number[] - tx_cnt: number - rx_cnt: number - w: Worker | undefined -} - -export interface LinkMeta { - // undirected link for visualization - uid: number - v1: number - v2: number - type: number -} - -export enum LINK_TYPE { - WIRED, - WIRELESS -} - -export interface FlowMeta { - id: number - e2e_src: number - e2e_dst: number - deadline: number - period: number - workload: number -} - -// Packet is transfered among nodes, at data-link layer -export interface Packet { - uid: number - type: number - ch: number - e2e_src: number - e2e_dst: number - mac_src: number - mac_dst: number - seq: number - asn: number - len: number - payload: any - - // for display on packet sniffer - id: number - children: any -} - -// Message is used for direct communication (debug, cmd, stats) between nodes and controller -export interface Message { - type: number - id: number // node id - payload: any -} - -export enum MSG_TYPE { - INIT, - ASN, - DONE, // finished all activities of the current slot - FLOW, // install periodic flow - STAT, - ASSOC_REQ -} - -export interface INIT_MSG_PAYLOAD { - id: number -} - -export interface ASN_MSG_PAYLOAD { - asn: number -} - -export enum ADDR { - CONTROLLER = 0, - ROOT = 1, - BROADCAST = -1, - ANY = -2 -} - -export type MsgHandler = (msg: Message) => void -export type PktHandler = (pkt: Packet) => void - -export interface TopologyConfig { - seed: number - num_nodes: number - num_es: number - grid_size: number - tx_range: number -} - -export class Network { - ID: number - Type: number - Nodes = ref([]) // nodes and endsystems, for visualization - NetworkDevices: any // tsn bridges, tsch relay or 5g ue/bs - EndSystems = ref([]) - Links = ref<{ [uid: number]: LinkMeta }>([]) - Flows = ref([]) - TopoConfig: Ref - KDTree: KDTree - SchConfig: any - Schedule: any - Packets = ref([]) - ASN = ref(0) - asnTimer: any - PacketsCurrent = ref([]) - - SignalReset = ref(0) - SlotDone = ref(false) - Running = ref(false) - SlotDuration = ref(750) - - Rand: SeededRandom - - constructor() { - this.ID = 1 - this.Type = -1 - this.TopoConfig = ref({ - seed: 1, - num_nodes: 10, - num_es: 1, - grid_size: 80, - tx_range: 25 - }) - this.Rand = new SeededRandom(this.TopoConfig.value.seed) - this.Nodes.value.push({}) // placeholder - this.KDTree = new KDTree() - } - - addLink(v1: number, v2: number, type: number) { - if (v1 > v2) { - ;[v1, v2] = [v2, v1] - } - // Cantor pairing - const uid = 0.5 * (v1 + v2) * (v1 + v2 + 1) + v2 - if (this.Links.value[uid] == undefined) { - this.Links.value[uid] = { uid, v1, v2, type } - } - } - - Run = () => { - this.Step() - this.Running.value = true - this.asnTimer = setInterval(() => { - this.ASN.value++ - this.SlotDone.value = false - }, this.SlotDuration.value) - } - Step = () => { - this.ASN.value++ - this.SlotDone.value = false - } - Pause = () => { - this.Running.value = false - clearInterval(this.asnTimer) - } - Reset = () => { - this.Running.value = false - clearInterval(this.asnTimer) - this.SignalReset.value++ - } -} diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 4735a39..8371b7b 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -3,7 +3,7 @@ import MenuBar from '@/components/MenuBar.vue' import ControlPanel from '@/components/ControlPanel.vue' import Topology from '@/components/NetworkTopology.vue' import PacketSniffer from '@/components/PacketSniffer.vue' -import ScheduleTable from '@/components/ScheduleTable.vue' +// import ScheduleTable from '@/components/ScheduleTable.vue' import NodeStats from '@/components/NodeStats.vue' import MiniMap from '@/components/MiniMap.vue' import SettingsPanel from '@/components/SettingsPanel.vue' @@ -37,9 +37,9 @@ import FlowsPanelVue from '@/components/FlowsPanel.vue' - + From acffecc5a528b9294f483d13980a5f68c15f0b9a Mon Sep 17 00:00:00 2001 From: AmyangXYZ Date: Sat, 30 Dec 2023 17:46:21 -0500 Subject: [PATCH 2/4] adding topo edit toolbox --- src/components/TopoEditToolbox.vue | 33 ++++++++ src/core/network.ts | 130 ++++++++++++++++++++++++----- src/core/node.ts | 1 + src/core/node_end_system.ts | 21 ++++- src/core/node_five_g_bs.ts | 1 + src/core/node_five_g_ue.ts | 6 -- src/core/node_tsch.ts | 15 +++- src/hooks/useDrawTopology.ts | 39 ++++++--- src/hooks/useStates.ts | 11 +-- src/utils/kdtree.ts | 2 +- src/views/HomeView.vue | 12 ++- 11 files changed, 214 insertions(+), 57 deletions(-) create mode 100644 src/components/TopoEditToolbox.vue diff --git a/src/components/TopoEditToolbox.vue b/src/components/TopoEditToolbox.vue new file mode 100644 index 0000000..6ba6c0d --- /dev/null +++ b/src/components/TopoEditToolbox.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/src/core/network.ts b/src/core/network.ts index 153a462..7dbe2b5 100644 --- a/src/core/network.ts +++ b/src/core/network.ts @@ -1,5 +1,5 @@ -import { ref, type Ref, watch } from 'vue' -import { KDTree } from '@/utils/kdtree' +import { ref, type Ref, watch, toRaw } from 'vue' +import { KDNode, KDTree } from '@/utils/kdtree' import { NODE_TYPE, type Config, @@ -9,7 +9,9 @@ import { type Packet, type Message, MSG_TYPE, - type ASNMsgPayload + type ASNMsgPayload, + LINK_TYPE, + type InitMsgPayload } from './typedefs' import { SeededRandom } from '@/utils/rand' @@ -22,7 +24,7 @@ export class NetworkHub { PacketsCurrent = ref([]) ASN = ref(0) // absolute slot number Rand: SeededRandom - KDTree = new KDTree() // to find nearest neighbors + kdTree: KDTree // to find nearest neighbors asnTimer: any SignalReset = ref(0) @@ -35,12 +37,14 @@ export class NetworkHub { this.Config = ref(config) this.Nodes = ref([{ id: 0 }]) // placeholder to let node_id start from 1 this.Rand = new SeededRandom(this.Config.value.seed) - - this.createNodes() + this.kdTree = new KDTree() + // this.createNodes() + this.AddNode(1, NODE_TYPE.END_SYSTEM) watch(this.ASN, () => { this.doneCnt = 0 this.PacketsCurrent.value = [] + if (this.Nodes.value.length > 1) { for (const n of this.Nodes.value) { if (n.w != undefined) { @@ -55,11 +59,60 @@ export class NetworkHub { }) } - createNodes() { + AddNode(id: number, type: number) { + const n = { + id: id, + type: type, + pos: [ + Math.floor(this.Rand.next() * this.Config.value.grid_size) - + this.Config.value.grid_size / 2, + Math.floor(this.Rand.next() * this.Config.value.grid_size) - this.Config.value.grid_size / 2 + ], + neighbors: [], + tx_cnt: 0, + rx_cnt: 0, + w: undefined + } + const worker_path = `./node_${NODE_TYPE[n.type].toLowerCase()}.ts` + n.w = new Worker(new URL(worker_path, import.meta.url), { type: 'module' }) + this.Nodes.value.push(n) + } + + FindNeighbors() { + this.kdTree = new KDTree() + this.Links.value = [] + for (const n of this.Nodes.value) { + if (n.id == 0 || n.w == undefined || n.type == NODE_TYPE.END_SYSTEM) continue + this.kdTree.Insert(new KDNode(n.id, n.pos)) + } + + for (const n of this.Nodes.value) { + if (n.id == 0 || n.w == undefined) continue + + if (n.type == NODE_TYPE.TSN || n.type == NODE_TYPE.TSCH) { + n.neighbors = this.kdTree.FindKNearest(n.pos, 4, 20) + } else { + n.neighbors = this.kdTree.FindKNearest(n.pos, 1, this.Config.value.grid_size) + } + + n.w.postMessage({ + type: MSG_TYPE.INIT, + id: n.id, + payload: { id: n.id, neighbors: toRaw(n.neighbors) } + }) + + n.neighbors.forEach((nn: number) => { + this.AddLink(n.id, nn) + }) + } + } + + private createNodes() { for (let i = 1; i <= this.Config.value.num_nodes; i++) { const n = { id: i, - type: Math.floor((this.Rand.next() * Object.values(NODE_TYPE).length) / 2), + // type: Math.floor((this.Rand.next() * Object.values(NODE_TYPE).length) / 2), + type: [NODE_TYPE.TSN, NODE_TYPE.END_SYSTEM][Math.floor(this.Rand.next() * 2)], pos: [ Math.floor(this.Rand.next() * this.Config.value.grid_size) - this.Config.value.grid_size / 2, @@ -73,10 +126,14 @@ export class NetworkHub { w: undefined } + if (n.type != NODE_TYPE.END_SYSTEM) { + this.kdTree.Insert(new KDNode(n.id, n.pos)) + } + const worker_path = `./node_${NODE_TYPE[n.type].toLowerCase()}.ts` n.w = new Worker(new URL(worker_path, import.meta.url), { type: 'module' }) - n!.w.onmessage = (e: any) => { + n.w.onmessage = (e: any) => { if ('uid' in e.data) { this.handlePkt(e.data) } else { @@ -85,21 +142,25 @@ export class NetworkHub { } this.Nodes.value.push(n) } - } - AddNode(id: number, type: number) { - const n = { id: id, type: type } - this.Nodes.value.push(n) - } + for (const n of this.Nodes.value) { + if (n.id == 0 || n.w == undefined) continue - AddLink(v1: number, v2: number, type: number) { - if (v1 > v2) { - ;[v1, v2] = [v2, v1] - } - // Cantor pairing - const uid = 0.5 * (v1 + v2) * (v1 + v2 + 1) + v2 - if (this.Links.value[uid] == undefined) { - this.Links.value[uid] = { uid, v1, v2, type } + if (n.type == NODE_TYPE.TSN || n.type == NODE_TYPE.TSCH) { + n.neighbors = this.kdTree.FindKNearest(n.pos, 4, 20) + } else { + n.neighbors = this.kdTree.FindKNearest(n.pos, 1, this.Config.value.grid_size) + } + + n.w.postMessage({ + type: MSG_TYPE.INIT, + id: n.id, + payload: { id: n.id, neighbors: toRaw(n.neighbors) } + }) + + n.neighbors.forEach((nn: number) => { + this.AddLink(n.id, nn) + }) } } @@ -110,7 +171,8 @@ export class NetworkHub { if (++this.doneCnt == this.Config.value.num_nodes) { this.SlotDone.value = true } - console.log(this.doneCnt, this.SlotDone.value) + break + case MSG_TYPE.STAT: break } } @@ -122,6 +184,28 @@ export class NetworkHub { this.PacketsCurrent.value.push(pkt) } + AddLink(v1: number, v2: number) { + if (v1 > v2) { + ;[v1, v2] = [v2, v1] + } + // Cantor pairing + const uid = 0.5 * (v1 + v2) * (v1 + v2 + 1) + v2 + + let type: number = LINK_TYPE.WIRELESS + if ( + this.Nodes.value[v1].type == NODE_TYPE.TSN || + this.Nodes.value[v1].type == NODE_TYPE.END_SYSTEM || + this.Nodes.value[v2].type == NODE_TYPE.TSN || + this.Nodes.value[v2].type == NODE_TYPE.END_SYSTEM + ) { + type = LINK_TYPE.WIRED + } + + if (this.Links.value[uid] == undefined) { + this.Links.value[uid] = { uid, v1, v2, type } + } + } + Run = () => { this.Step() this.Running.value = true diff --git a/src/core/node.ts b/src/core/node.ts index a4a5d7e..c7e9bab 100644 --- a/src/core/node.ts +++ b/src/core/node.ts @@ -2,6 +2,7 @@ import { type Message, type MsgHandler, type Packet, type PktHandler } from './t export class Node { id: number = 0 + neighbors: number[] = [] pkt_seq: number = 0 tx_cnt: number = 0 rx_cnt: number = 0 diff --git a/src/core/node_end_system.ts b/src/core/node_end_system.ts index 5700522..5d9e0bf 100644 --- a/src/core/node_end_system.ts +++ b/src/core/node_end_system.ts @@ -1,17 +1,36 @@ import { Node } from './node' -import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' +import { + PKT_TYPE, + type Message, + type Packet, + MSG_TYPE, + type ASNMsgPayload, + type InitMsgPayload +} from './typedefs' class EndSystem extends Node { constructor() { super() + this.registerMsgHandler(MSG_TYPE.INIT, this.initMsgHandler) this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) } + initMsgHandler = (msg: Message) => { + const payload: InitMsgPayload = msg.payload + this.id = payload.id + this.neighbors = payload.neighbors + } asnMsgHandler = (msg: Message) => { const payload: ASNMsgPayload = msg.payload this.ASN = payload.asn // do something + postMessage({ + uid: Math.floor(Math.random() * 0xffff), + type: PKT_TYPE.DATA, + mac_src: this.id, + mac_dst: this.neighbors[0] + }) postMessage({ type: MSG_TYPE.DONE diff --git a/src/core/node_five_g_bs.ts b/src/core/node_five_g_bs.ts index 4c41f86..9e7b173 100644 --- a/src/core/node_five_g_bs.ts +++ b/src/core/node_five_g_bs.ts @@ -7,6 +7,7 @@ class FiveGBS extends Node { this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) } + asnMsgHandler = (msg: Message) => { const payload: ASNMsgPayload = msg.payload this.ASN = payload.asn diff --git a/src/core/node_five_g_ue.ts b/src/core/node_five_g_ue.ts index b68883d..91c4525 100644 --- a/src/core/node_five_g_ue.ts +++ b/src/core/node_five_g_ue.ts @@ -11,12 +11,6 @@ class FiveGUE extends Node { this.ASN = payload.asn // do something - postMessage({ - uid: 11, - type: PKT_TYPE.DATA, - mac_src: 12, - mac_dst: 2 - }) postMessage({ type: MSG_TYPE.DONE diff --git a/src/core/node_tsch.ts b/src/core/node_tsch.ts index 35ee4a3..c6dc3c9 100644 --- a/src/core/node_tsch.ts +++ b/src/core/node_tsch.ts @@ -1,10 +1,18 @@ import { Node } from './node' -import { PKT_TYPE, type Message, type Packet, MSG_TYPE, type ASNMsgPayload } from './typedefs' +import { + PKT_TYPE, + type Message, + type Packet, + MSG_TYPE, + type ASNMsgPayload, + type InitMsgPayload +} from './typedefs' class TSCHNode extends Node { constructor() { super() this.registerMsgHandler(MSG_TYPE.ASN, this.asnMsgHandler) + this.registerMsgHandler(MSG_TYPE.INIT, this.initMsgHandler) this.registerPktHandler(PKT_TYPE.DATA, this.dataPktHandler) } asnMsgHandler = (msg: Message) => { @@ -17,6 +25,11 @@ class TSCHNode extends Node { type: MSG_TYPE.DONE }) } + initMsgHandler = (msg: Message) => { + const payload: InitMsgPayload = msg.payload + this.id = payload.id + this.neighbors = payload.neighbors + } dataPktHandler = (pkt: Packet) => { // console.log('tsn', pkt) } diff --git a/src/hooks/useDrawTopology.ts b/src/hooks/useDrawTopology.ts index 7bc667e..1953080 100644 --- a/src/hooks/useDrawTopology.ts +++ b/src/hooks/useDrawTopology.ts @@ -1,5 +1,11 @@ import { watch } from 'vue' -import { Network, SelectedNode, SignalEditTopology, SignalResetCamera } from './useStates' +import { + Network, + SelectedNode, + SignalEditTopology, + SignalResetCamera, + SignalUpdateTopology +} from './useStates' import * as THREE from 'three' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' @@ -107,14 +113,14 @@ export function useDrawTopology(dom: HTMLElement) { return sprite } - let drawnNodes: { [name: string]: any } = {} + let drawnNodes: { [id: number]: any } = {} const drawNodes = () => { drawTSCHDevices() - drawTSNDevices() + // drawTSNDevices() - drawFiveGBS() - drawFiveGUE() + // drawFiveGBS() + // drawFiveGUE() drawEndSystems() } @@ -148,7 +154,8 @@ export function useDrawTopology(dom: HTMLElement) { box.getSize(size) for (const node of Network.Nodes.value) { - if (node.id == 0 || node.type != NODE_TYPE.TSCH) continue + if (node.id == 0 || node.type != NODE_TYPE.TSCH || drawnNodes[node.id] != undefined) + continue let modelGroup: any = {} const model = modelTemplate.clone() @@ -170,7 +177,7 @@ export function useDrawTopology(dom: HTMLElement) { const { dragBox, dragBoxHelper } = createDragBox(node, model) - drawnNodes[`${NODE_TYPE[node.type]}-${node.id}`] = { + drawnNodes[node.id] = { model, label, modelGroup, @@ -223,7 +230,7 @@ export function useDrawTopology(dom: HTMLElement) { const { dragBox, dragBoxHelper } = createDragBox(node, model) - drawnNodes[`${NODE_TYPE[node.type]}-${node.id}`] = { + drawnNodes[node.id] = { model, label, modelGroup, @@ -271,7 +278,7 @@ export function useDrawTopology(dom: HTMLElement) { const { dragBox, dragBoxHelper } = createDragBox(node, model) - drawnNodes[`${NODE_TYPE[node.type]}-${node.id}`] = { + drawnNodes[node.id] = { model, label, modelGroup, @@ -320,7 +327,7 @@ export function useDrawTopology(dom: HTMLElement) { const { dragBox, dragBoxHelper } = createDragBox(node, model) - drawnNodes[`${NODE_TYPE[node.type]}-${node.id}`] = { + drawnNodes[node.id] = { model, label, modelGroup, @@ -363,7 +370,7 @@ export function useDrawTopology(dom: HTMLElement) { box.getSize(size) for (const es of Network.Nodes.value) { - if (es.type !== typeVal) continue + if (es.type !== typeVal || drawnNodes[es.id] != undefined) continue const model = modelTemplate.clone() model.position.set(es.pos[0], positionY, es.pos[1]) @@ -381,7 +388,7 @@ export function useDrawTopology(dom: HTMLElement) { const { dragBox, dragBoxHelper } = createDragBox(es, model) - drawnNodes[`${NODE_TYPE[es.type]}-${es.id}`] = { + drawnNodes[es.id] = { model, label, modelGroup: model, @@ -429,8 +436,8 @@ export function useDrawTopology(dom: HTMLElement) { objectsToDrag.push(dragBox) const dragBoxHelper = new THREE.BoxHelper(dragBox, 'skyblue') - dragBoxHelper.visible = false dragBoxHelper.castShadow = false + dragBoxHelper.visible = SignalEditTopology.value scene.add(dragBoxHelper) return { dragBox, dragBoxHelper } } @@ -712,6 +719,12 @@ export function useDrawTopology(dom: HTMLElement) { drawLinks() animate() + watch(SignalUpdateTopology, () => { + drawNodes() + clearLinks() + drawLinks() + }) + watch(Network.SlotDone, () => { if (Network.SlotDone.value) { drawLinks() diff --git a/src/hooks/useStates.ts b/src/hooks/useStates.ts index f865ace..ffa8582 100644 --- a/src/hooks/useStates.ts +++ b/src/hooks/useStates.ts @@ -2,18 +2,10 @@ import { ref } from 'vue' import { type Config } from '@/core/typedefs' -// import { TSCHNetwork } from '@/networks/TSCH/network' -// export const Network = new TSCHNetwork() - -// import { TSNNetwork } from '@/networks/TSN/network' -// export const Network = new TSNNetwork() - -// import { FiveGNetwork } from '@/networks/5G/network' -// export const Network = new FiveGNetwork() import { NetworkHub } from '@/core/network' export const Network = new NetworkHub({ - seed: 123, + seed: 17, num_nodes: 15, grid_size: 80 }) @@ -26,4 +18,5 @@ export const SignalShowSettings = ref(false) export const SignalShowSchedule = ref(false) export const SignalShowStatistics = ref(false) export const SignalEditTopology = ref(false) +export const SignalUpdateTopology = ref(0) export const SelectedScenario = ref('') // added by Jack Medrek diff --git a/src/utils/kdtree.ts b/src/utils/kdtree.ts index 24cd324..e5ab5ee 100644 --- a/src/utils/kdtree.ts +++ b/src/utils/kdtree.ts @@ -49,7 +49,7 @@ export class KDTree { if (current == undefined) return const d = this._distanceSquared(current.pos, pos) - if (current.pos != pos && d <= range) { + if (current.pos[0] != pos[0] && current.pos[1] != pos[1] && d <= range) { if (nearestNodes.length < k) { nearestNodes.push(current) } else if (d < this._distanceSquared(pos, nearestNodes[k - 1].pos)) { diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 8371b7b..4492876 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -4,11 +4,13 @@ import ControlPanel from '@/components/ControlPanel.vue' import Topology from '@/components/NetworkTopology.vue' import PacketSniffer from '@/components/PacketSniffer.vue' // import ScheduleTable from '@/components/ScheduleTable.vue' -import NodeStats from '@/components/NodeStats.vue' +// import NodeStats from '@/components/NodeStats.vue' import MiniMap from '@/components/MiniMap.vue' import SettingsPanel from '@/components/SettingsPanel.vue' import ScenariosPanel from '@/components/ScenariosPanel.vue' import FlowsPanelVue from '@/components/FlowsPanel.vue' +import TopoEditToolbox from '@/components/TopoEditToolbox.vue' +import { SignalEditTopology } from '@/hooks/useStates'