1use std::fmt::Debug;
42use std::sync::Arc;
43
44use solverforge_core::domain::PlanningSolution;
45
46pub trait SolverEventListener<S: PlanningSolution>: Send + Sync + Debug {
51 fn on_best_solution_changed(&self, solution: &S, score: &S::Score);
58
59 fn on_solving_started(&self, _solution: &S) {}
61
62 fn on_solving_ended(&self, _solution: &S, _is_terminated_early: bool) {}
64}
65
66pub trait PhaseLifecycleListener<S: PlanningSolution>: Send + Sync + Debug {
71 fn on_phase_started(&self, phase_index: usize, phase_type: &str);
78
79 fn on_phase_ended(&self, phase_index: usize, phase_type: &str);
86}
87
88pub trait StepLifecycleListener<S: PlanningSolution>: Send + Sync + Debug {
93 fn on_step_started(&self, step_index: u64);
99
100 fn on_step_ended(&self, step_index: u64, score: &S::Score);
107}
108
109pub struct SolverEventSupport<S: PlanningSolution> {
114 solver_listeners: Vec<Arc<dyn SolverEventListener<S>>>,
116
117 phase_listeners: Vec<Arc<dyn PhaseLifecycleListener<S>>>,
119
120 step_listeners: Vec<Arc<dyn StepLifecycleListener<S>>>,
122}
123
124impl<S: PlanningSolution> SolverEventSupport<S> {
125 pub fn new() -> Self {
127 Self {
128 solver_listeners: Vec::new(),
129 phase_listeners: Vec::new(),
130 step_listeners: Vec::new(),
131 }
132 }
133
134 pub fn add_solver_listener(&mut self, listener: Arc<dyn SolverEventListener<S>>) {
138 self.solver_listeners.push(listener);
139 }
140
141 pub fn add_phase_listener(&mut self, listener: Arc<dyn PhaseLifecycleListener<S>>) {
143 self.phase_listeners.push(listener);
144 }
145
146 pub fn add_step_listener(&mut self, listener: Arc<dyn StepLifecycleListener<S>>) {
148 self.step_listeners.push(listener);
149 }
150
151 pub fn clear_listeners(&mut self) {
153 self.solver_listeners.clear();
154 self.phase_listeners.clear();
155 self.step_listeners.clear();
156 }
157
158 pub fn fire_best_solution_changed(&self, solution: &S, score: &S::Score) {
162 for listener in &self.solver_listeners {
163 listener.on_best_solution_changed(solution, score);
164 }
165 }
166
167 pub fn fire_solving_started(&self, solution: &S) {
169 for listener in &self.solver_listeners {
170 listener.on_solving_started(solution);
171 }
172 }
173
174 pub fn fire_solving_ended(&self, solution: &S, is_terminated_early: bool) {
176 for listener in &self.solver_listeners {
177 listener.on_solving_ended(solution, is_terminated_early);
178 }
179 }
180
181 pub fn fire_phase_started(&self, phase_index: usize, phase_type: &str) {
183 for listener in &self.phase_listeners {
184 listener.on_phase_started(phase_index, phase_type);
185 }
186 }
187
188 pub fn fire_phase_ended(&self, phase_index: usize, phase_type: &str) {
190 for listener in &self.phase_listeners {
191 listener.on_phase_ended(phase_index, phase_type);
192 }
193 }
194
195 pub fn fire_step_started(&self, step_index: u64) {
197 for listener in &self.step_listeners {
198 listener.on_step_started(step_index);
199 }
200 }
201
202 pub fn fire_step_ended(&self, step_index: u64, score: &S::Score) {
204 for listener in &self.step_listeners {
205 listener.on_step_ended(step_index, score);
206 }
207 }
208
209 pub fn solver_listener_count(&self) -> usize {
213 self.solver_listeners.len()
214 }
215
216 pub fn phase_listener_count(&self) -> usize {
218 self.phase_listeners.len()
219 }
220
221 pub fn step_listener_count(&self) -> usize {
223 self.step_listeners.len()
224 }
225
226 pub fn has_listeners(&self) -> bool {
228 !self.solver_listeners.is_empty()
229 || !self.phase_listeners.is_empty()
230 || !self.step_listeners.is_empty()
231 }
232}
233
234impl<S: PlanningSolution> Default for SolverEventSupport<S> {
235 fn default() -> Self {
236 Self::new()
237 }
238}
239
240impl<S: PlanningSolution> Debug for SolverEventSupport<S> {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 f.debug_struct("SolverEventSupport")
243 .field("solver_listeners", &self.solver_listeners.len())
244 .field("phase_listeners", &self.phase_listeners.len())
245 .field("step_listeners", &self.step_listeners.len())
246 .finish()
247 }
248}
249
250#[derive(Debug, Clone, Default)]
254pub struct LoggingEventListener {
255 prefix: String,
257}
258
259impl LoggingEventListener {
260 pub fn new() -> Self {
262 Self::default()
263 }
264
265 pub fn with_prefix(prefix: impl Into<String>) -> Self {
267 Self {
268 prefix: prefix.into(),
269 }
270 }
271}
272
273impl<S: PlanningSolution> SolverEventListener<S> for LoggingEventListener {
274 fn on_best_solution_changed(&self, _solution: &S, score: &S::Score) {
275 println!(
276 "{}[Event] New best solution found with score: {:?}",
277 self.prefix, score
278 );
279 }
280
281 fn on_solving_started(&self, _solution: &S) {
282 println!("{}[Event] Solving started", self.prefix);
283 }
284
285 fn on_solving_ended(&self, _solution: &S, is_terminated_early: bool) {
286 if is_terminated_early {
287 println!("{}[Event] Solving ended (terminated early)", self.prefix);
288 } else {
289 println!("{}[Event] Solving ended", self.prefix);
290 }
291 }
292}
293
294impl<S: PlanningSolution> PhaseLifecycleListener<S> for LoggingEventListener {
295 fn on_phase_started(&self, phase_index: usize, phase_type: &str) {
296 println!(
297 "{}[Event] Phase {} ({}) started",
298 self.prefix, phase_index, phase_type
299 );
300 }
301
302 fn on_phase_ended(&self, phase_index: usize, phase_type: &str) {
303 println!(
304 "{}[Event] Phase {} ({}) ended",
305 self.prefix, phase_index, phase_type
306 );
307 }
308}
309
310impl<S: PlanningSolution> StepLifecycleListener<S> for LoggingEventListener {
311 fn on_step_started(&self, step_index: u64) {
312 println!("{}[Event] Step {} started", self.prefix, step_index);
313 }
314
315 fn on_step_ended(&self, step_index: u64, score: &S::Score) {
316 println!(
317 "{}[Event] Step {} ended with score: {:?}",
318 self.prefix, step_index, score
319 );
320 }
321}
322
323#[derive(Debug, Default)]
327pub struct CountingEventListener {
328 best_solution_count: std::sync::atomic::AtomicUsize,
329 solving_started_count: std::sync::atomic::AtomicUsize,
330 solving_ended_count: std::sync::atomic::AtomicUsize,
331 phase_started_count: std::sync::atomic::AtomicUsize,
332 phase_ended_count: std::sync::atomic::AtomicUsize,
333 step_started_count: std::sync::atomic::AtomicUsize,
334 step_ended_count: std::sync::atomic::AtomicUsize,
335}
336
337impl CountingEventListener {
338 pub fn new() -> Self {
340 Self::default()
341 }
342
343 pub fn best_solution_count(&self) -> usize {
345 self.best_solution_count
346 .load(std::sync::atomic::Ordering::SeqCst)
347 }
348
349 pub fn solving_started_count(&self) -> usize {
351 self.solving_started_count
352 .load(std::sync::atomic::Ordering::SeqCst)
353 }
354
355 pub fn solving_ended_count(&self) -> usize {
357 self.solving_ended_count
358 .load(std::sync::atomic::Ordering::SeqCst)
359 }
360
361 pub fn phase_started_count(&self) -> usize {
363 self.phase_started_count
364 .load(std::sync::atomic::Ordering::SeqCst)
365 }
366
367 pub fn phase_ended_count(&self) -> usize {
369 self.phase_ended_count
370 .load(std::sync::atomic::Ordering::SeqCst)
371 }
372
373 pub fn step_started_count(&self) -> usize {
375 self.step_started_count
376 .load(std::sync::atomic::Ordering::SeqCst)
377 }
378
379 pub fn step_ended_count(&self) -> usize {
381 self.step_ended_count
382 .load(std::sync::atomic::Ordering::SeqCst)
383 }
384
385 pub fn reset(&self) {
387 self.best_solution_count
388 .store(0, std::sync::atomic::Ordering::SeqCst);
389 self.solving_started_count
390 .store(0, std::sync::atomic::Ordering::SeqCst);
391 self.solving_ended_count
392 .store(0, std::sync::atomic::Ordering::SeqCst);
393 self.phase_started_count
394 .store(0, std::sync::atomic::Ordering::SeqCst);
395 self.phase_ended_count
396 .store(0, std::sync::atomic::Ordering::SeqCst);
397 self.step_started_count
398 .store(0, std::sync::atomic::Ordering::SeqCst);
399 self.step_ended_count
400 .store(0, std::sync::atomic::Ordering::SeqCst);
401 }
402}
403
404impl<S: PlanningSolution> SolverEventListener<S> for CountingEventListener {
405 fn on_best_solution_changed(&self, _solution: &S, _score: &S::Score) {
406 self.best_solution_count
407 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
408 }
409
410 fn on_solving_started(&self, _solution: &S) {
411 self.solving_started_count
412 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
413 }
414
415 fn on_solving_ended(&self, _solution: &S, _is_terminated_early: bool) {
416 self.solving_ended_count
417 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
418 }
419}
420
421impl<S: PlanningSolution> PhaseLifecycleListener<S> for CountingEventListener {
422 fn on_phase_started(&self, _phase_index: usize, _phase_type: &str) {
423 self.phase_started_count
424 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
425 }
426
427 fn on_phase_ended(&self, _phase_index: usize, _phase_type: &str) {
428 self.phase_ended_count
429 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
430 }
431}
432
433impl<S: PlanningSolution> StepLifecycleListener<S> for CountingEventListener {
434 fn on_step_started(&self, _step_index: u64) {
435 self.step_started_count
436 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
437 }
438
439 fn on_step_ended(&self, _step_index: u64, _score: &S::Score) {
440 self.step_ended_count
441 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
442 }
443}
444
445#[cfg(test)]
446#[path = "event_tests.rs"]
447mod tests;