use heapless::Vec;
use crate::{
bus::{Envelope, SimBus},
clock::{Duration, Instant},
fault::FaultConfig,
io::NodeAddress,
rng::{Rng, Xorshift64},
};
#[derive(Debug, Clone)]
pub struct CanFaultConfig {
pub message: FaultConfig,
pub arbitration_loss: (u32, u32),
pub bit_error: (u32, u32),
pub bus_off: (u32, u32),
}
impl CanFaultConfig {
pub fn none() -> Self {
Self {
message: FaultConfig::none(),
arbitration_loss: (0, 1),
bit_error: (0, 1),
bus_off: (0, 1),
}
}
pub fn light() -> Self {
Self {
message: FaultConfig::light(),
arbitration_loss: (1, 200),
bit_error: (1, 200),
bus_off: (1, 200),
}
}
pub fn chaos() -> Self {
Self {
message: FaultConfig::chaos(),
arbitration_loss: (1, 10),
bit_error: (1, 10),
bus_off: (1, 10),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CanBusState {
Active,
ErrorPassive,
BusOff { since: Instant },
}
impl CanBusState {
pub fn is_operational(&self) -> bool {
matches!(self, Self::Active | Self::ErrorPassive)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CanEvent {
BusOff,
BusRecovered,
BitError { src: NodeAddress },
}
#[derive(Debug)]
pub struct CanSimBus<const N: usize, const Q: usize> {
inner: SimBus<N, Q>,
can_faults: CanFaultConfig,
bus_state: CanBusState,
events: Vec<CanEvent, 16>,
rng: Xorshift64,
}
impl<const N: usize, const Q: usize> CanSimBus<N, Q> {
pub fn new(seed: u64, faults: CanFaultConfig) -> Self {
Self {
inner: SimBus::new(seed, faults.message.clone()),
can_faults: faults,
bus_state: CanBusState::Active,
events: heapless::Vec::new(),
rng: Xorshift64::new(seed.wrapping_add(2)),
}
}
pub fn send(&mut self, src: NodeAddress, dst: NodeAddress, data: &[u8]) -> bool {
if !self.bus_state.is_operational() {
return false;
}
if self.rng.chance(
self.can_faults.arbitration_loss.0,
self.can_faults.arbitration_loss.1,
) {
return false;
}
if self
.rng
.chance(self.can_faults.bit_error.0, self.can_faults.bit_error.1)
{
let _ = self.events.push(CanEvent::BitError { src: src.clone() });
return false;
}
self.inner.send(src, dst, data)
}
pub fn trigger_bus_off(&mut self) {
let now = self.inner.now();
self.bus_state = CanBusState::BusOff { since: now };
let _ = self.events.push(CanEvent::BusOff);
}
pub fn reset_bus_off(&mut self) {
if matches!(self.bus_state, CanBusState::BusOff { .. }) {
self.bus_state = CanBusState::Active;
let _ = self.events.push(CanEvent::BusRecovered);
}
}
pub fn bus_state(&self) -> &CanBusState {
&self.bus_state
}
pub fn tick(&mut self, duration: Duration) -> heapless::Vec<Envelope<N>, Q> {
if self.bus_state.is_operational() {
if self
.rng
.chance(self.can_faults.bus_off.0, self.can_faults.bus_off.1)
{
let now = self.inner.now();
self.bus_state = CanBusState::BusOff { since: now };
let _ = self.events.push(CanEvent::BusOff);
}
}
if !self.bus_state.is_operational() {
let _ = self.inner.tick(duration);
return heapless::Vec::new();
}
self.inner.tick(duration)
}
pub fn now(&self) -> Instant {
self.inner.now()
}
pub fn drain_events(&mut self) -> impl Iterator<Item = CanEvent> + '_ {
self.events.drain(..)
}
pub fn set_faults(&mut self, faults: CanFaultConfig) {
self.inner.set_faults(faults.message.clone());
self.can_faults = faults;
}
pub fn inner_mut(&mut self) -> &mut SimBus<N, Q> {
&mut self.inner
}
}