1#[cfg(test)]
2#[path = "../../../tests/unit/format/problem/model_test.rs"]
3mod model_test;
4
5extern crate serde_json;
6
7use crate::format::{FormatError, Location, MultiFormatError};
8use serde::{Deserialize, Serialize};
9use std::io::{BufReader, BufWriter, Error, Read, Write};
10use vrp_core::prelude::Float;
11#[derive(Clone, Deserialize, Debug, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub enum RelationType {
17 Any,
19 Sequence,
21 Strict,
23}
24
25#[derive(Clone, Deserialize, Debug, Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Relation {
29 #[serde(rename(deserialize = "type", serialize = "type"))]
31 pub type_field: RelationType,
32 pub jobs: Vec<String>,
34 pub vehicle_id: String,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub shift_index: Option<usize>,
39}
40
41#[derive(Clone, Deserialize, Debug, Serialize)]
43#[serde(rename_all = "camelCase")]
44pub struct JobSkills {
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub all_of: Option<Vec<String>>,
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub one_of: Option<Vec<String>>,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub none_of: Option<Vec<String>>,
54}
55
56#[derive(Clone, Deserialize, Debug, Serialize)]
58pub struct JobPlace {
59 pub location: Location,
61 pub duration: Float,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub times: Option<Vec<Vec<String>>>,
66 #[serde(skip_serializing_if = "Option::is_none")]
69 pub tag: Option<String>,
70}
71
72#[derive(Clone, Deserialize, Debug, Serialize)]
74pub struct JobTask {
75 pub places: Vec<JobPlace>,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub demand: Option<Vec<i32>>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub order: Option<i32>,
83}
84
85#[derive(Clone, Deserialize, Debug, Serialize)]
90pub struct Job {
91 pub id: String,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub pickups: Option<Vec<JobTask>>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub deliveries: Option<Vec<JobTask>>,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub replacements: Option<Vec<JobTask>>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub services: Option<Vec<JobTask>>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub skills: Option<JobSkills>,
113
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub value: Option<Float>,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub group: Option<String>,
121
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub compatibility: Option<String>,
125}
126
127#[derive(Clone, Deserialize, Debug, Serialize)]
131#[serde(tag = "type")]
132pub enum Clustering {
133 #[serde(rename(deserialize = "vicinity", serialize = "vicinity"))]
135 Vicinity {
136 profile: VehicleProfile,
139 threshold: VicinityThresholdPolicy,
141 visiting: VicinityVisitPolicy,
143 serving: VicinityServingPolicy,
145 filtering: Option<VicinityFilteringPolicy>,
147 },
148}
149
150#[derive(Clone, Deserialize, Debug, Serialize)]
152#[serde(rename_all = "camelCase")]
153pub struct VicinityThresholdPolicy {
154 pub duration: Float,
156 pub distance: Float,
158 pub min_shared_time: Option<Float>,
160 pub smallest_time_window: Option<Float>,
162 pub max_jobs_per_cluster: Option<usize>,
164}
165
166#[derive(Clone, Deserialize, Debug, Serialize)]
168#[serde(rename_all = "camelCase")]
169pub enum VicinityVisitPolicy {
170 Return,
172 Continue,
175}
176
177#[derive(Clone, Deserialize, Debug, Serialize)]
179#[serde(tag = "type")]
180pub enum VicinityServingPolicy {
181 #[serde(rename(deserialize = "original", serialize = "original"))]
183 Original {
184 parking: Float,
186 },
187 #[serde(rename(deserialize = "multiplier", serialize = "multiplier"))]
189 Multiplier {
190 value: Float,
192 parking: Float,
194 },
195 #[serde(rename(deserialize = "fixed", serialize = "fixed"))]
197 Fixed {
198 value: Float,
200 parking: Float,
202 },
203}
204
205#[derive(Clone, Deserialize, Debug, Serialize)]
207#[serde(rename_all = "camelCase")]
208pub struct VicinityFilteringPolicy {
209 pub exclude_job_ids: Vec<String>,
211}
212
213#[derive(Clone, Deserialize, Debug, Serialize)]
217pub struct Plan {
218 pub jobs: Vec<Job>,
220
221 #[serde(skip_serializing_if = "Option::is_none")]
223 pub relations: Option<Vec<Relation>>,
224
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub clustering: Option<Clustering>,
228}
229
230#[derive(Clone, Deserialize, Debug, Serialize)]
236pub struct VehicleCosts {
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub fixed: Option<Float>,
240
241 pub distance: Float,
243
244 pub time: Float,
246}
247
248#[derive(Clone, Deserialize, Debug, Serialize)]
250pub struct ShiftStart {
251 pub earliest: String,
253
254 #[serde(skip_serializing_if = "Option::is_none")]
258 pub latest: Option<String>,
259
260 pub location: Location,
262}
263
264#[derive(Clone, Deserialize, Debug, Serialize)]
266pub struct ShiftEnd {
267 #[serde(skip_serializing_if = "Option::is_none")]
270 pub earliest: Option<String>,
271
272 pub latest: String,
274
275 pub location: Location,
277}
278
279#[derive(Clone, Deserialize, Debug, Serialize)]
281pub struct VehicleShift {
282 pub start: ShiftStart,
284
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub end: Option<ShiftEnd>,
288
289 #[serde(skip_serializing_if = "Option::is_none")]
291 pub breaks: Option<Vec<VehicleBreak>>,
292
293 #[serde(skip_serializing_if = "Option::is_none")]
296 pub reloads: Option<Vec<VehicleReload>>,
297
298 #[serde(skip_serializing_if = "Option::is_none")]
300 pub recharges: Option<VehicleRecharges>,
301}
302
303#[derive(Clone, Deserialize, Debug, Serialize)]
305#[serde(rename_all = "camelCase")]
306pub struct VehicleReload {
307 pub location: Location,
309
310 pub duration: Float,
312
313 #[serde(skip_serializing_if = "Option::is_none")]
315 pub times: Option<Vec<Vec<String>>>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub tag: Option<String>,
320
321 #[serde(skip_serializing_if = "Option::is_none")]
323 pub resource_id: Option<String>,
324}
325
326#[derive(Clone, Deserialize, Debug, Serialize)]
328#[serde(rename_all = "camelCase")]
329pub struct VehicleRecharges {
330 pub max_distance: Float,
332
333 pub stations: Vec<VehicleRechargeStation>,
335}
336
337pub type VehicleRechargeStation = JobPlace;
339
340#[derive(Clone, Deserialize, Debug, Serialize)]
342#[serde(rename_all = "camelCase")]
343pub struct VehicleLimits {
344 #[serde(skip_serializing_if = "Option::is_none")]
347 pub max_distance: Option<Float>,
348
349 #[serde(skip_serializing_if = "Option::is_none")]
352 #[serde(alias = "shiftTime")]
353 pub max_duration: Option<Float>,
354
355 #[serde(skip_serializing_if = "Option::is_none")]
358 pub tour_size: Option<usize>,
359}
360
361#[derive(Clone, Deserialize, Debug, Serialize)]
363#[serde(untagged)]
364pub enum VehicleOptionalBreakTime {
365 TimeWindow(Vec<String>),
367 TimeOffset(Vec<Float>),
369}
370
371#[derive(Clone, Deserialize, Debug, Serialize)]
373#[serde(untagged)]
374pub enum VehicleRequiredBreakTime {
375 ExactTime {
378 earliest: String,
380 latest: String,
382 },
383 OffsetTime {
386 earliest: Float,
388 latest: Float,
390 },
391}
392
393#[derive(Clone, Deserialize, Debug, Serialize)]
395pub struct VehicleOptionalBreakPlace {
396 pub duration: Float,
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub location: Option<Location>,
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub tag: Option<String>,
404}
405
406#[derive(Clone, Deserialize, Debug, Serialize)]
408#[serde(rename_all = "kebab-case")]
409pub enum VehicleOptionalBreakPolicy {
410 SkipIfNoIntersection,
412 SkipIfArrivalBeforeEnd,
414}
415
416#[derive(Clone, Deserialize, Debug, Serialize)]
418#[serde(untagged)]
419pub enum VehicleBreak {
420 Optional {
422 time: VehicleOptionalBreakTime,
424 places: Vec<VehicleOptionalBreakPlace>,
426 policy: Option<VehicleOptionalBreakPolicy>,
428 },
429 Required {
432 time: VehicleRequiredBreakTime,
434 duration: Float,
436 },
437}
438
439#[derive(Clone, Deserialize, Debug, Serialize)]
441#[serde(rename_all = "camelCase")]
442pub struct VehicleType {
443 pub type_id: String,
445
446 pub vehicle_ids: Vec<String>,
448
449 pub profile: VehicleProfile,
451
452 pub costs: VehicleCosts,
454
455 pub shifts: Vec<VehicleShift>,
457
458 pub capacity: Vec<i32>,
460
461 #[serde(skip_serializing_if = "Option::is_none")]
463 pub skills: Option<Vec<String>>,
464
465 #[serde(skip_serializing_if = "Option::is_none")]
467 pub limits: Option<VehicleLimits>,
468}
469
470#[derive(Clone, Deserialize, Debug, Serialize)]
472pub struct VehicleProfile {
473 pub matrix: String,
475
476 #[serde(skip_serializing_if = "Option::is_none")]
479 pub scale: Option<Float>,
480}
481
482#[derive(Clone, Deserialize, Debug, Serialize)]
484pub struct MatrixProfile {
485 pub name: String,
487
488 #[serde(skip_serializing_if = "Option::is_none")]
491 pub speed: Option<Float>,
492}
493
494#[derive(Clone, Deserialize, Debug, Serialize)]
496#[serde(tag = "type")]
497pub enum VehicleResource {
498 #[serde(rename(deserialize = "reload", serialize = "reload"))]
500 Reload {
501 id: String,
503 capacity: Vec<i32>,
505 },
506}
507
508#[derive(Clone, Deserialize, Debug, Serialize)]
510pub struct Fleet {
511 pub vehicles: Vec<VehicleType>,
513
514 pub profiles: Vec<MatrixProfile>,
516
517 #[serde(skip_serializing_if = "Option::is_none")]
519 pub resources: Option<Vec<VehicleResource>>,
520}
521
522#[derive(Clone, Deserialize, Debug, Serialize)]
528#[serde(tag = "type", rename_all = "kebab-case")]
529pub enum Objective {
530 MinimizeCost,
532
533 MinimizeDistance,
535
536 MinimizeDuration,
538
539 MinimizeTours,
541
542 MaximizeTours,
544
545 MaximizeValue {
547 #[serde(skip_serializing_if = "Option::is_none")]
549 breaks: Option<Float>,
550 },
551
552 MinimizeUnassigned {
554 #[serde(skip_serializing_if = "Option::is_none")]
557 breaks: Option<Float>,
558 },
559
560 MinimizeArrivalTime,
562
563 BalanceMaxLoad,
565
566 BalanceActivities,
568
569 BalanceDistance,
571
572 BalanceDuration,
574
575 CompactTour {
577 job_radius: usize,
579 },
580
581 TourOrder,
583
584 FastService,
586
587 MultiObjective {
589 strategy: MultiStrategy,
591 objectives: Vec<Objective>,
593 },
594}
595
596#[derive(Clone, Deserialize, Debug, Serialize)]
599#[serde(tag = "name", rename_all = "kebab-case")]
600pub enum MultiStrategy {
601 Sum,
603
604 WeightedSum {
606 weights: Vec<Float>,
608 },
609}
610
611#[derive(Clone, Deserialize, Debug, Serialize)]
617pub struct Problem {
618 pub plan: Plan,
620
621 pub fleet: Fleet,
623
624 #[serde(skip_serializing_if = "Option::is_none")]
626 pub objectives: Option<Vec<Objective>>,
627}
628
629#[derive(Clone, Deserialize, Debug, Serialize)]
631#[serde(rename_all = "camelCase")]
632pub struct Matrix {
633 pub profile: Option<String>,
635
636 pub timestamp: Option<String>,
638
639 #[serde(alias = "durations")]
641 pub travel_times: Vec<i64>,
642
643 pub distances: Vec<i64>,
645
646 #[serde(skip_serializing_if = "Option::is_none")]
648 pub error_codes: Option<Vec<i64>>,
649}
650
651impl Job {
654 pub fn all_tasks_iter(&self) -> impl Iterator<Item = &JobTask> {
656 self.pickups
657 .iter()
658 .chain(self.deliveries.iter())
659 .chain(self.services.iter())
660 .chain(self.replacements.iter())
661 .flatten()
662 }
663}
664
665pub fn deserialize_problem<R: Read>(reader: BufReader<R>) -> Result<Problem, MultiFormatError> {
667 serde_json::from_reader(reader).map_err(|err| {
668 vec![FormatError::new(
669 "E0000".to_string(),
670 "cannot deserialize problem".to_string(),
671 format!("check input json: '{err}'"),
672 )]
673 .into()
674 })
675}
676
677pub fn deserialize_matrix<R: Read>(reader: BufReader<R>) -> Result<Matrix, MultiFormatError> {
679 serde_json::from_reader(reader).map_err(|err| {
680 vec![FormatError::new(
681 "E0001".to_string(),
682 "cannot deserialize matrix".to_string(),
683 format!("check input json: '{err}'"),
684 )]
685 .into()
686 })
687}
688
689pub fn deserialize_locations<R: Read>(reader: BufReader<R>) -> Result<Vec<Location>, MultiFormatError> {
691 serde_json::from_reader(reader).map_err(|err| {
692 vec![FormatError::new(
693 "E0000".to_string(),
694 "cannot deserialize locations".to_string(),
695 format!("check input json: '{err}'"),
696 )]
697 .into()
698 })
699}
700
701pub fn serialize_problem<W: Write>(problem: &Problem, writer: &mut BufWriter<W>) -> Result<(), Error> {
703 serde_json::to_writer_pretty(writer, problem).map_err(Error::from)
704}