1#[cfg(test)]
2#[path = "../../tests/unit/lilim/reader_test.rs"]
3mod reader_test;
4
5use crate::common::*;
6use std::collections::HashMap;
7use std::io::{BufReader, Read};
8use std::sync::Arc;
9use vrp_core::construction::features::JobDemandDimension;
10use vrp_core::models::common::*;
11use vrp_core::models::problem::*;
12use vrp_core::models::*;
13use vrp_core::models::{Extras, Problem};
14use vrp_core::prelude::GenericError;
15use vrp_core::utils::Float;
16
17pub trait LilimProblem {
19 fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError>;
21}
22
23impl<R: Read> LilimProblem for BufReader<R> {
24 fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError> {
25 LilimReader { buffer: String::new(), reader: self, coord_index: CoordIndex::default() }.read_problem(is_rounded)
26 }
27}
28
29impl LilimProblem for String {
30 fn read_lilim(self, is_rounded: bool) -> Result<Problem, GenericError> {
31 BufReader::new(self.as_bytes()).read_lilim(is_rounded)
32 }
33}
34
35struct VehicleLine {
36 number: usize,
37 capacity: usize,
38 _ignored: usize,
39}
40
41struct JobLine {
42 id: usize,
43 location: (i32, i32),
44 demand: i32,
45 tw: TimeWindow,
46 service: usize,
47 relation: usize,
48}
49
50struct Relation {
51 pickup: usize,
52 delivery: usize,
53}
54
55struct LilimReader<R: Read> {
56 buffer: String,
57 reader: BufReader<R>,
58 coord_index: CoordIndex,
59}
60
61impl<R: Read> TextReader for LilimReader<R> {
62 fn create_goal_context(
63 &self,
64 activity: Arc<SimpleActivityCost>,
65 transport: Arc<dyn TransportCost>,
66 ) -> Result<GoalContext, GenericError> {
67 let is_time_constrained = true;
68 create_goal_context_prefer_min_tours(activity, transport, is_time_constrained)
69 }
70
71 fn read_definitions(&mut self) -> Result<(Vec<Job>, Fleet), GenericError> {
72 let fleet = self.read_fleet()?;
73 let jobs = self.read_jobs()?;
74
75 Ok((jobs, fleet))
76 }
77
78 fn create_transport(&self, is_rounded: bool) -> Result<Arc<dyn TransportCost>, GenericError> {
79 self.coord_index.create_transport(is_rounded, &self.get_logger())
80 }
81
82 fn create_extras(&self) -> Extras {
83 get_extras(self.coord_index.clone())
84 }
85}
86
87impl<R: Read> LilimReader<R> {
88 fn read_fleet(&mut self) -> Result<Fleet, GenericError> {
89 let vehicle = self.read_vehicle()?;
90 let depot = self.read_customer()?;
91
92 Ok(create_fleet_with_distance_costs(
93 vehicle.number,
94 vehicle.capacity,
95 self.coord_index.collect(depot.location),
96 depot.tw,
97 ))
98 }
99
100 fn read_jobs(&mut self) -> Result<Vec<Job>, GenericError> {
101 let mut customers: HashMap<usize, JobLine> = Default::default();
102 let mut relations: Vec<Relation> = Default::default();
103 loop {
104 match self.read_customer() {
105 Ok(customer) => {
106 if customer.demand > 0 {
107 relations.push(Relation { pickup: customer.id, delivery: customer.relation });
108 }
109 customers.insert(customer.id, customer);
110 }
111 Err(error) => {
112 if self.buffer.is_empty() {
113 break;
114 } else {
115 return Err(error);
116 }
117 }
118 }
119 }
120
121 let mut jobs: Vec<Job> = Default::default();
122 relations.iter().zip(0..).for_each(|(relation, index)| {
123 let pickup = customers.get(&relation.pickup).unwrap();
124 let delivery = customers.get(&relation.delivery).unwrap();
125
126 jobs.push(Job::Multi(Multi::new_shared(
127 vec![self.create_single_job(pickup), self.create_single_job(delivery)],
128 create_dimens_with_id("", &index.to_string(), |id, dimens| {
129 dimens.set_job_id(id.to_string());
130 }),
131 )));
132 });
133
134 Ok(jobs)
135 }
136
137 fn create_single_job(&mut self, customer: &JobLine) -> Arc<Single> {
138 let mut dimens = create_dimens_with_id("c", &customer.id.to_string(), |id, dimens| {
139 dimens.set_job_id(id.to_string());
140 });
141 dimens.set_job_demand(if customer.demand > 0 {
142 Demand::<SingleDimLoad> {
143 pickup: (SingleDimLoad::default(), SingleDimLoad::new(customer.demand)),
144 delivery: (SingleDimLoad::default(), SingleDimLoad::default()),
145 }
146 } else {
147 Demand::<SingleDimLoad> {
148 pickup: (SingleDimLoad::default(), SingleDimLoad::default()),
149 delivery: (SingleDimLoad::default(), SingleDimLoad::new(customer.demand)),
150 }
151 });
152
153 Arc::new(Single {
154 places: vec![Place {
155 location: Some(self.coord_index.collect(customer.location)),
156 duration: customer.service as Float,
157 times: vec![TimeSpan::Window(customer.tw.clone())],
158 }],
159 dimens: Default::default(),
160 })
161 }
162
163 fn read_vehicle(&mut self) -> Result<VehicleLine, GenericError> {
164 read_line(&mut self.reader, &mut self.buffer)?;
165 let (number, capacity, _ignored) = self
166 .buffer
167 .split_whitespace()
168 .map(|line| line.parse::<usize>().unwrap())
169 .try_collect_tuple()
170 .ok_or_else(|| "cannot parse vehicle number or/and capacity".to_string())?;
171
172 Ok(VehicleLine { number, capacity, _ignored })
173 }
174
175 fn read_customer(&mut self) -> Result<JobLine, GenericError> {
176 read_line(&mut self.reader, &mut self.buffer)?;
177 let (id, x, y, demand, start, end, service, _, relation) = self
178 .buffer
179 .split_whitespace()
180 .map(|line| line.parse::<i32>().unwrap())
181 .try_collect_tuple()
182 .ok_or_else(|| "cannot read customer line".to_string())?;
183 Ok(JobLine {
184 id: id as usize,
185 location: (x, y),
186 demand,
187 tw: TimeWindow::new(start as Float, end as Float),
188 service: service as usize,
189 relation: relation as usize,
190 })
191 }
192}