use crate::clock::{Clock, Duration, Instant, SimClock};
use crate::fault::FaultConfig;
use crate::io::NodeAddress;
use crate::rng::{Rng, Xorshift64};
#[derive(Debug, Clone)]
pub struct Envelope<const N: usize> {
pub src: NodeAddress,
pub dst: NodeAddress,
pub data: heapless::Vec<u8, N>,
pub deliver_at: Instant,
}
pub struct SimBus<const N: usize, const Q: usize> {
clock: SimClock,
rng: Xorshift64,
faults: FaultConfig,
queue: heapless::Vec<Envelope<N>, Q>,
}
impl<const N: usize, const Q: usize> SimBus<N, Q> {
pub fn new(seed: u64, faults: FaultConfig) -> Self {
Self {
clock: SimClock::new(),
rng: Xorshift64::new(seed),
faults,
queue: heapless::Vec::new(),
}
}
pub fn now(&self) -> Instant {
self.clock.now()
}
pub fn tick(&mut self, duration: Duration) -> heapless::Vec<Envelope<N>, Q> {
self.clock.advance(duration);
let now = self.clock.now();
let mut delivered = heapless::Vec::new();
let mut remaining = heapless::Vec::new();
for envelope in self.queue.drain(..) {
if envelope.deliver_at <= now {
let _ = delivered.push(envelope);
} else {
let _ = remaining.push(envelope);
}
}
if delivered.len() > 1 {
for i in 0..delivered.len() - 1 {
if self
.rng
.chance(self.faults.message_reorder.0, self.faults.message_reorder.1)
{
delivered.swap(i, i + 1);
}
}
}
self.queue = remaining;
delivered
}
pub fn send(&mut self, src: NodeAddress, dst: NodeAddress, data: &[u8]) -> bool {
if self
.rng
.chance(self.faults.message_loss.0, self.faults.message_loss.1)
{
return false;
}
if self
.rng
.chance(self.faults.timeout.0, self.faults.timeout.1)
{
return false;
}
let mut payload = heapless::Vec::new();
for &byte in data {
if self
.rng
.chance(self.faults.corruption.0, self.faults.corruption.1)
{
let _ = payload.push(byte ^ self.rng.next_u8());
} else {
let _ = payload.push(byte);
}
}
let deliver_at = if self
.rng
.chance(self.faults.message_delay.0, self.faults.message_delay.1)
{
let delay_us = self.rng.next_u64() % self.faults.max_delay.as_micros().max(1);
self.clock.now() + Duration::from_micros(delay_us)
} else {
self.clock.now()
};
let envelope = Envelope {
src,
dst,
data: payload,
deliver_at,
};
self.queue.push(envelope).is_ok()
}
pub fn faults(&self) -> &FaultConfig {
&self.faults
}
pub fn set_faults(&mut self, faults: FaultConfig) {
self.faults = faults;
}
pub fn next_u8(&mut self) -> u8 {
self.rng.next_u8()
}
pub fn chance(&mut self, numerator: u32, denominator: u32) -> bool {
self.rng.chance(numerator, denominator)
}
}