Wi-Fi网络的离散事件模型
该模型模拟具有有限数量的并行通道的路由器和产生网络流量的多个客户端设备的操作。 该模拟器演示了无线网络的基本原理,包括资源竞争、排队和信道加载动态
. 我们模型的主要元素如下所述。 结构 **Событие**它是离散事件模型的基本要素,表示系统中在特定时间发生的单独事件。 它存储四个主要属性。 领域 время 它包含一个时间戳,指示事件何时应该发生在条件模拟单元中,这允许调度程序按时间顺序排列事件。 领域 тип 使用符号定义事件类别,例如 :генерация_запроса, :запрос 或 :конец_передачи,这决定了应该调用哪个处理函数。 领域 устройство 通过存储特定客户端设备的字符串标识符(例如,"Laptop_1"),将事件绑定到其源或接收者。 最后,场 данные 它旨在以命名字段的元组或另一类型的形式存储与事件相关的任何附加信息;例如,对于请求事件,唯一的请求标识符及其创建时间可以存储在这里。 该结构是不可变的,这保证了在整个仿真过程中创建事件数据后的完整性。
结构 **Устройство**模拟Wi-Fi网络上的客户端硬件,如笔记本电脑或智能手机。 它是可变的,因为它的状态必须在模拟期间更新。 该结构包括三个字段。 领域 имя 它包含设备的唯一字符串标识符,例如"Smartphone_2",用于将事件和请求与其源相关联。 领域 интенсивность 通过存储连续数据请求生成之间的平均时间(以条件模拟单位)来确定设备的行为;这个数字越低,设备在网络上创建负载的频率越高。 领域 счетчик_запросов 它跟踪自模拟开始以来此设备生成的请求总数;该值随着每个新请求而递增,通常用于为这些请求生成唯一标识符。
结构 **Роутер**表示资源有限的Wi-Fi热点模型。 这是一个多变的结构,因为通道和队列的状态不断变化。 它包括三个字段。 领域 каналы 通过存储一个整数来定义路由器的最大带宽,该整数指示可用于同时数据传输的并行连接或通道数(例如,2)。 领域 занятые_каналы 跟踪当前负载,显示当前有多少可用信道正忙于处理数据传输。 领域 очередь 它是一个字符串向量(Vector{String}),其中每个字符串是来自试图开始传输但所有通道都忙的设备的请求的标识符(例如,"笔记本电脑_1-03");这个向量实现了FIFO(先进先出)逻辑,其中第一个传入的请求将在队列中提供服务。 该结构有一个构造函数 Роутер(каналы::Int) 其中,在创建新对象时,自动将占用的通道数初始化为零,并创建空队列,确保模型的正确初始状态。
结构 **ПланировщикСобытий**它是离散事件模拟引擎的核心,负责管理所有操作的时间和顺序。 这是一个可变的结构。 它包含两个字段。 领域 события 它是所有计划但尚未处理的事件的按时间顺序排列的集合,存储为结构的向量。 Событие. 为了在时间上有效地提取下一个事件,向量保持在时间排序状态,这允许您从向量的末尾快速提取最早的事件。 领域 текущее_время 它存储仿真进展到的虚拟时间点;每次检索和处理新事件时都会更新该值,并作为整个模型的全局时钟。 该结构有一个构造函数 ПланировщикСобытий(),其初始化空事件列表并将初始模拟时间设置为0.0。 主模拟循环不断地请求调度程序的下一个事件,更新 текущее_время 在该事件发生时,并将该事件传送给适当的功能进行处理,从而模拟系统中的时间流逝。
struct Событие
время::Float64
тип::Symbol
устройство::String
данные::Any
end
mutable struct Устройство
имя::String
интенсивность::Float64
счетчик_запросов::Int
end
mutable struct Роутер
каналы::Int
занятые_каналы::Int
очередь::Vector{String}
function Роутер(каналы::Int)
new(каналы, 0, String[])
end
end
mutable struct ПланировщикСобытий
события::Vector{Событие}
текущее_время::Float64
function ПланировщикСобытий()
new(Событие[], 0.0)
end
end
功能 добавить_событие! 在调度程序中放置一个新事件,并以相反的顺序按时间对整个列表进行排序,确保最早的事件始终位于向量的末尾。 功能 следующее_событие 从向量的末尾检索并返回此最早的事件,如果列表为空,则返回 nothing,其信号模拟结束。
function добавить_событие!(планировщик::ПланировщикСобытий, событие::Событие)
push!(планировщик.события, событие)
sort!(планировщик.события, by=x->x.время, rev=true)
end
function следующее_событие(планировщик::ПланировщикСобытий)
isempty(планировщик.события) ? nothing : pop!(планировщик.события)
end
功能 генерировать_запрос! 从设备创建一个新的网络请求:它增加请求计数器,生成一个唯一标识符,并立即安排一个事件,如 :запрос 对于当前时间,然后计算一个随机间隔(基于特征 интенсивность 设备)用于规划下一个事件 :генерация_запроса,从而模拟装置的周期性活动。
功能 обработать_запрос 接收数据传输请求并尝试为其分配路由器资源。 如果有空闲信道,则占用它,计算随机传输持续时间,并调度事件。 :конец_передачи 为未来,并显示成功消息。 如果所有通道都忙,该函数将请求ID放在路由器队列中,并显示一条关于备用状态的消息。
功能 завершить_передачу 数据传输完成后释放路由器通道,显示相应的消息。 然后它检查队列中待处理的请求:如果队列不为空,函数检索下一个请求,立即为其分配新释放的通道,安排其完成并输出消息,从而实现连续处理和先进先出原则(先进先出)。
function генерировать_запрос!(устройство::Устройство, планировщик::ПланировщикСобытий,
текущее_время::Float64, роутер::Роутер)
устройство.счетчик_запросов += 1
запрос_ид = "$(устройство.имя)-$(lpad(устройство.счетчик_запросов, 2, '0'))"
событие_запрос = Событие(
текущее_время,
:запрос,
устройство.имя,
(идентификатор=запрос_ид, время_запроса=текущее_время)
)
добавить_событие!(планировщик, событие_запрос)
следующее_время = текущее_время + устройство.интенсивность * (0.5 + rand())
следующее_событие = Событие(
следующее_время,
:генерация_запроса,
устройство.имя,
nothing
)
добавить_событие!(планировщик, следующее_событие)
println("⏰ t=$(round(текущее_время, digits=2)) | $(устройство.имя) | Создан запрос $запрос_ид")
end
function обработать_запрос(устройство::Устройство, данные, планировщик::ПланировщикСобытий,
роутер::Роутер, текущее_время::Float64)
запрос_ид = данные.идентификатор
время_запроса = данные.время_запроса
if роутер.занятые_каналы < роутер.каналы
роутер.занятые_каналы += 1
время_передачи = 0.5 + rand() * 2.0
время_окончания = текущее_время + время_передачи
событие = Событие(
время_окончания,
:конец_передачи,
устройство.имя,
(идентификатор=запрос_ид, время_начала=текущее_время,
время_передачи=время_передачи)
)
добавить_событие!(планировщик, событие)
println("✅ t=$(round(текущее_время, digits=2)) | $запрос_ид | Канал получен (ожидание: $(round(текущее_время-время_запроса, digits=2)))")
else
push!(роутер.очередь, запрос_ид)
println("⌛ t=$(round(текущее_время, digits=2)) | $запрос_ид | Поставлен в очередь (длина: $(length(роутер.очередь)))")
end
end
function завершить_передачу(устройство::Устройство, данные, планировщик::ПланировщикСобытий,
роутер::Роутер, текущее_время::Float64)
роутер.занятые_каналы -= 1
запрос_ид = данные.идентификатор
время_начала = данные.время_начала
время_передачи = данные.время_передачи
println("🔓 t=$(round(текущее_время, digits=2)) | $запрос_ид | Канал освобожден (передача: $(round(время_передачи, digits=2)))")
if !isempty(роутер.очередь)
следующий_запрос = popfirst!(роутер.очередь)
имя_устройства = split(следующий_запрос, "-")[1]
время_передачи = 0.5 + rand() * 2.0
время_окончания = текущее_время + время_передачи
событие = Событие(
время_окончания,
:конец_передачи,
имя_устройства,
(идентификатор=следующий_запрос, время_начала=текущее_время,
время_передачи=время_передачи)
)
добавить_событие!(планировщик, событие)
роутер.занятые_каналы += 1
println("➡️ t=$(round(текущее_время, digits=2)) | $следующий_запрос | Извлечен из очереди и начал передачу")
end
end
功能 запустить_симуляцию 他是该计划的主要董事总经理。 它初始化所有系统组件:它创建一个事件调度器,一个具有设定数量的通道的路由器,以及具有随机特性的设定数量的客户端设备,之后它调度第一个请
然后该函数启动离散事件模拟的主循环,其连续地从调度器提取最早的事件,促进系统的虚拟时间并且,取决于事件的类型,将其重定向到处理三个关键: генерировать_запрос!, обработать_запрос 或 завершить_передачу. 循环运行直到指定的模拟时间到期或直到事件用完,之后函数输出最终的网络统计信息并将其作为字典返回。
function запустить_симуляцию(длительность=30.0, количество_устройств=4, каналы_роутера=2)
println("="^50)
println("WI-FI网络模拟(纯JULIA)")
println("="^50)
println("参数:")
println(" Длительность: $длительность ед. времени")
println(" Устройства: $количество_устройств")
println(" Каналы роутера: $каналы_роутера")
println("-"^50)
планировщик = ПланировщикСобытий()
роутер = Роутер(каналы_роутера)
устройства_dict = Dict{String, Устройство}()
типы_устройств = ["Ноутбук", "Смартфон", "Планшет", "ТВ"]
for i in 1:количество_устройств
тип = типы_устройств[mod1(i, length(типы_устройств))]
имя = "$(тип)_$i"
интенсивность = 2.0 + rand() * 3.0
устройство = Устройство(имя, интенсивность, 0)
устройства_dict[имя] = устройство
println("➕ Добавлено: $имя (интервал: $(round(интенсивность, digits=2)))")
первое_событие = Событие(
rand() * 1.0,
:генерация_запроса,
имя,
nothing
)
добавить_событие!(планировщик, первое_событие)
end
println("-"^50)
println("Запуск симуляции...\n")
статистика = Dict{String, Int}()
статистика["всего_запросов"] = 0
статистика["передач_завершено"] = 0
while планировщик.текущее_время < длительность
событие = следующее_событие(планировщик)
if событие === nothing
break
end
планировщик.текущее_время = событие.время
if планировщик.текущее_время > длительность
break
end
if !haskey(устройства_dict, событие.устройство)
continue
end
устройство = устройства_dict[событие.устройство]
if событие.тип == :генерация_запроса
генерировать_запрос!(устройство, планировщик, планировщик.текущее_время, роутер)
статистика["всего_запросов"] += 1
elseif событие.тип == :запрос
обработать_запрос(устройство, событие.данные, планировщик, роутер, планировщик.текущее_время)
elseif событие.тип == :конец_передачи
завершить_передачу(устройство, событие.данные, планировщик, роутер, планировщик.текущее_время)
статистика["передач_завершено"] += 1
end
end
println("\n" * "-"^50)
println("模拟统计")
println("-"^50)
println("Общее время симуляции: $(round(планировщик.текущее_время, digits=2))")
println("Всего запросов: $(статистика["всего_запросов"])")
println("Передач завершено: $(статистика["передач_завершено"])")
println("Осталось в очереди: $(length(роутер.очередь))")
println("Занято каналов в конце: $(роутер.занятые_каналы)")
println("="^50)
return статистика
end
запустить_симуляцию(25.0, 5, 2)
该仿真演示了网络在周期性过载状态下的操作:有五个活动设备和只有两个路由器通道,系统中不断形成一个等待队列,其峰值长度可以达到几个请求,这表明当前负载系统性地缺乏带宽。 操作时间有一个明确的划分:在初始阶段,当队列尚未形成时,请求几乎立即得到服务,但随着负载的积累,大多数设备被迫花费相当长的时间等待空闲信道,其中一些设备产生强度更高的请求,产生了不成比例的大负载,增加了平均等待时间。 对于网络的所有成员。 虽然整个系统处理绝大多数请求,但在会话结束时出现队列和所有通道的不断使用表明它正在发挥其能力的极限,在这种情况下,请求强度或设备数量的任何增加都将导致延迟的急剧增加和总体效率的下降。
结论
此实现演示了如何在没有外部依赖的情况下在纯Julia上构建功能齐全的离散事件模型。 此代码为试验网络算法提供了良好的基础。 您可以轻松修改它以探索不同的场景:添加不同的服务类,设备优先级,丢包模型或实时可视化。
这种实现的主要优点是完全控制仿真逻辑和不存在依赖关系,这使得代码成为训练和原型网络模型的理想选择。