Skip to main content

odem_rs_core/
config.rs

1//! This module is about preparing the simulation library for the execution of
2//! a simulation model, defining traits and a default configuration for
3//! specifying various data types and constants.
4//!
5//! Our library abstracts from concrete types and initial values for model time,
6//! priority, and shared data, which requires the user to specify these
7//! properties before any simulation model may be executed. To make this as
8//! painless as possible, we employ the builder-pattern to construct a
9//! configuration that is automatically passed down to the various constructors
10//! for the model elements.
11//!
12//! ## `Config` Trait
13//!
14//! The [`Config`] trait is used to configure the simulation model and includes
15//! the following associated types:
16//!
17//! - `Time`: Type for the model time. It is required to be copyable, partially
18//!   ordered, and have a debug representation. It cannot be self-referential.
19//! - `Rank`: Type used to prioritize pucks scheduled at the same model time,
20//!   required to be copyable, totally ordered, and have a debug representation.
21//!   It can also not be self-referential.
22//! - `Data`: User-defined globally shared data type, intended for statistical
23//!   aggregators, shared random number generators, or any data accessible from
24//!   anywhere in the simulation model.
25//! - `Plan`: Type of the continuation calendar, implementing the `Scheduler`
26//!   trait for this configuration.
27//!
28//! Additionally, the `Config` trait includes methods to retrieve default values
29//! for simulation start time, default rank for agents, and a reference to
30//! globally shared data.
31//!
32//! ## `Time` Trait
33//!
34//! The [`Time`] trait encapsulates traits needed for the model-time type in a
35//! configuration, including being `Unpin`, `PartialOrd`, `Copy`, and `Debug`.
36//! It provides a default implementation for displaying time in a human-readable
37//! format.
38//!
39//! ## `Rank` Trait
40//!
41//! The [`Rank`] trait encapsulates traits needed for the rank-type in a
42//! configuration, including being `Unpin`, `Ord`, `Copy`, and `Debug`.
43//! A blanket implementation is provided for all types meeting those criteria.
44//!
45//! ## Default Configuration
46//!
47//! The empty tuple `()` implements the simulation configuration used by
48//! default. It uses `f64` for the model time with an initial value of `0.0`, an
49//! empty tuple for rank, and no additional data.
50
51use crate::calendar::{DefaultPlan, Scheduler};
52use core::{any::Any, fmt, marker::PhantomData};
53
54#[doc(inline)]
55pub use odem_rs_meta::Config;
56
57/* ************************* Configuration Traits *************************** */
58
59/// Trait used to configure the various data types and constants used in a
60/// simulation model.
61pub trait Config: 'static {
62	/// The type used for the model time.
63	type Time: Time;
64	/// The type used to prioritize pucks that are scheduled at the same
65	/// model time.
66	type Rank: Rank;
67	/// User-defined, globally shared data type.
68	///
69	/// It is intended to be used for injecting statistical aggregators and
70	/// shared random number generators but can be used whenever you would
71	/// like to access some data from anywhere in the simulation model.
72	/// Only one copy of this data exists during a simulation-run.
73	type Data: Any;
74	/// The type of the continuation calendar which has to implement the
75	/// `Scheduler` trait for this configuration.
76	///
77	/// # Note
78	///
79	/// The `Scheduler`-trait is not yet part of the public API due to
80	/// instability. The only valid choice at this point is [`DefaultPlan`].
81	type Plan: Scheduler<Config = Self>;
82
83	/// Returns the start or default time of the simulation.
84	fn default_time(&self) -> Self::Time;
85
86	/// Returns the default rank for agents in the simulation.
87	fn default_rank(&self) -> Self::Rank;
88
89	/// Returns a reference to the globally shared data during a simulation run.
90	fn global_data(&self) -> &Self::Data;
91}
92
93/// Helper trait that encapsulates all the traits needed for the model-time
94/// type of [configuration](Config).
95pub trait Time: Unpin + PartialOrd + Copy + fmt::Debug + 'static {
96	/// Formats the time in human-readable format.
97	///
98	/// Uses the debug implementation by default but can be overridden with
99	/// a more suitable representation.
100	fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101		fmt::Debug::fmt(&self, f)
102	}
103
104	/// Displays the time in human-readable format using the [`format`]-method.
105	///
106	/// [`format`]: Self::format
107	fn display(self) -> DisplayTime<Self> {
108		DisplayTime(self)
109	}
110}
111
112/// Helper trait that encapsulates all the traits needed for the rank-type
113/// of a [configuration](Config).
114pub trait Rank: Unpin + Ord + Copy + fmt::Debug + 'static {}
115
116// blanket-implementation for all the right types
117impl<R> Rank for R where R: Unpin + Ord + Copy + fmt::Debug + 'static {}
118
119/* **************************************************** Default Configuration */
120
121impl Config for () {
122	type Time = f64;
123	type Rank = ();
124	type Data = ();
125	type Plan = DefaultPlan<()>;
126
127	fn default_time(&self) -> Self::Time {
128		0.0
129	}
130
131	fn default_rank(&self) -> Self::Rank {}
132
133	fn global_data(&self) -> &Self::Data {
134		self
135	}
136}
137
138/* ****************************************************** Built-In Time Types */
139
140/// Implements [`Display`] by referring to [`Time::format`].
141///
142/// [`Display`]: fmt::Display
143pub struct DisplayTime<T>(pub T);
144
145impl<T: Time> fmt::Debug for DisplayTime<T> {
146	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147		self.0.format(f)
148	}
149}
150
151impl<T: Time> fmt::Display for DisplayTime<T> {
152	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153		self.0.format(f)
154	}
155}
156
157macro_rules! impl_signed_integral_time {
158	($($T:ty),* $(,)?) => {$(
159		impl Time for $T {
160			fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161				use fmt::Display;
162				if f.alternate() {
163					Display::fmt(&ClockTime::seconds(*self as isize), f)
164				} else {
165					Display::fmt(self, f)
166				}
167			}
168		}
169	)*};
170}
171
172impl_signed_integral_time!(i8, i16, i32, i64, i128, isize);
173
174macro_rules! impl_unsigned_integral_time {
175	($($T:ty),* $(,)?) => {$(
176		impl Time for $T {
177			fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178				use fmt::Display;
179				if f.alternate() {
180					Display::fmt(&Seconds(*self as usize), f)
181				} else {
182					Display::fmt(self, f)
183				}
184			}
185		}
186	)*};
187}
188
189impl_unsigned_integral_time!(u8, u16, u32, u64, u128, usize);
190
191macro_rules! impl_floating_time {
192	($($T:ty),* $(,)?) => {$(
193		impl Time for $T {
194			fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195				use fmt::Display;
196				if f.alternate() {
197					match f.precision() {
198						None | Some(0) => Display::fmt(&ClockTime::seconds(*self as isize), f),
199						Some(1) => Display::fmt(&ClockTime::milliseconds((*self * 1e+3) as isize), f),
200						Some(2) => Display::fmt(&ClockTime::microseconds((*self * 1e+6) as isize), f),
201						_ => Display::fmt(&ClockTime::nanoseconds((*self * 1e+9) as isize), f),
202					}
203				} else {
204					Display::fmt(self, f)
205				}
206			}
207		}
208	)*};
209}
210
211impl_floating_time!(f32, f64);
212
213impl Time for () {}
214
215/* **************************************************** Normalized Model Time */
216
217/// Helper for displaying normalized model-time in digital-clock-format.
218#[derive(Copy, Clone)]
219pub struct ClockTime<U> {
220	/// The value-part of the time.
221	value: isize,
222	/// Indicator of the unit of time.
223	_unit: PhantomData<U>,
224}
225
226impl ClockTime<()> {
227	/// Constructs a [displayable](fmt::Display) clock time in nanoseconds.
228	pub const fn nanoseconds(value: isize) -> impl fmt::Display {
229		ClockTime::<Nanoseconds> {
230			value,
231			_unit: PhantomData,
232		}
233	}
234
235	/// Constructs a [displayable](fmt::Display) clock time in microseconds.
236	pub const fn microseconds(value: isize) -> impl fmt::Display {
237		ClockTime::<Microseconds> {
238			value,
239			_unit: PhantomData,
240		}
241	}
242
243	/// Constructs a [displayable](fmt::Display) clock time in milliseconds.
244	pub const fn milliseconds(value: isize) -> impl fmt::Display {
245		ClockTime::<Milliseconds> {
246			value,
247			_unit: PhantomData,
248		}
249	}
250
251	/// Constructs a [displayable](fmt::Display) clock time in seconds.
252	pub const fn seconds(value: isize) -> impl fmt::Display {
253		ClockTime::<Seconds> {
254			value,
255			_unit: PhantomData,
256		}
257	}
258
259	/// Constructs a [displayable](fmt::Display) clock time in minutes.
260	pub const fn minutes(value: isize) -> impl fmt::Display {
261		ClockTime::<Minutes> {
262			value,
263			_unit: PhantomData,
264		}
265	}
266
267	/// Constructs a [displayable](fmt::Display) clock time in hours.
268	pub const fn hours(value: isize) -> impl fmt::Display {
269		ClockTime::<Hours> {
270			value,
271			_unit: PhantomData,
272		}
273	}
274
275	/// Constructs a [displayable](fmt::Display) clock time in days.
276	pub const fn days(value: isize) -> impl fmt::Display {
277		ClockTime::<Days> {
278			value,
279			_unit: PhantomData,
280		}
281	}
282
283	/// Constructs a [displayable](fmt::Display) clock time in years.
284	pub const fn years(value: isize) -> impl fmt::Display {
285		ClockTime::<Years> {
286			value,
287			_unit: PhantomData,
288		}
289	}
290}
291
292impl<U> ClockTime<U> {
293	/// Private method that prints the time's sign.
294	fn write_sign(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295		if self.value < 0 {
296			write!(f, "-")
297		} else if f.sign_plus() {
298			write!(f, "+")
299		} else {
300			Ok(())
301		}
302	}
303}
304
305impl fmt::Display for ClockTime<Years> {
306	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307		self.write_sign(f)?;
308		Years(self.value.unsigned_abs()).fmt(f)
309	}
310}
311
312impl fmt::Display for ClockTime<Days> {
313	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314		self.write_sign(f)?;
315		Days(self.value.unsigned_abs()).fmt(f)
316	}
317}
318
319impl fmt::Display for ClockTime<Hours> {
320	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321		self.write_sign(f)?;
322		Hours(self.value.unsigned_abs()).fmt(f)
323	}
324}
325
326impl fmt::Display for ClockTime<Minutes> {
327	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328		self.write_sign(f)?;
329		Minutes(self.value.unsigned_abs()).fmt(f)
330	}
331}
332
333impl fmt::Display for ClockTime<Seconds> {
334	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335		self.write_sign(f)?;
336		Seconds(self.value.unsigned_abs()).fmt(f)
337	}
338}
339
340impl fmt::Display for ClockTime<Milliseconds> {
341	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342		self.write_sign(f)?;
343		Milliseconds(self.value.unsigned_abs()).fmt(f)
344	}
345}
346
347impl fmt::Display for ClockTime<Microseconds> {
348	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349		self.write_sign(f)?;
350		Microseconds(self.value.unsigned_abs()).fmt(f)
351	}
352}
353
354impl fmt::Display for ClockTime<Nanoseconds> {
355	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356		self.write_sign(f)?;
357		Nanoseconds(self.value.unsigned_abs()).fmt(f)
358	}
359}
360
361/// Marks the inner quantity as 'in nanoseconds', i.e., 10^(-9) seconds.
362struct Nanoseconds(usize);
363/// Marks the inner quantity as 'in microseconds', i.e., 10^(-6) seconds.
364struct Microseconds(usize);
365/// Marks the inner quantity as 'in milliseconds', i.e., 10^(-3) seconds.
366struct Milliseconds(usize);
367/// Marks the inner quantity as 'in seconds'.
368struct Seconds(usize);
369/// Marks the inner quantity as 'in minutes'.
370struct Minutes(usize);
371/// Marks the inner quantity as 'in hours'.
372struct Hours(usize);
373/// Marks the inner quantity as 'in days'.
374struct Days(usize);
375/// Marks the inner quantity as 'in years'.
376struct Years(usize);
377
378impl fmt::Display for Years {
379	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380		if self.0 != 0 || f.sign_aware_zero_pad() {
381			write!(f, "{}a", self.0)
382		} else {
383			f.write_str("  ")
384		}
385	}
386}
387
388impl fmt::Display for Days {
389	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390		let mut quantity = self.0;
391		let mut p;
392
393		if {
394			p = quantity >= 365;
395			p
396		} || f.width().map(|w| w > 5).unwrap_or(false)
397		{
398			Years(quantity / 365).fmt(f)?;
399			f.write_str(" ")?;
400		}
401
402		if quantity >= 7 || f.width().map(|w| w > 4).unwrap_or(false) {
403			quantity %= 365;
404
405			p |= quantity >= 7;
406			if p || f.sign_aware_zero_pad() {
407				write!(f, "{:2}w ", quantity / 7)?;
408			} else {
409				f.write_str("    ")?;
410			}
411
412			quantity %= 7;
413		}
414
415		if p || quantity != 0 || f.sign_aware_zero_pad() {
416			write!(f, "{quantity}d")
417		} else {
418			f.write_str("  ")
419		}
420	}
421}
422
423impl fmt::Display for Hours {
424	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425		let mut quantity = self.0;
426		let p;
427
428		if {
429			p = quantity >= 24;
430			p
431		} || f.width().map(|w| w > 3).unwrap_or(false)
432		{
433			Days(quantity / 24).fmt(f)?;
434			quantity %= 24;
435			f.write_str(" ")?;
436		}
437
438		if p || quantity != 0 || f.sign_aware_zero_pad() {
439			write!(f, "{quantity:02}h")
440		} else {
441			f.write_str("   ")
442		}
443	}
444}
445
446impl fmt::Display for Minutes {
447	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448		let mut quantity = self.0;
449		let p;
450
451		if {
452			p = quantity >= 60;
453			p
454		} || f.width().map(|w| w > 2).unwrap_or(false)
455		{
456			Hours(quantity / 60).fmt(f)?;
457			quantity %= 60;
458			f.write_str(" ")?;
459		}
460
461		if p || quantity != 0 || f.sign_aware_zero_pad() {
462			write!(f, "{quantity:02}m")
463		} else {
464			f.write_str("   ")
465		}
466	}
467}
468
469impl fmt::Display for Seconds {
470	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471		let mut quantity = self.0;
472		let p;
473
474		if {
475			p = quantity >= 60;
476			p
477		} || f.width().map(|w| w > 1).unwrap_or(false)
478		{
479			Minutes(quantity / 60).fmt(f)?;
480			quantity %= 60;
481			f.write_str(" ")?;
482		}
483
484		if p || quantity != 0 || f.sign_aware_zero_pad() {
485			write!(f, "{quantity:02}s")
486		} else {
487			f.write_str(" 0s")
488		}
489	}
490}
491
492impl fmt::Display for Milliseconds {
493	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494		let mut quantity = self.0;
495
496		Seconds(quantity / 1000).fmt(f)?;
497		quantity %= 1000;
498		write!(f, " {quantity:03}ms")
499	}
500}
501
502impl fmt::Display for Microseconds {
503	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504		let mut quantity = self.0;
505
506		Milliseconds(quantity / 1000).fmt(f)?;
507		quantity %= 1000;
508		write!(f, " {quantity:03}µs")
509	}
510}
511
512impl fmt::Display for Nanoseconds {
513	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514		let mut quantity = self.0;
515
516		Microseconds(quantity / 1000).fmt(f)?;
517		quantity %= 1000;
518		write!(f, " {quantity:03}ns")
519	}
520}
521
522/* ****************************************************** Optional Time Types */
523
524// support si time quantities as model time
525#[cfg(feature = "uom")]
526#[cfg_attr(docsrs, doc(cfg(feature = "uom")))]
527mod uom {
528	use super::{ClockTime, Time};
529	use core::fmt;
530	use uom::{
531		Conversion,
532		fmt::DisplayStyle,
533		num_traits::{AsPrimitive, Num},
534		si::{Units, time},
535	};
536
537	impl<U, V> Time for time::Time<U, V>
538	where
539		U: Units<V> + ?Sized + 'static,
540		V: Conversion<V>
541			+ Num
542			+ PartialOrd
543			+ PartialEq
544			+ AsPrimitive<isize>
545			+ fmt::Debug
546			+ fmt::Display
547			+ Unpin
548			+ 'static,
549		time::second: Conversion<V, T = V::T>,
550		time::millisecond: Conversion<V, T = V::T>,
551		time::microsecond: Conversion<V, T = V::T>,
552		time::nanosecond: Conversion<V, T = V::T>,
553	{
554		fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555			use fmt::Display;
556
557			if f.alternate() {
558				// switch to a wall-clock-format for the alternative format
559				match f.precision() {
560					None | Some(0) => ClockTime::seconds(self.get::<time::second>().as_()).fmt(f),
561					Some(1) => {
562						ClockTime::milliseconds(self.get::<time::millisecond>().as_()).fmt(f)
563					}
564					Some(2) => {
565						ClockTime::microseconds(self.get::<time::microsecond>().as_()).fmt(f)
566					}
567					_ => ClockTime::nanoseconds(self.get::<time::nanosecond>().as_()).fmt(f),
568				}
569			} else {
570				// use the abbreviated format by default
571				self.into_format_args(time::second, DisplayStyle::Abbreviation)
572					.fmt(f)
573			}
574		}
575	}
576}