trellis_runner/watchers/
mod.rs1#![allow(clippy::type_complexity)]
2
3use crate::engine::EngineSignal;
4use crate::state::{StateView, UserState};
5
6use std::sync::{Arc, Mutex};
7
8#[cfg(feature = "writing")]
9mod csv_file;
10
11mod failure;
12mod metrics;
13
14#[cfg(feature = "plotting")]
15mod plot;
16
17mod sampler;
18mod tracing;
19
20#[cfg(feature = "writing")]
21pub use csv_file::CsvProgressWriter;
22
23#[cfg(feature = "plotting")]
24pub use plot::PlotObserver;
25
26pub use tracing::Tracer;
27
28pub trait Observe<S: UserState>: Send + Sync {
38 fn observe(&self, name: &'static str, state: StateView<'_, S>, event: &EngineSignal<S::Float>);
39}
40
41impl<S, T> Observe<S> for Arc<T>
45where
46 S: UserState,
47 T: Observe<S> + ?Sized,
48{
49 fn observe(&self, name: &'static str, state: StateView<'_, S>, event: &EngineSignal<S::Float>) {
50 (**self).observe(name, state, event)
51 }
52}
53
54#[derive(Copy, Clone, Debug, Eq, PartialEq)]
62pub enum Frequency {
63 Always,
64 Every(usize),
65 OnExit,
66 Never,
67}
68
69impl Frequency {
70 fn should_run<F>(&self, event: &EngineSignal<F>, iteration: usize) -> bool {
71 match self {
72 Self::Never => false,
73 Self::Always => true,
74 Self::OnExit => matches!(event, EngineSignal::Termination(_)),
75 Self::Every(n) => *n != 0 && iteration.is_multiple_of(*n),
76 }
77 }
78}
79
80pub struct Observers<S> {
85 inner: Vec<(Arc<Mutex<dyn Observe<S>>>, Frequency)>,
86}
87
88impl<S: UserState> Observers<S> {
89 pub fn new() -> Self {
91 Self { inner: Vec::new() }
92 }
93
94 pub fn attach(&mut self, observer: Arc<Mutex<dyn Observe<S>>>, frequency: Frequency) {
96 self.inner.push((observer, frequency));
97 }
98
99 pub fn dispatch(
103 &self,
104 ident: &'static str,
105 state: StateView<'_, S>,
106 event: &EngineSignal<S::Float>,
107 ) {
108 let iter = state.iteration();
109
110 for (obs, freq) in &self.inner {
111 if !freq.should_run(event, iter) {
112 continue;
113 }
114
115 let obs = obs.lock().unwrap();
116 obs.observe(ident, state, event);
117 }
118 }
119}