des_sim/modeling/sampler/instance/
mode.rs1use crate::modeling::sampler::{DurationSampler, PendingDuration};
9use crate::primitive::time::SimTime;
10use rand::Rng;
11
12pub trait TimeTrigger {
14 fn get_active_index(&self, now: SimTime) -> usize;
16
17 fn max_possible_index_hint(&self) -> usize;
19}
20
21pub struct ModeSampler<T: TimeTrigger> {
23 trigger: T,
24 samplers: Vec<Box<dyn DurationSampler>>,
25}
26
27impl<T: TimeTrigger> DurationSampler for ModeSampler<T> {
28 fn sample(&mut self, rng: &mut dyn Rng, current_tick: SimTime) -> PendingDuration {
29 let index = self.trigger.get_active_index(current_tick);
30
31 if let Some(sampler) = self.samplers.get_mut(index) {
33 sampler.sample(rng, current_tick)
34 } else {
35 log::warn!(
36 "ModeSampler index {} out of bounds, falling back to 0",
37 index
38 );
39 self.samplers[0].sample(rng, current_tick)
40 }
41 }
42}
43
44impl<T: TimeTrigger> ModeSampler<T> {
45 pub fn new(trigger: T, samplers: impl IntoIterator<Item = Box<dyn DurationSampler>>) -> Self {
50 let samplers: Vec<_> = samplers.into_iter().collect();
51
52 assert!(
53 !samplers.is_empty(),
54 "ModeSampler requires at least one sampler"
55 );
56 debug_assert!(
57 trigger.max_possible_index_hint() < samplers.len(),
58 "ModeSampler trigger hint exceeds sampler collection bounds"
59 );
60
61 Self { trigger, samplers }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::modeling::sampler::CombinatorExt;
69 use crate::modeling::sampler::instance::ConstantSampler;
70 use rand::SeedableRng;
71 use rand::rngs::SmallRng;
72
73 struct MockTimeTrigger {
75 active_index: usize,
76 max_index_hint: usize,
77 }
78
79 impl TimeTrigger for MockTimeTrigger {
80 fn get_active_index(&self, _now: SimTime) -> usize {
81 self.active_index
82 }
83
84 fn max_possible_index_hint(&self) -> usize {
85 self.max_index_hint
86 }
87 }
88
89 #[test]
90 fn test_mode_sampler_valid_index() {
91 let mut rng = SmallRng::seed_from_u64(2);
92 let s1 = ConstantSampler::new(10.0);
93 let s2 = ConstantSampler::new(20.0);
94 let s3 = ConstantSampler::new(30.0);
95
96 let trigger = MockTimeTrigger {
97 active_index: 1,
98 max_index_hint: 2,
99 };
100 let mut sampler = ModeSampler::new(trigger, vec![s1.boxed(), s2.boxed(), s3.boxed()]);
101
102 let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
103 assert_eq!(sample.raw_value(), 20.0); }
105
106 #[test]
107 fn test_mode_sampler_out_of_bounds_fallback() {
108 let mut rng = SmallRng::seed_from_u64(2);
109 let s1 = ConstantSampler::new(10.0);
110 let s2 = ConstantSampler::new(20.0);
111
112 let trigger = MockTimeTrigger {
113 active_index: 5, max_index_hint: 1,
115 };
116 let mut sampler = ModeSampler::new(trigger, vec![s1.boxed(), s2.boxed()]);
117
118 let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
119 assert_eq!(sample.raw_value(), 10.0); }
121
122 #[test]
123 #[should_panic(expected = "ModeSampler requires at least one sampler")]
124 fn test_mode_sampler_empty_samplers() {
125 let trigger = MockTimeTrigger {
126 active_index: 0,
127 max_index_hint: 0,
128 };
129 let _sampler = ModeSampler::new(trigger, vec![]);
130 }
131}