Skip to main content

des_sim/context/
source.rs

1//! The `source` module provides the `SourceContext`, which is used by sources to interact with the simulation environment
2//! when they are fired.
3//!
4//! It allows sources to schedule new events and access the current simulation time.
5
6use crate::context::UserContext;
7use crate::event_scheduler::EventScheduler;
8use crate::modeling::event::EventPriority;
9use crate::modeling::hook::Hook;
10use crate::modeling::hook::instance::HookDelegate;
11use crate::modeling::model::Model;
12use crate::primitive::time::{Duration, MicroStep, SimTime};
13use crate::primitive::time::{MicroStepStatus, TickStatus};
14use crate::source_handler::SourceHandler;
15
16/// Context information provided during the source execution phase.
17///
18/// This context provides access to the current simulation time and micro-step status
19/// during model execution, as well as functionality for scheduling events.
20pub struct SourceContext<E, M: Model<E>> {
21    pub(crate) current_tick_status: TickStatus,
22    pub(crate) current_micro_step_status: MicroStepStatus,
23    pub(crate) hook_delegate: HookDelegate<E, M>,
24    // ### Internal Design Note
25    // The `source_handler` is stored as an `Option`. This is to allow temporary extraction of the
26    // `SourceHandler` when re-registering (re-queuing) sources after they are fired. This design
27    // pattern avoids lifetime issues during the holding period and ensures safe transfer of
28    // ownership to the `MicroStepHandler`.
29    pub(crate) source_handler: Option<SourceHandler<E, M>>,
30    pub(crate) event_scheduler: EventScheduler<E>,
31}
32
33impl<E, M: Model<E>> UserContext<E, M> for SourceContext<E, M> {
34    fn current_tick(&self) -> SimTime {
35        self.current_tick_status.current()
36    }
37
38    fn current_micro_step(&self) -> MicroStep {
39        self.current_micro_step_status.current()
40    }
41
42    fn schedule_event(&mut self, delay: Duration, priority: EventPriority, event_payload: E) {
43        self.event_scheduler
44            .schedule(self.current_tick(), delay, priority, event_payload);
45    }
46}
47
48impl<E, M: Model<E>> SourceContext<E, M> {
49    /// Returns the hook associated with the current execution phase.
50    pub(crate) fn hook(&self) -> &impl Hook<E, M> {
51        &self.hook_delegate
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::context::EventContext;
59    use crate::event_scheduler::EventScheduler;
60    use crate::modeling::event::{Event, EventPriority};
61    use crate::modeling::hook::instance::HookDelegate;
62    use crate::modeling::model::Model;
63    use crate::primitive::time::{Duration, MicroStep, SimTime};
64    use crate::primitive::time::{MicroStepStatus, TickStatus};
65
66    #[derive(Debug, PartialEq)]
67    enum TestEvent {
68        EventA,
69    }
70
71    struct TestModel;
72
73    impl Model<TestEvent> for TestModel {
74        fn handle_event(
75            &mut self,
76            _context: &mut EventContext<TestEvent, Self>,
77            _event: &Event<TestEvent>,
78        ) {
79            // No-op for testing
80        }
81    }
82
83    /// Helper to create a default SourceContext for testing.
84    fn create_test_source_context() -> SourceContext<TestEvent, TestModel> {
85        SourceContext {
86            current_tick_status: TickStatus::new(SimTime::from_ticks(0), Duration::zero()),
87            current_micro_step_status: MicroStepStatus::new(MicroStep::zero()),
88            hook_delegate: HookDelegate::new(),
89            source_handler: None,
90            event_scheduler: EventScheduler::new(),
91        }
92    }
93
94    #[test]
95    fn test_current_tick() {
96        let context = create_test_source_context();
97        assert_eq!(context.current_tick(), SimTime::from_ticks(0));
98    }
99
100    #[test]
101    fn test_current_micro_step() {
102        let context = create_test_source_context();
103        assert_eq!(context.current_micro_step(), MicroStep::zero());
104    }
105
106    #[test]
107    fn test_schedule_event() {
108        let mut context = create_test_source_context();
109
110        // Register a few events in advance
111        context.event_scheduler.schedule(
112            SimTime::from_ticks(0),
113            Duration::one(),
114            EventPriority::minimum(),
115            TestEvent::EventA,
116        );
117        context.event_scheduler.schedule(
118            SimTime::from_ticks(0),
119            Duration::one(),
120            EventPriority::minimum(),
121            TestEvent::EventA,
122        );
123        context.event_scheduler.schedule(
124            SimTime::from_ticks(0),
125            Duration::one(),
126            EventPriority::minimum(),
127            TestEvent::EventA,
128        );
129
130        // Move events into the ready queue
131        context.event_scheduler.flush_pending();
132        let initial_event_count = context.event_scheduler.ready_queue_len();
133
134        // Schedule a new event and verify the count
135        context.schedule_event(Duration::one(), EventPriority::minimum(), TestEvent::EventA);
136        context.event_scheduler.flush_pending();
137
138        assert_eq!(
139            context.event_scheduler.ready_queue_len(),
140            initial_event_count + 1
141        );
142    }
143}