micro_traffic_sim_core 0.1.9

Core library for microscopic traffic simulation via cellular automata.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
use crate::behaviour::{BehaviourType};
use crate::agents_types::AgentType;
use crate::grid::cell::CellID;
use std::fmt;

/// Vehicle generation patterns for trip scheduling.
/// 
/// Determines how frequently vehicles are spawned during simulation.
///
/// # Examples
/// 
/// ```rust
/// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
/// 
/// // Predictable traffic flow
/// let steady_flow = Trip::new(1, 10, TripType::Constant).with_time(5);
/// 
/// // Variable traffic flow  
/// let variable_flow = Trip::new(1, 10, TripType::Random).with_probability(0.3);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TripType {
    /// Default uninitialized state
    Undefined,
    /// Generate vehicles at regular intervals (use `time` field)
    Constant,
    /// Generate vehicles probabilistically each time step (use `probability` field)
    Random,
}

impl fmt::Display for TripType {
    /// Formats the trip type for display.
    /// 
    /// Returns a short, lowercase string representation suitable for
    /// logging, debugging, and user interfaces.
    /// 
    /// # Examples
    /// 
    /// ```rust
    /// use micro_traffic_sim_core::trips::trip::{TripType};
    /// 
    /// assert_eq!(format!("{}", TripType::Undefined), "undefined");
    /// assert_eq!(format!("{}", TripType::Constant), "constant");
    /// assert_eq!(format!("{}", TripType::Random), "random");
    /// ```
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let trip_type_str = match self {
            TripType::Undefined => "undefined",
            TripType::Constant => "constant",
            TripType::Random => "random",
        };
        write!(f, "{}", trip_type_str)
    }
}

pub type TripID = i64; // Alias for TripID

/// Vehicle generator that spawns vehicles along specified routes.
///
/// A `Trip` defines a vehicle generation pattern including timing, routing,
/// and vehicle characteristics. It acts as a factory for creating vehicles
/// during simulation runtime.
///
/// # Generation Modes
///
/// - **Constant**: Regular interval spawning using `time` field  
/// - **Random**: Probabilistic spawning using `probability` field
/// - **Time-bounded**: Optional `start_time` and `end_time` limits
///
/// # Examples
///
/// ```rust
/// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
/// use micro_traffic_sim_core::agents_types::AgentType;
/// 
/// // Rush hour traffic: frequent car generation
/// let rush_hour = Trip::new(1, 50, TripType::Constant)
///     .with_time(5)  // Every 5 time units
///     .with_allowed_agent_type(AgentType::Car)
///     .build();
/// ```
#[derive(Debug, Clone)]
pub struct Trip {
    // Numeric identifier of the trip
    pub id: TripID,
    // Cells which must be traversed in exact given order by the agent
    pub transit_cells: Vec<CellID>,
    // Initial speed which generated vehicle would have after appearing on the road network
    pub initial_speed: i32,
    // Probability with which vehicles are generated if trip type is TripType::Random
    pub probability: f64,
    // Source vertex of microscopic graph (`birth` zone)
    pub from_node: CellID,
    // Target vertex of microscopic graph (`death` zone)
    pub to_node: CellID,
    // Allowed agent type for the generator
    pub allowed_agent_type: AgentType,
    // Allowed behaviour type for the generator
    pub allowed_behaviour_type: BehaviourType,
    // Type trip for generating different vehicles. See TripType description
    pub trip_type: TripType,
    // Frequency (seconds) with which vehicles are generated if trip type is TripType::Constant
    pub time: i32,
    // Start time (second) for the trip. Until that no vehicles will be generated. Default: 0
    pub start_time: i32,
    // End time (second) for the trip. After that no vehicles will be generated. Default:: i32::MAX
    pub end_time: i32,
    // Relaxation time in the transit cells
    pub relax_time: i32,
    // Size of tail of the vehicle. By default every vehicle in cellular automata has size 1 itself and tail 0. When it
    // is needed to make larger vehicles (e.g. limousine) it could be handy to know extra size for
    // modeling heterogeneous road traffic flow instead of homogeneous one.
    //
    // NOTE: Gradual Materialization - When a vehicle is spawned from a trip generator, its tail_cells
    // vector starts empty. The tail "materializes" gradually as the vehicle moves: each cell the head
    // passes through becomes a tail cell, until len(tail_cells) == vehicle_tail_size. This models
    // a vehicle "entering" the network head-first. During this growth period, the vehicle is effectively
    // shorter than its declared tail_size.
    //
    // Example 1: Road [A]-[B]-[C]-[D]-[E]-[F]-[G], vehicle spawns at D with tail_size=2, speed=1
    //   Step 0: [A]-[B]-[C]-[D*]-[E]-[F]-[G]  tail_cells=[]      (head only, no tail yet)
    //   Step 1: [A]-[B]-[C]-[d]-[E*]-[F]-[G]  tail_cells=[D]     (1 tail cell)
    //   Step 2: [A]-[B]-[C]-[d]-[e]-[F*]-[G]  tail_cells=[D,E]   (full tail, 2 cells)
    //   Step 3: [A]-[B]-[C]-[ ]-[d]-[e]-[G*]  tail_cells=[E,F]   (tail shifts, D released)
    //
    // Example 2: Same road, tail_size=2, speed=2 (tail materializes faster)
    //   Step 0: [A]-[B]-[C]-[D*]-[E]-[F]-[G]  tail_cells=[]      (head only)
    //   Step 1: [A]-[B]-[C]-[d]-[e]-[F*]-[G]  tail_cells=[D,E]   (full tail in 1 step!)
    //   Step 2: [A]-[B]-[ ]-[ ]-[d]-[e]-[H*]  tail_cells=[F,G]   (D,E released)
    //
    // Example 3: Same road, tail_size=2, speed=3 (tail is "cut" to max size)
    //   Step 0: [A]-[B]-[C]-[D*]-[E]-[F]-[G]  tail_cells=[]      (head only)
    //   Step 1: [A]-[B]-[C]-[ ]-[d]-[e]-[G*]  tail_cells=[E,F]   (full tail, D skipped - only last 2 kept)
    //
    //   (* = head, lowercase = tail)
    //
    // See also: Vehicle::tail_cells.
    pub vehicle_tail_size: usize,
    // Speed limit for generated vehicles. If >= 0, overrides the behaviour-derived speed limit.
    // Default: -1 (meaning "resolve from behaviour type")
    pub speed_limit: i32,
}

/// A builder pattern implementation for constructing `Trip` objects.
///
/// `TripBuilder` allows for optional configuration of `Trip` fields before building the final `Trip` object.
pub struct TripBuilder {
    trip: Trip,
}

impl Trip {
    /// Constructs a new `TripBuilder` for building a `Trip` object.
    ///
    /// # Arguments
    ///
    /// * `from_node` - The starting point of the trip.
    /// * `to_node` - The ending point of the trip.
    /// * `trip_type` - The type of the trip (e.g., static or random generator).
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::agents_types::AgentType;
    /// use micro_traffic_sim_core::behaviour::BehaviourType;
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_allowed_agent_type(AgentType::Car)
    ///     .with_allowed_behaviour_type(BehaviourType::Cooperative)
    ///     .with_time(10)
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn new(from_node: CellID, to_node: CellID, trip_type: TripType) -> TripBuilder {
        TripBuilder {
            trip: Trip {
                id: 0,
                transit_cells: Vec::new(),
                initial_speed: 0,
                probability: 0.5,
                from_node,
                to_node,
                allowed_agent_type: AgentType::Undefined,
                allowed_behaviour_type: BehaviourType::Undefined,
                trip_type,
                time: -1,
                start_time: 0,
                end_time: i32::MAX,
                relax_time: -1,
                vehicle_tail_size: 0,
                speed_limit: -1,
            },
        }
    }
}

impl TripBuilder {
    /// Sets the trip ID.
    ///
    /// # Arguments
    ///
    /// * `id` - The numeric identifier of the trip.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_id(42)
    ///     .build();
    /// assert_eq!(trip.id, 42);
    /// ```
    pub fn with_id(mut self, id: TripID) -> Self {
        self.trip.id = id;
        self
    }

    /// Sets the cells IDs list in which each cell must be traversed in exact given order by the agent and relaxation time for the trip.
    ///
    /// # Arguments
    ///
    /// * `cells` - A list of IDs of cells
    /// * `t` - The time (in time units) should be spent at each transit stop.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_transits_cells(vec![2, 3, 4], 10)
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn with_transits_cells(mut self, cells: Vec<CellID>, t: i32) -> Self {
        self.trip.transit_cells = cells;
        self.trip.relax_time = t;
        self
    }

    /// Sets the initial speed of the trip.
    ///
    /// # Arguments
    ///
    /// * `speed` - The initial speed for the trip.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_initial_speed(60)
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    ///
    pub fn with_initial_speed(mut self, speed: i32) -> Self {
        self.trip.initial_speed = speed;
        self
    }

    /// Sets the allowed agent type for the trip.
    ///
    /// # Arguments
    ///
    /// * `agent_type` - The agent type (e.g., car, bus) allowed for this trip.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::agents_types::AgentType;
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_allowed_agent_type(AgentType::Car)
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    ///
    pub fn with_allowed_agent_type(mut self, agent_type: AgentType) -> Self {
        self.trip.allowed_agent_type = agent_type;
        self
    }

    /// Sets the allowed behavior type for the trip.
    ///
    /// # Arguments
    ///
    /// * `behaviour_type` - The behavior type (e.g., aggressive, cautious).
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::behaviour::BehaviourType;
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_allowed_behaviour_type(BehaviourType::Aggressive)
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    ///
    pub fn with_allowed_behaviour_type(mut self, behaviour_type: BehaviourType) -> Self {
        self.trip.allowed_behaviour_type = behaviour_type;
        self
    }

    /// Sets the frequency for generating vehicle
    ///
    /// # Arguments
    ///
    /// * `time` - Frequency (seconds) with which vehicles are generated if trip type is TripType::Constant
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant) // Trip type must be TripType::Constant
    ///     .with_time(10) // Vehicle will be generated every 10 seconds
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn with_time(mut self, time: i32) -> Self {
        self.trip.time = time;
        self
    }

    /// Sets the probability for generating vehicle
    ///
    /// # Arguments
    ///
    /// * `probability` - Probability with which vehicles are generated if trip type is TripType::Random
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Random) // Trip type must be TripType::Random
    ///     .with_probability(0.7) // Vehicle will be generated with 70% probablity
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn with_probability(mut self, probability: f64) -> Self {
        self.trip.probability = probability;
        self
    }

    /// Sets the start time (second) for the trip. Until that time no vehicles will be generated.
    ///
    /// # Arguments
    ///
    /// * `start_time` - start second. Until that time no vehicles will be generated.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_start_time(5) // Until then vehicles won't be generated
    ///     .with_time(2) // Vehicles will be generated on 7th, 9th and so so seconds.
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn with_start_time(mut self, start_time: i32) -> Self {
        self.trip.start_time = start_time;
        self
    }

    /// Sets the end time (second) for the trip. After that time no vehicles will be generated.
    ///
    /// # Arguments
    ///
    /// * `end_time` - end second. After that time no vehicles will be generated.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Random) // Trip type must be TripType::Random
    ///     .with_start_time(5) // Until then vehicles won't be generated
    ///     .with_time(2) // Vehicles will be generated on 7th, 9th and 11th seconds.
    ///     .with_end_time(12) // Vehicles will stop to be generated after this second ticks
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn with_end_time(mut self, end_time: i32) -> Self {
        self.trip.end_time = end_time;
        self
    }

    /// Sets the tail size of the vehicle.
    /// DEPRECATED: Tail size will be auto-resolved from the agent type.
    /// Setting with_vehicle_tail_size(0) for multi-cell agents (Bus, Truck, LargeBus) will be overridden by the default.
    /// There's no way to explicitly force a Bus to have zero tail size - the auto-resolution always runs when vehicle_tail_size == 0
    ///
    /// # Arguments
    ///
    /// * `size` - The tail size of the vehicle (default: 0).
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_vehicle_tail_size(3)
    ///     .build();
    /// println!("{:?}", trip);
    /// ```
    pub fn with_vehicle_tail_size(mut self, len: usize) -> Self {
        self.trip.vehicle_tail_size = len;
        self
    }

    /// Sets the speed limit for generated vehicles.
    ///
    /// If set to a value >= 0, this speed limit will be used instead of the
    /// behaviour-derived speed limit. The default value of -1 means the speed
    /// limit will be resolved from the behaviour type.
    ///
    /// # Arguments
    ///
    /// * `limit` - The speed limit value. Use -1 to resolve from behaviour type.
    ///
    /// # Example
    ///
    /// ```
    /// use micro_traffic_sim_core::trips::trip::{Trip, TripType};
    /// use micro_traffic_sim_core::behaviour::BehaviourType;
    /// let trip = Trip::new(1, 10, TripType::Constant)
    ///     .with_allowed_behaviour_type(BehaviourType::Aggressive)
    ///     // Override behaviour speed limit with 5
    ///     .with_speed_limit(5)
    ///     .build();
    /// assert_eq!(trip.speed_limit, 5);
    /// ```
    pub fn with_speed_limit(mut self, limit: i32) -> Self {
        self.trip.speed_limit = limit;
        self
    }

    /// Builds the final `Trip` object with the configured properties.
    ///
    /// If `vehicle_tail_size` was not explicitly set, it will be automatically
    /// resolved from the `allowed_agent_type` using [`AgentType::tail_size_default()`].
    ///
    /// # Returns
    /// The fully constructed `Trip` object.
    pub fn build(mut self) -> Trip {
        // Auto-resolve tail size from agent type if not explicitly set
        if self.trip.vehicle_tail_size == 0 {
            self.trip.vehicle_tail_size = self.trip.allowed_agent_type.tail_size_default();
        }
        self.trip
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tail_size_auto_resolution() {
        // Car should have tail_size = 0
        let car_trip = Trip::new(1, 10, TripType::Constant)
            .with_allowed_agent_type(AgentType::Car)
            .build();
        assert_eq!(car_trip.vehicle_tail_size, 0);

        // Bus should have tail_size = 1
        let bus_trip = Trip::new(1, 10, TripType::Constant)
            .with_allowed_agent_type(AgentType::Bus)
            .build();
        assert_eq!(bus_trip.vehicle_tail_size, 1);

        // Truck should have tail_size = 1
        let truck_trip = Trip::new(1, 10, TripType::Constant)
            .with_allowed_agent_type(AgentType::Truck)
            .build();
        assert_eq!(truck_trip.vehicle_tail_size, 1);

        // LargeBus should have tail_size = 2
        let large_bus_trip = Trip::new(1, 10, TripType::Constant)
            .with_allowed_agent_type(AgentType::LargeBus)
            .build();
        assert_eq!(large_bus_trip.vehicle_tail_size, 2);
    }

    #[test]
    fn test_tail_size_explicit_override() {
        // Explicit tail size should not be overridden
        let trip = Trip::new(1, 10, TripType::Constant)
            .with_allowed_agent_type(AgentType::Car)
            .with_vehicle_tail_size(5)
            .build();
        assert_eq!(trip.vehicle_tail_size, 5);
    }

    #[test]
    fn test_speed_limit_default() {
        // Default speed_limit should be -1
        let trip = Trip::new(1, 10, TripType::Constant).build();
        assert_eq!(trip.speed_limit, -1);
    }

    #[test]
    fn test_speed_limit_explicit() {
        // Explicit speed_limit should be set
        let trip = Trip::new(1, 10, TripType::Constant)
            .with_speed_limit(5)
            .build();
        assert_eq!(trip.speed_limit, 5);
    }

    #[test]
    fn test_speed_limit_with_behaviour_first() {
        // Speed limit set after behaviour should still apply
        let trip = Trip::new(1, 10, TripType::Constant)
            .with_allowed_behaviour_type(BehaviourType::Aggressive)
            .with_speed_limit(3)
            .build();
        assert_eq!(trip.speed_limit, 3);
    }

    #[test]
    fn test_speed_limit_before_behaviour() {
        // Speed limit set before behaviour should still apply
        let trip = Trip::new(1, 10, TripType::Constant)
            .with_speed_limit(3)
            .with_allowed_behaviour_type(BehaviourType::Aggressive)
            .build();
        assert_eq!(trip.speed_limit, 3);
    }
}