1use crate::context::{ExecutorContext, SourceContext};
8use crate::event_scheduler::EventScheduler;
9use crate::modeling::event::EventPriority;
10use crate::modeling::hook::Hook;
11use crate::modeling::hook::instance::{HookDelegate, SharedHook};
12use crate::modeling::model::Model;
13use crate::modeling::source::Source;
14use crate::primitive::time::{Duration, SimTime};
15use crate::primitive::time::{MicroStepStatus, TickStatus};
16use crate::source_handler::SourceHandler;
17
18pub struct Engine<E, M: Model<E>> {
25 pub(crate) hook_delegate: HookDelegate<E, M>,
26 pub(crate) source_handler: SourceHandler<E, M>,
27 pub(crate) event_scheduler: EventScheduler<E>,
28}
29
30impl<E, M: Model<E>> Default for Engine<E, M> {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl<E, M: Model<E>> Engine<E, M> {
37 pub fn new() -> Engine<E, M> {
39 Engine {
40 hook_delegate: HookDelegate::new(),
41 source_handler: SourceHandler::new(),
42 event_scheduler: EventScheduler::new(),
43 }
44 }
45
46 pub fn begin_simulation(mut self, model: &M) -> ExecutorContext<E, M> {
54 self.hook_delegate.before_simulation(model);
55
56 self.source_handler.flush_pending();
58
59 {
61 let mut source_context = SourceContext {
63 current_tick_status: TickStatus::initialize(),
64 current_micro_step_status: MicroStepStatus::initialize(),
65 hook_delegate: self.hook_delegate,
66 source_handler: None,
70 event_scheduler: self.event_scheduler,
71 };
72
73 self.source_handler.initialize_sources(|source| {
74 source_context
75 .hook()
76 .before_register_source(model, source.name.as_ref());
77 let first_fired_opt = source.source.on_registered(&mut source_context, model);
78 source_context
79 .hook()
80 .after_register_source(model, source.name.as_ref());
81
82 first_fired_opt
83 });
84
85 self = Engine {
87 hook_delegate: source_context.hook_delegate,
88 source_handler: self.source_handler,
89 event_scheduler: source_context.event_scheduler,
90 };
91 }
92
93 self.event_scheduler.flush_pending();
95
96 ExecutorContext {
97 next_tick_status: TickStatus::initialize(),
98 current_tick: SimTime::zero(),
99 hook_delegate: self.hook_delegate,
100 source_handler: self.source_handler,
101 event_scheduler: self.event_scheduler,
102 }
103 }
104
105 pub fn add_hook<H>(&mut self, hook: H) -> &mut Self
107 where
108 H: Hook<E, M> + 'static,
109 {
110 self.hook_delegate.add_hook(hook);
111 self
112 }
113
114 pub fn add_shared_hook<H>(&mut self, shared_hook: SharedHook<E, M, H>) -> &mut Self
116 where
117 E: 'static,
118 M: 'static,
119 H: Hook<E, M> + 'static,
120 {
121 self.hook_delegate.add_shared_hook(shared_hook);
122 self
123 }
124
125 pub fn add_source<S>(&mut self, name: &'static str, source: S) -> &mut Self
127 where
128 S: Source<E, M> + 'static,
129 {
130 self.source_handler
131 .add_source_for_before_simulation(name, source);
132
133 self
134 }
135
136 pub fn schedule_event_at(
138 &mut self,
139 sim_time: SimTime,
140 priority: EventPriority,
141 event_payload: E,
142 ) -> &mut Self {
143 self.event_scheduler
144 .schedule(sim_time, Duration::zero(), priority, event_payload);
145
146 self
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::context::{EventContext, UserContext};
154 use crate::modeling::event::Event;
155 use crate::modeling::hook::instance::InteractiveStepHook;
156
157 #[derive(Debug, PartialEq)]
158 enum TestEvent {
159 Start,
160 }
161
162 struct TestModel;
163
164 impl TestModel {
165 fn new() -> Self {
166 TestModel
167 }
168 }
169
170 impl Model<TestEvent> for TestModel {
171 fn handle_event(
172 &mut self,
173 _context: &mut EventContext<TestEvent, Self>,
174 _event: &Event<TestEvent>,
175 ) {
176 }
178 }
179
180 #[test]
181 fn test_engine_new() {
182 let engine: Engine<TestEvent, TestModel> = Engine::new();
183
184 assert!(engine.hook_delegate.is_empty());
186 assert_eq!(engine.source_handler.ready_queue_len(), 0);
187 assert_eq!(engine.event_scheduler.ready_queue_len(), 0);
188 }
189
190 #[test]
191 fn test_engine_begin_simulation() {
192 let model = TestModel::new();
193 let engine: Engine<TestEvent, TestModel> = Engine::new();
194
195 let context = engine.begin_simulation(&model);
197
198 assert!(context.hook_delegate.is_empty());
199 assert_eq!(context.source_handler.ready_queue_len(), 0);
200 assert_eq!(context.event_scheduler.ready_queue_len(), 0);
201 }
202
203 #[test]
204 fn test_engine_add_hook() {
205 let mut engine: Engine<TestEvent, TestModel> = Engine::new();
206 engine.add_hook(InteractiveStepHook);
207
208 assert_eq!(engine.hook_delegate.len(), 1);
210 }
211
212 #[test]
213 fn test_engine_add_source() {
214 struct MySource;
215 impl Source<TestEvent, TestModel> for MySource {
216 fn on_registered(
217 &mut self,
218 context: &mut dyn UserContext<TestEvent, TestModel>,
219 _model: &TestModel,
220 ) -> Option<Duration> {
221 context.schedule_event(Duration::one(), EventPriority::minimum(), TestEvent::Start);
223 None
224 }
225
226 fn fire(
227 &mut self,
228 _context: &mut SourceContext<TestEvent, TestModel>,
229 _model: &TestModel,
230 ) -> Option<Duration> {
231 None
232 }
233 }
234
235 let model = TestModel;
236 let mut engine: Engine<TestEvent, TestModel> = Engine::new();
237 engine.add_source("my_source", MySource);
238
239 let context = engine.begin_simulation(&model);
241 assert_eq!(context.event_scheduler.ready_queue_len(), 1);
242 }
243
244 #[test]
245 fn test_engine_schedule_event_at() {
246 let mut engine: Engine<TestEvent, TestModel> = Engine::new();
247 let sim_time = SimTime::from_ticks(10);
248
249 engine.schedule_event_at(sim_time, EventPriority::minimum(), TestEvent::Start);
250
251 assert_eq!(engine.event_scheduler.ready_queue_len(), 0);
253
254 engine.event_scheduler.flush_pending();
256 assert_eq!(engine.event_scheduler.ready_queue_len(), 1);
257 }
258}