use core::{
cell::RefCell,
marker::PhantomData,
sync::atomic::{AtomicI8, Ordering},
};
use rand::Rng;
#[allow(unused_imports)]
use crate::float_polyfill::FloatPolyfill;
use crate::{
bmc::{acceptable_master::AcceptableMasterList, bmca::Bmca},
clock::Clock,
config::{InstanceConfig, PortConfig},
datastructures::{
common::PortIdentity,
datasets::{
InternalCurrentDS, InternalDefaultDS, InternalParentDS, PathTraceDS, TimePropertiesDS,
},
},
filters::{Filter, FilterEstimate},
observability::{current::CurrentDS, default::DefaultDS, parent::ParentDS},
port::{InBmca, Port},
time::Duration,
};
pub struct PtpInstance<F, S = RefCell<PtpInstanceState>> {
state: S,
log_bmca_interval: AtomicI8,
_filter: PhantomData<F>,
}
#[derive(Debug)]
pub struct PtpInstanceState {
pub(crate) default_ds: InternalDefaultDS,
pub(crate) current_ds: InternalCurrentDS,
pub(crate) parent_ds: InternalParentDS,
pub(crate) path_trace_ds: PathTraceDS,
pub(crate) time_properties_ds: TimePropertiesDS,
}
impl PtpInstanceState {
fn bmca<A: AcceptableMasterList, C: Clock, F: Filter, R: Rng, S: PtpInstanceStateMutex>(
&mut self,
ports: &mut [&mut Port<'_, InBmca, A, R, C, F, S>],
bmca_interval: Duration,
) {
debug_assert_eq!(self.default_ds.number_ports as usize, ports.len());
for port in ports.iter_mut() {
port.calculate_best_local_announce_message()
}
let ebest = Bmca::<()>::find_best_announce_message(
ports
.iter()
.filter_map(|port| port.best_local_announce_message_for_bmca()),
);
for port in ports.iter_mut() {
let recommended_state = Bmca::<()>::calculate_recommended_state(
&self.default_ds,
ebest,
port.best_local_announce_message_for_state(), port.state(),
);
log::debug!(
"Recommended state port {}: {recommended_state:?}",
port.number(),
);
if let Some(recommended_state) = recommended_state {
port.set_recommended_state(
recommended_state,
&mut self.path_trace_ds,
&mut self.time_properties_ds,
&mut self.current_ds,
&mut self.parent_ds,
&self.default_ds,
);
}
}
for port in ports.iter_mut() {
port.step_announce_age(bmca_interval);
}
}
}
impl<F, S: PtpInstanceStateMutex> PtpInstance<F, S> {
pub fn new(config: InstanceConfig, time_properties_ds: TimePropertiesDS) -> Self {
let default_ds = InternalDefaultDS::new(config);
Self {
state: S::new(PtpInstanceState {
default_ds,
current_ds: Default::default(),
parent_ds: InternalParentDS::new(default_ds),
path_trace_ds: PathTraceDS::new(config.path_trace),
time_properties_ds,
}),
log_bmca_interval: AtomicI8::new(i8::MAX),
_filter: PhantomData,
}
}
pub fn default_ds(&self) -> DefaultDS {
self.state.with_ref(|s| (&s.default_ds).into())
}
pub fn current_ds(&self, port_contribution: Option<FilterEstimate>) -> CurrentDS {
self.state
.with_ref(|s| CurrentDS::from_state(&s.current_ds, port_contribution))
}
pub fn parent_ds(&self) -> ParentDS {
self.state.with_ref(|s| (&s.parent_ds).into())
}
pub fn time_properties_ds(&self) -> TimePropertiesDS {
self.state.with_ref(|s| s.time_properties_ds)
}
pub fn path_trace_ds(&self) -> PathTraceDS {
self.state.with_ref(|s| s.path_trace_ds.clone())
}
}
impl<F: Filter, S: PtpInstanceStateMutex> PtpInstance<F, S> {
pub fn add_port<A, C, R: Rng>(
&self,
config: PortConfig<A>,
filter_config: F::Config,
clock: C,
rng: R,
) -> Port<'_, InBmca, A, R, C, F, S> {
self.log_bmca_interval
.fetch_min(config.announce_interval.as_log_2(), Ordering::Relaxed);
let port_identity = self.state.with_mut(|state| {
state.default_ds.number_ports += 1;
PortIdentity {
clock_identity: state.default_ds.clock_identity,
port_number: state.default_ds.number_ports,
}
});
Port::new(
&self.state,
config,
filter_config,
clock,
port_identity,
rng,
)
}
pub fn bmca<A: AcceptableMasterList, C: Clock, R: Rng>(
&self,
ports: &mut [&mut Port<'_, InBmca, A, R, C, F, S>],
) {
self.state.with_mut(|state| {
state.bmca(
ports,
Duration::from_seconds(
2f64.powi(self.log_bmca_interval.load(Ordering::Relaxed) as i32),
),
);
});
}
pub fn bmca_interval(&self) -> core::time::Duration {
core::time::Duration::from_secs_f64(
2f64.powi(self.log_bmca_interval.load(Ordering::Relaxed) as i32),
)
}
}
pub trait PtpInstanceStateMutex {
fn new(state: PtpInstanceState) -> Self;
fn with_ref<R, F: FnOnce(&PtpInstanceState) -> R>(&self, f: F) -> R;
fn with_mut<R, F: FnOnce(&mut PtpInstanceState) -> R>(&self, f: F) -> R;
}
impl PtpInstanceStateMutex for RefCell<PtpInstanceState> {
fn new(state: PtpInstanceState) -> Self {
RefCell::new(state)
}
fn with_ref<R, F: FnOnce(&PtpInstanceState) -> R>(&self, f: F) -> R {
f(&RefCell::borrow(self))
}
fn with_mut<R, F: FnOnce(&mut PtpInstanceState) -> R>(&self, f: F) -> R {
f(&mut RefCell::borrow_mut(self))
}
}
#[cfg(feature = "std")]
impl PtpInstanceStateMutex for std::sync::RwLock<PtpInstanceState> {
fn new(state: PtpInstanceState) -> Self {
std::sync::RwLock::new(state)
}
fn with_ref<R, F: FnOnce(&PtpInstanceState) -> R>(&self, f: F) -> R {
f(&self.read().unwrap())
}
fn with_mut<R, F: FnOnce(&mut PtpInstanceState) -> R>(&self, f: F) -> R {
f(&mut self.write().unwrap())
}
}