1use serde::{Serialize, de::DeserializeOwned};
4
5use crate::config::{
6 BuildingConfig, ElevatorConfig, GroupConfig, LineConfig, PassengerSpawnConfig, SimConfig,
7 SimulationParams,
8};
9use crate::dispatch::scan::ScanDispatch;
10use crate::dispatch::{BuiltinReposition, DispatchStrategy, RepositionStrategy};
11use crate::error::SimError;
12use crate::hooks::{Phase, PhaseHooks};
13use crate::ids::GroupId;
14use crate::sim::Simulation;
15use crate::stop::{StopConfig, StopId};
16use crate::world::World;
17use std::collections::BTreeMap;
18
19type ExtRegistration = Box<dyn FnOnce(&mut World) + Send>;
21
22pub struct SimulationBuilder {
36 config: SimConfig,
38 dispatchers: BTreeMap<GroupId, Box<dyn DispatchStrategy>>,
40 repositioners: Vec<(GroupId, Box<dyn RepositionStrategy>, BuiltinReposition)>,
42 hooks: PhaseHooks,
44 ext_registrations: Vec<ExtRegistration>,
46}
47
48impl Default for SimulationBuilder {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl SimulationBuilder {
55 #[must_use]
68 pub fn new() -> Self {
69 let config = SimConfig {
70 building: BuildingConfig {
71 name: "Default".into(),
72 stops: vec![
73 StopConfig {
74 id: StopId(0),
75 name: "Ground".into(),
76 position: 0.0,
77 },
78 StopConfig {
79 id: StopId(1),
80 name: "Top".into(),
81 position: 10.0,
82 },
83 ],
84 lines: None,
85 groups: None,
86 },
87 elevators: vec![ElevatorConfig {
88 id: 0,
89 name: "Elevator 1".into(),
90 max_speed: 2.0,
91 acceleration: 1.5,
92 deceleration: 2.0,
93 weight_capacity: 800.0,
94 starting_stop: StopId(0),
95 door_open_ticks: 10,
96 door_transition_ticks: 5,
97 restricted_stops: Vec::new(),
98 #[cfg(feature = "energy")]
99 energy_profile: None,
100 service_mode: None,
101 inspection_speed_factor: 0.25,
102 }],
103 simulation: SimulationParams {
104 ticks_per_second: 60.0,
105 },
106 passenger_spawning: PassengerSpawnConfig {
107 mean_interval_ticks: 120,
108 weight_range: (50.0, 100.0),
109 },
110 };
111
112 let mut dispatchers = BTreeMap::new();
113 dispatchers.insert(
114 GroupId(0),
115 Box::new(ScanDispatch::new()) as Box<dyn DispatchStrategy>,
116 );
117
118 Self {
119 config,
120 dispatchers,
121 repositioners: Vec::new(),
122 hooks: PhaseHooks::default(),
123 ext_registrations: Vec::new(),
124 }
125 }
126
127 #[must_use]
132 pub fn from_config(config: SimConfig) -> Self {
133 let mut dispatchers = BTreeMap::new();
134 dispatchers.insert(
135 GroupId(0),
136 Box::new(ScanDispatch::new()) as Box<dyn DispatchStrategy>,
137 );
138
139 Self {
140 config,
141 dispatchers,
142 repositioners: Vec::new(),
143 hooks: PhaseHooks::default(),
144 ext_registrations: Vec::new(),
145 }
146 }
147
148 #[must_use]
152 pub fn stops(mut self, stops: Vec<StopConfig>) -> Self {
153 self.config.building.stops = stops;
154 self
155 }
156
157 #[must_use]
159 pub fn stop(mut self, id: StopId, name: impl Into<String>, position: f64) -> Self {
160 self.config.building.stops.push(StopConfig {
161 id,
162 name: name.into(),
163 position,
164 });
165 self
166 }
167
168 #[must_use]
172 pub fn elevators(mut self, elevators: Vec<ElevatorConfig>) -> Self {
173 self.config.elevators = elevators;
174 self
175 }
176
177 #[must_use]
179 pub fn elevator(mut self, config: ElevatorConfig) -> Self {
180 self.config.elevators.push(config);
181 self
182 }
183
184 #[must_use]
188 pub fn line(mut self, config: LineConfig) -> Self {
189 self.config
190 .building
191 .lines
192 .get_or_insert_with(Vec::new)
193 .push(config);
194 self
195 }
196
197 #[must_use]
201 pub fn lines(mut self, lines: Vec<LineConfig>) -> Self {
202 self.config.building.lines = Some(lines);
203 self
204 }
205
206 #[must_use]
208 pub fn group(mut self, config: GroupConfig) -> Self {
209 self.config
210 .building
211 .groups
212 .get_or_insert_with(Vec::new)
213 .push(config);
214 self
215 }
216
217 #[must_use]
219 pub fn groups(mut self, groups: Vec<GroupConfig>) -> Self {
220 self.config.building.groups = Some(groups);
221 self
222 }
223
224 #[must_use]
226 pub const fn ticks_per_second(mut self, tps: f64) -> Self {
227 self.config.simulation.ticks_per_second = tps;
228 self
229 }
230
231 #[must_use]
233 pub fn building_name(mut self, name: impl Into<String>) -> Self {
234 self.config.building.name = name.into();
235 self
236 }
237
238 #[must_use]
240 pub fn dispatch(mut self, strategy: impl DispatchStrategy + 'static) -> Self {
241 self.dispatchers.insert(GroupId(0), Box::new(strategy));
242 self
243 }
244
245 #[must_use]
247 pub fn dispatch_for_group(
248 mut self,
249 group: GroupId,
250 strategy: impl DispatchStrategy + 'static,
251 ) -> Self {
252 self.dispatchers.insert(group, Box::new(strategy));
253 self
254 }
255
256 #[must_use]
258 pub fn before(
259 mut self,
260 phase: Phase,
261 hook: impl Fn(&mut World) + Send + Sync + 'static,
262 ) -> Self {
263 self.hooks.add_before(phase, Box::new(hook));
264 self
265 }
266
267 #[must_use]
269 pub fn after(
270 mut self,
271 phase: Phase,
272 hook: impl Fn(&mut World) + Send + Sync + 'static,
273 ) -> Self {
274 self.hooks.add_after(phase, Box::new(hook));
275 self
276 }
277
278 #[must_use]
280 pub fn before_group(
281 mut self,
282 phase: Phase,
283 group: GroupId,
284 hook: impl Fn(&mut World) + Send + Sync + 'static,
285 ) -> Self {
286 self.hooks.add_before_group(phase, group, Box::new(hook));
287 self
288 }
289
290 #[must_use]
292 pub fn after_group(
293 mut self,
294 phase: Phase,
295 group: GroupId,
296 hook: impl Fn(&mut World) + Send + Sync + 'static,
297 ) -> Self {
298 self.hooks.add_after_group(phase, group, Box::new(hook));
299 self
300 }
301
302 #[must_use]
307 pub fn reposition(
308 self,
309 strategy: impl RepositionStrategy + 'static,
310 id: BuiltinReposition,
311 ) -> Self {
312 self.reposition_for_group(GroupId(0), strategy, id)
313 }
314
315 #[must_use]
317 pub fn reposition_for_group(
318 mut self,
319 group: GroupId,
320 strategy: impl RepositionStrategy + 'static,
321 id: BuiltinReposition,
322 ) -> Self {
323 self.repositioners.push((group, Box::new(strategy), id));
324 self
325 }
326
327 #[must_use]
332 pub fn with_ext<T: 'static + Send + Sync + Serialize + DeserializeOwned>(
333 mut self,
334 name: &str,
335 ) -> Self {
336 let name = name.to_owned();
337 self.ext_registrations
338 .push(Box::new(move |world: &mut World| {
339 world.register_ext::<T>(&name);
340 }));
341 self
342 }
343
344 pub fn validate(&self) -> Result<(), SimError> {
354 Simulation::validate_config(&self.config)
355 }
356
357 pub fn build(self) -> Result<Simulation, SimError> {
388 let mut sim = Simulation::new_with_hooks(&self.config, self.dispatchers, self.hooks)?;
389
390 for (group, strategy, id) in self.repositioners {
391 sim.set_reposition(group, strategy, id);
392 }
393
394 for register in self.ext_registrations {
395 register(sim.world_mut());
396 }
397
398 Ok(sim)
399 }
400}