1use crate::context::{ActiveExecutorContext, EventContext, SourceContext};
8use crate::execution::phase::{EventPhase, MicroStepResult, SourcePhase, UncheckedActiveExecutor};
9use crate::modeling::hook::Hook;
10use crate::modeling::model::Model;
11use crate::primitive::time::MicroStepStatus;
12
13pub struct MicroStepHandler<CTX> {
19 context: CTX,
20}
21
22impl<CTX> MicroStepHandler<CTX> {
23 pub(crate) fn new(context: CTX) -> MicroStepHandler<CTX> {
25 MicroStepHandler { context }
26 }
27
28 pub fn ref_context(&self) -> &CTX {
30 &self.context
31 }
32
33 pub fn ref_mut_context(&mut self) -> &mut CTX {
35 &mut self.context
36 }
37}
38
39impl<E, M: Model<E>> MicroStepHandler<ActiveExecutorContext<E, M>> {
40 pub fn start_source_phase(self, model: &M) -> SourcePhase<E, M> {
44 let mut context = self.context;
45 context.hook().before_source_phase(
46 model,
47 context.current_tick_status.current(),
48 context.next_micro_step_status.current(),
49 );
50
51 let ready_sources = context
52 .source_handler
53 .drain_ready(context.current_tick_status.current());
54
55 SourcePhase::new(
56 SourceContext {
57 current_tick_status: context.current_tick_status,
58 current_micro_step_status: context.next_micro_step_status,
59 hook_delegate: context.hook_delegate,
60 source_handler: None,
61 event_scheduler: context.event_scheduler,
62 },
63 context.source_handler,
64 ready_sources,
65 )
66 }
67}
68
69impl<E, M: Model<E>> MicroStepHandler<SourceContext<E, M>> {
70 pub fn to_event_phase(self, model: &M) -> EventPhase<E, M> {
75 let mut context = self.context;
76 context.hook().before_event_phase(
77 model,
78 context.current_tick_status.current(),
79 context.current_micro_step_status.current(),
80 );
81
82 let ready_events = context
83 .event_scheduler
84 .drain_ready(context.current_tick_status.current());
85
86 EventPhase::new(
87 EventContext {
88 current_tick_status: context.current_tick_status,
89 current_micro_step_status: context.current_micro_step_status,
90 hook_delegate: context.hook_delegate,
91 source_handler: context
92 .source_handler
93 .expect("Failed to retrieve SourceHandler from SourcePhase."),
94 event_scheduler: context.event_scheduler,
95 },
96 ready_events,
97 )
98 }
99}
100
101impl<E, M: Model<E>> MicroStepHandler<EventContext<E, M>> {
102 pub fn end_micro_step(mut self, model: &M) -> MicroStepResult<E, M> {
107 self.ref_mut_context().source_handler.flush_pending();
109 self.ref_mut_context().event_scheduler.flush_pending();
110 let current_tick = self.ref_context().current_tick_status.current();
111
112 let next_event_at = self.ref_context().event_scheduler.peek_next_time();
114 let next_source_at = self.ref_context().source_handler.peek_next_time();
115
116 let has_next_in_current_tick = matches!(next_event_at, Some(t) if t == current_tick)
117 || matches!(next_source_at, Some(t) if t == current_tick);
118
119 if has_next_in_current_tick {
120 let current_micro_step = self.ref_context().current_micro_step_status.current();
122 let next_micro_step = current_micro_step.next();
123
124 self.context.hook().after_micro_step(
125 model,
126 current_tick,
127 self.ref_context().current_micro_step_status.current(),
128 );
129
130 let active_context = ActiveExecutorContext {
131 current_tick_status: self.ref_context().current_tick_status,
132 next_micro_step_status: MicroStepStatus::new(next_micro_step),
133 hook_delegate: self.context.hook_delegate,
134 source_handler: self.context.source_handler,
135 event_scheduler: self.context.event_scheduler,
136 };
137
138 MicroStepResult::Continue(UncheckedActiveExecutor::new(
139 active_context,
140 current_micro_step,
141 ))
142 } else {
143 let last_micro_step_status = self.ref_context().current_micro_step_status;
145
146 self.context.hook().after_micro_step(
147 model,
148 current_tick,
149 self.ref_context().current_micro_step_status.current(),
150 );
151
152 let active_context = ActiveExecutorContext {
153 current_tick_status: self.ref_context().current_tick_status,
154 next_micro_step_status: last_micro_step_status,
155 hook_delegate: self.context.hook_delegate,
156 source_handler: self.context.source_handler,
157 event_scheduler: self.context.event_scheduler,
158 };
159
160 MicroStepResult::Complete(active_context, last_micro_step_status)
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::context::{ActiveExecutorContext, EventContext, SourceContext};
169 use crate::event_scheduler::EventScheduler;
170 use crate::modeling::event::{Event, EventPriority};
171 use crate::modeling::hook::instance::{HookDelegate, SharedHook};
172 use crate::modeling::model::Model;
173 use crate::primitive::time::{Duration, MicroStep, MicroStepStatus, SimTime, TickStatus};
174 use crate::source_handler::{SourceHandler, SourceReadyEntry, SourceView};
175 use std::rc::Rc;
176 use std::sync::Mutex;
177
178 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
179 enum TestEvent {
180 A,
181 }
182
183 struct TestModel;
184
185 impl Model<TestEvent> for TestModel {
186 fn handle_event(
187 &mut self,
188 _context: &mut EventContext<TestEvent, Self>,
189 _event: &Event<TestEvent>,
190 ) {
191 }
193 }
194
195 struct TransitionTrackerHook {
197 called_before_source_phase: Rc<Mutex<bool>>,
198 called_before_event_phase: Rc<Mutex<bool>>,
199 called_after_micro_step: Rc<Mutex<bool>>,
200 }
201
202 impl Hook<TestEvent, TestModel> for TransitionTrackerHook {
203 fn before_simulation(&self, _model: &TestModel) {
204 }
206 fn after_simulation(&self, _model: &TestModel, _end_tick: SimTime) {
207 }
209 fn before_tick(
210 &self,
211 _model: &TestModel,
212 _current_tick: SimTime,
213 _skipped_duration: Duration,
214 ) {
215 }
217 fn after_tick(
218 &self,
219 _model: &TestModel,
220 _current_tick: SimTime,
221 _last_micro_step: MicroStep,
222 ) {
223 }
225 fn before_micro_step(
226 &self,
227 _model: &TestModel,
228 _current_tick: SimTime,
229 _current_micro_step: MicroStep,
230 ) {
231 }
233 fn after_micro_step(
234 &self,
235 _model: &TestModel,
236 _current_tick: SimTime,
237 _current_micro_step: MicroStep,
238 ) {
239 *self.called_after_micro_step.lock().unwrap() = true;
240 }
241 fn on_discard_remain_micro_step(
242 &self,
243 _model: &TestModel,
244 _current_tick: SimTime,
245 _first_discarded_micro_step: MicroStep,
246 _discarded_sources: &[SourceReadyEntry],
247 _discarded_events: &[Event<TestEvent>],
248 ) {
249 }
251 fn before_register_source(&self, _model: &TestModel, _name: &str) {
252 }
254 fn after_register_source(&self, _model: &TestModel, _name: &str) {
255 }
257 fn before_source_phase(
258 &self,
259 _model: &TestModel,
260 _current_tick: SimTime,
261 _current_micro_step: MicroStep,
262 ) {
263 *self.called_before_source_phase.lock().unwrap() = true;
264 }
265 fn before_source(
266 &self,
267 _model: &TestModel,
268 _current_tick: SimTime,
269 _current_micro_step: MicroStep,
270 _source_view: &SourceView,
271 ) {
272 }
274 fn after_source(
275 &self,
276 _model: &TestModel,
277 _current_tick: SimTime,
278 _current_micro_step: MicroStep,
279 _source_view: &SourceView,
280 _computed_next_fire: Option<SimTime>,
281 ) {
282 }
284 fn cancel_source(
285 &self,
286 _model: &TestModel,
287 _current_tick: SimTime,
288 _current_micro_step: MicroStep,
289 _scheduled_at: SimTime,
290 _source_view: &SourceView,
291 ) {
292 }
294 fn discard_source(
295 &self,
296 _model: &TestModel,
297 _current_tick: SimTime,
298 _current_micro_step: MicroStep,
299 _source_view: &SourceView,
300 ) {
301 }
303 fn after_source_phase(
304 &self,
305 _model: &TestModel,
306 _current_tick: SimTime,
307 _current_micro_step: MicroStep,
308 ) {
309 }
311 fn before_event_phase(
312 &self,
313 _model: &TestModel,
314 _current_tick: SimTime,
315 _current_micro_step: MicroStep,
316 ) {
317 *self.called_before_event_phase.lock().unwrap() = true;
318 }
319 fn before_event(
320 &self,
321 _model: &TestModel,
322 _current_tick: SimTime,
323 _current_micro_step: MicroStep,
324 _event: &Event<TestEvent>,
325 ) {
326 }
328 fn after_event(
329 &self,
330 _model: &TestModel,
331 _current_tick: SimTime,
332 _current_micro_step: MicroStep,
333 _event: &Event<TestEvent>,
334 ) {
335 }
337 fn cancel_event(
338 &self,
339 _model: &TestModel,
340 _current_tick: SimTime,
341 _current_micro_step: MicroStep,
342 _scheduled_at: SimTime,
343 _event: &Event<TestEvent>,
344 ) {
345 }
347 fn discard_event(
348 &self,
349 _model: &TestModel,
350 _current_tick: SimTime,
351 _current_micro_step: MicroStep,
352 _event: &Event<TestEvent>,
353 ) {
354 }
356 fn after_event_phase(
357 &self,
358 _model: &TestModel,
359 _current_tick: SimTime,
360 _current_micro_step: MicroStep,
361 ) {
362 }
364 }
365
366 fn setup_tracker_hook() -> SharedHook<TestEvent, TestModel, TransitionTrackerHook> {
367 SharedHook::new(TransitionTrackerHook {
368 called_before_source_phase: Rc::new(Mutex::new(false)),
369 called_before_event_phase: Rc::new(Mutex::new(false)),
370 called_after_micro_step: Rc::new(Mutex::new(false)),
371 })
372 }
373
374 #[test]
375 fn test_ref_context_and_mut() {
376 let mut handler = MicroStepHandler::new(42);
377 assert_eq!(*handler.ref_context(), 42);
378
379 *handler.ref_mut_context() = 100;
380 assert_eq!(*handler.ref_context(), 100);
381 }
382
383 #[test]
384 fn test_start_source_phase() {
385 let model = TestModel;
386 let hook = setup_tracker_hook();
387 let mut hook_delegate = HookDelegate::new();
388 hook_delegate.add_shared_hook(hook.clone());
389
390 let active_context = ActiveExecutorContext {
391 current_tick_status: TickStatus::initialize(),
392 next_micro_step_status: MicroStepStatus::initialize(),
393 hook_delegate,
394 source_handler: SourceHandler::new(),
395 event_scheduler: EventScheduler::new(),
396 };
397
398 let handler = MicroStepHandler::new(active_context);
399 let mut source_phase = handler.start_source_phase(&model);
400
401 assert!(source_phase.source_handler.is_some());
402 assert!(source_phase.get_context().source_handler.is_none());
403 assert!(*hook.get_ref().called_before_source_phase.lock().unwrap());
404 }
405
406 #[test]
407 fn test_to_event_phase() {
408 let model = TestModel;
409 let hook = setup_tracker_hook();
410 let mut hook_delegate = HookDelegate::new();
411 hook_delegate.add_shared_hook(hook.clone());
412
413 let source_context = SourceContext {
414 current_tick_status: TickStatus::initialize(),
415 current_micro_step_status: MicroStepStatus::initialize(),
416 hook_delegate,
417 source_handler: Some(SourceHandler::new()),
418 event_scheduler: EventScheduler::new(),
419 };
420
421 let handler = MicroStepHandler::new(source_context);
422 let _event_phase = handler.to_event_phase(&model);
423
424 assert!(*hook.get_ref().called_before_event_phase.lock().unwrap());
425 }
426
427 #[test]
428 fn test_end_micro_step_continue() {
429 let model = TestModel;
430 let hook = setup_tracker_hook();
431 let mut hook_delegate = HookDelegate::new();
432 hook_delegate.add_shared_hook(hook.clone());
433
434 let tick_status = TickStatus::initialize();
435 let current_time = tick_status.current();
436
437 let mut event_scheduler = EventScheduler::new();
438 event_scheduler.schedule(
439 current_time,
440 Duration::zero(),
441 EventPriority::minimum(),
442 TestEvent::A,
443 );
444 event_scheduler.flush_pending();
445
446 assert_eq!(event_scheduler.peek_next_time(), Some(current_time));
447
448 let event_context = EventContext {
449 current_tick_status: tick_status,
450 current_micro_step_status: MicroStepStatus::initialize(),
451 hook_delegate,
452 source_handler: SourceHandler::new(),
453 event_scheduler,
454 };
455
456 let handler = MicroStepHandler::new(event_context);
457 let result = handler.end_micro_step(&model);
458
459 match result {
460 MicroStepResult::Continue(_) => {
461 }
463 MicroStepResult::Complete(_, _) => {
464 panic!("Expected MicroStepResult::Continue, but got Complete");
465 }
466 }
467 assert!(*hook.get_ref().called_after_micro_step.lock().unwrap());
468 }
469
470 #[test]
471 fn test_end_micro_step_complete() {
472 let model = TestModel;
473 let hook = setup_tracker_hook();
474 let mut hook_delegate = HookDelegate::new();
475 hook_delegate.add_shared_hook(hook.clone());
476
477 let event_context = EventContext {
478 current_tick_status: TickStatus::initialize(),
479 current_micro_step_status: MicroStepStatus::initialize(),
480 hook_delegate,
481 source_handler: SourceHandler::new(),
482 event_scheduler: EventScheduler::new(),
483 };
484
485 let handler = MicroStepHandler::new(event_context);
486 let result = handler.end_micro_step(&model);
487
488 match result {
489 MicroStepResult::Complete(_, _) => {
490 }
492 MicroStepResult::Continue(_) => {
493 panic!("Expected MicroStepResult::Complete, but got Continue");
494 }
495 }
496 assert!(*hook.get_ref().called_after_micro_step.lock().unwrap());
497 }
498}