1use super::FeatureCollection;
2use crate::format::{CoordIndex, Location};
3use crate::{format_time, parse_time};
4use serde::{Deserialize, Serialize};
5use std::io::{BufReader, BufWriter, Error, Read, Write};
6use vrp_core::models::common::{Duration, Timestamp};
7use vrp_core::models::solution::Commute as DomainCommute;
8use vrp_core::models::solution::CommuteInfo as DomainCommuteInfo;
9use vrp_core::prelude::Float;
10
11#[derive(Clone, Default, Deserialize, Serialize, PartialEq, Eq, Debug)]
13pub struct Timing {
14 pub driving: i64,
16 pub serving: i64,
18 pub waiting: i64,
20 #[serde(rename(serialize = "break", deserialize = "break"))]
22 pub break_time: i64,
23 #[serde(default = "i64::default")]
25 pub commuting: i64,
26 #[serde(default = "i64::default")]
28 pub parking: i64,
29}
30
31#[derive(Clone, Deserialize, Default, Serialize, PartialEq, Debug)]
33pub struct Statistic {
34 pub cost: Float,
36 pub distance: i64,
38 pub duration: i64,
40 pub times: Timing,
42}
43
44#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
46pub struct Schedule {
47 pub arrival: String,
49 pub departure: String,
51}
52
53#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
55pub struct Interval {
56 pub start: String,
58 pub end: String,
60}
61
62#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
64pub struct Commute {
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub forward: Option<CommuteInfo>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub backward: Option<CommuteInfo>,
71}
72
73#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
75pub struct CommuteInfo {
76 pub location: Location,
78 pub distance: Float,
80 pub time: Interval,
82}
83
84#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
86#[serde(rename_all = "camelCase")]
87pub struct Activity {
88 pub job_id: String,
90 #[serde(rename(serialize = "type", deserialize = "type"))]
92 pub activity_type: String,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub location: Option<Location>,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub time: Option<Interval>,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub job_tag: Option<String>,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub commute: Option<Commute>,
105}
106
107#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
109#[serde(untagged)]
110pub enum Stop {
111 Point(PointStop),
113 Transit(TransitStop),
115}
116
117impl Stop {
118 pub fn schedule(&self) -> &Schedule {
120 match self {
121 Self::Transit(transit) => &transit.time,
122 Self::Point(point) => &point.time,
123 }
124 }
125
126 pub fn schedule_mut(&mut self) -> &mut Schedule {
128 match self {
129 Self::Transit(transit) => &mut transit.time,
130 Self::Point(point) => &mut point.time,
131 }
132 }
133
134 pub fn load(&self) -> &Vec<i32> {
136 match self {
137 Self::Transit(transit) => &transit.load,
138 Self::Point(point) => &point.load,
139 }
140 }
141
142 pub fn load_mut(&mut self) -> &mut Vec<i32> {
144 match self {
145 Self::Transit(transit) => &mut transit.load,
146 Self::Point(point) => &mut point.load,
147 }
148 }
149
150 pub fn activities(&self) -> &Vec<Activity> {
152 match self {
153 Stop::Transit(transit) => &transit.activities,
154 Stop::Point(point) => &point.activities,
155 }
156 }
157
158 pub fn activities_mut(&mut self) -> &mut Vec<Activity> {
160 match self {
161 Stop::Transit(transit) => &mut transit.activities,
162 Stop::Point(point) => &mut point.activities,
163 }
164 }
165
166 pub fn location(&self) -> Option<&Location> {
168 match self {
169 Self::Transit(_) => None,
170 Self::Point(point) => Some(&point.location),
171 }
172 }
173
174 pub fn as_point(&self) -> Option<&PointStop> {
176 match self {
177 Self::Point(point) => Some(point),
178 _ => None,
179 }
180 }
181
182 pub fn to_point(self) -> PointStop {
184 match self {
185 Self::Point(point) => point,
186 _ => panic!("non point type"),
187 }
188 }
189}
190
191#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
193pub struct TransitStop {
194 pub time: Schedule,
196 pub load: Vec<i32>,
198 pub activities: Vec<Activity>,
200}
201
202#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
204pub struct PointStop {
205 pub location: Location,
207 pub time: Schedule,
209 pub distance: i64,
211 pub load: Vec<i32>,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub parking: Option<Interval>,
216 pub activities: Vec<Activity>,
218}
219
220#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
222#[serde(rename_all = "camelCase")]
223pub struct Tour {
224 pub vehicle_id: String,
226 pub type_id: String,
228 #[serde(default)]
230 pub shift_index: usize,
231 pub stops: Vec<Stop>,
233 pub statistic: Statistic,
235}
236
237#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
239pub struct UnassignedJobReason {
240 pub code: String,
242 pub description: String,
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub details: Option<Vec<UnassignedJobDetail>>,
247}
248
249#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
251#[serde(rename_all = "camelCase")]
252pub struct UnassignedJobDetail {
253 pub vehicle_id: String,
255 pub shift_index: usize,
257}
258
259#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
261#[serde(rename_all = "camelCase")]
262pub struct UnassignedJob {
263 pub job_id: String,
265 pub reasons: Vec<UnassignedJobReason>,
267}
268
269#[derive(Clone, Deserialize, Serialize, Eq, PartialEq, Debug)]
271#[serde(rename_all = "camelCase")]
272#[serde(tag = "type")]
273pub enum Violation {
274 #[serde(rename(deserialize = "break", serialize = "break"))]
276 Break {
277 vehicle_id: String,
279 shift_index: usize,
281 },
282}
283
284#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
286pub struct Metrics {
287 pub duration: usize,
289 pub generations: usize,
291 pub speed: Float,
293 pub evolution: Vec<Generation>,
295}
296
297#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
299#[serde(rename_all = "camelCase")]
300pub struct Generation {
301 pub number: usize,
303 pub timestamp: Float,
305 pub i_all_ratio: Float,
307 pub i_1000_ratio: Float,
309 pub is_improvement: bool,
311 pub population: Population,
313}
314
315#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
317#[serde(rename_all = "camelCase")]
318pub struct Individual {
319 pub difference: Float,
321 pub fitness: Vec<Float>,
323}
324
325#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
327#[serde(rename_all = "camelCase")]
328pub struct Population {
329 pub individuals: Vec<Individual>,
331}
332
333#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
335pub struct Extras {
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub metrics: Option<Metrics>,
339
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub features: Option<FeatureCollection>,
343}
344
345#[derive(Clone, Deserialize, Serialize, PartialEq, Debug)]
347#[serde(rename_all = "camelCase")]
348pub struct Solution {
349 pub statistic: Statistic,
351
352 pub tours: Vec<Tour>,
354
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub unassigned: Option<Vec<UnassignedJob>>,
358
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub violations: Option<Vec<Violation>>,
362
363 #[serde(skip_serializing_if = "Option::is_none")]
365 pub extras: Option<Extras>,
366}
367
368pub fn serialize_solution<W: Write>(solution: &Solution, writer: &mut BufWriter<W>) -> Result<(), Error> {
370 serde_json::to_writer_pretty(writer, solution).map_err(Error::from)
371}
372
373pub fn deserialize_solution<R: Read>(reader: BufReader<R>) -> Result<Solution, Error> {
375 serde_json::from_reader(reader).map_err(Error::from)
376}
377
378impl Interval {
379 pub fn duration(&self) -> Duration {
381 parse_time(&self.end) - parse_time(&self.start)
382 }
383}
384
385impl Commute {
386 pub fn new(commute: &DomainCommute, start: Timestamp, end: Timestamp, coord_index: &CoordIndex) -> Commute {
388 let parse_info = |info: &DomainCommuteInfo, time: Timestamp| {
389 if info.is_zero_distance() {
390 None
391 } else {
392 Some(CommuteInfo {
393 location: coord_index.get_by_idx(info.location).expect("commute info has no location"),
394 distance: info.distance,
395 time: Interval { start: format_time(time), end: format_time(time + info.duration) },
396 })
397 }
398 };
399
400 Commute { forward: parse_info(&commute.forward, start), backward: parse_info(&commute.backward, end) }
401 }
402
403 pub(crate) fn to_domain(&self, coord_index: &CoordIndex) -> DomainCommute {
405 let parse_info = |info: &Option<CommuteInfo>| {
406 info.as_ref().map_or(DomainCommuteInfo::default(), |info| {
407 let start = parse_time(&info.time.start);
408 let end = parse_time(&info.time.end);
409 DomainCommuteInfo {
410 location: coord_index.get_by_loc(&info.location).expect("expect to have coordinate in commute"),
411 distance: info.distance,
412 duration: end - start,
413 }
414 })
415 };
416
417 DomainCommute { forward: parse_info(&self.forward), backward: parse_info(&self.backward) }
418 }
419}