///|
pub(all) struct NetworkTopology[T] {
mgr : @event.EventManager
priv nodes : Map[Int, NodeStatus]
priv links : Map[String, NetworkLink]
priv mailboxes : Map[Int, Array[Packet[T]]]
mut next_seed : Int
}
///|
fn link_key(src : Int, dst : Int) -> String {
"\{src}->\{dst}"
}
///|
pub fn[T] NetworkTopology::new(
mgr : @event.EventManager,
seed : Int,
) -> NetworkTopology[T] {
{
mgr,
nodes: Map::from_array([]),
links: Map::from_array([]),
mailboxes: Map::from_array([]),
next_seed: if seed <= 0 {
1234567
} else {
seed
},
}
}
///|
fn[T] NetworkTopology::deterministic_random(
self : NetworkTopology[T],
) -> Double {
self.next_seed = (self.next_seed * 1103515245 + 12345) & 0x7FFFFFFF
Double::from_int(self.next_seed) / 2147483648.0
}
///|
pub fn[T] NetworkTopology::add_node(
self : NetworkTopology[T],
node_id : Int,
) -> Unit {
self.nodes.set(node_id, Online)
self.mailboxes.set(node_id, [])
}
///|
pub fn[T] NetworkTopology::set_node_status(
self : NetworkTopology[T],
node_id : Int,
status : NodeStatus,
) -> Unit {
self.nodes.set(node_id, status)
}
///|
pub fn[T] NetworkTopology::get_node_status(
self : NetworkTopology[T],
node_id : Int,
) -> NodeStatus? {
self.nodes.get(node_id)
}
///|
pub fn[T] NetworkTopology::add_link(
self : NetworkTopology[T],
src : Int,
dst : Int,
latency : Double,
drop_probability : Double,
) -> Unit {
let link = NetworkLink::new(src, dst, latency, drop_probability)
self.links.set(link_key(src, dst), link)
}
///|
pub fn[T] NetworkTopology::get_link(
self : NetworkTopology[T],
src : Int,
dst : Int,
) -> NetworkLink? {
self.links.get(link_key(src, dst))
}
///|
pub fn[T] NetworkTopology::send_packet(
self : NetworkTopology[T],
src : Int,
dst : Int,
payload : T,
priority : Int,
on_deliver : (Packet[T]) -> Unit,
) -> Bool {
match (self.nodes.get(src), self.nodes.get(dst)) {
(Some(Online), Some(Online)) => ()
_ => return false
}
let key = link_key(src, dst)
match self.links.get(key) {
None => false
Some(link) => {
link.packets_sent = link.packets_sent + 1
let rand_val = self.deterministic_random()
if rand_val < link.drop_probability {
link.packets_dropped = link.packets_dropped + 1
false
} else {
let send_time = self.mgr.clock().now_seconds()
let pkt = { src, dst, payload, send_time }
let _ = self.mgr.schedule_action(link.latency, priority, fn() {
match self.nodes.get(dst) {
Some(Online) => {
link.packets_delivered = link.packets_delivered + 1
match self.mailboxes.get(dst) {
Some(box_arr) => box_arr.push(pkt)
None => ()
}
on_deliver(pkt)
}
_ => link.packets_dropped = link.packets_dropped + 1
}
})
true
}
}
}
}
///|
pub fn[T] NetworkTopology::get_mailbox(
self : NetworkTopology[T],
node_id : Int,
) -> Array[Packet[T]] {
match self.mailboxes.get(node_id) {
None => []
Some(arr) => arr
}
}
///|
pub fn[T] NetworkTopology::pop_mailbox(
self : NetworkTopology[T],
node_id : Int,
) -> Packet[T]? {
match self.mailboxes.get(node_id) {
None => None
Some(arr) =>
if arr.is_empty() {
None
} else {
let pkt = arr[0]
let _ = arr.remove(0)
Some(pkt)
}
}
}