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
use crate::format::{JobIndex, UNASSIGNABLE_ROUTE_KEY};
use std::sync::Arc;
use vrp_core::construction::constraints::ConstraintPipeline;
use vrp_core::construction::heuristics::*;
use vrp_core::models::common::{IdDimension, ValueDimension};
use vrp_core::utils::compare_floats;

/// Returns route modifier.
pub fn get_route_modifier(constraint: Arc<ConstraintPipeline>, job_index: JobIndex) -> RouteModifier {
    RouteModifier::new(move |route_ctx: RouteContext| {
        let actor = &route_ctx.route.actor;
        let vehicle = &actor.vehicle;

        let shift_index = vehicle.dimens.get_value::<usize>("shift_index").expect("cannot find shift index");
        let vehicle_id = vehicle.dimens.get_id().expect("cannot get vehicle id");

        let candidates = (1..)
            .map(|idx| format!("{}_dispatch_{}_{}", vehicle_id, shift_index, idx))
            .map(|job_id| job_index.get(&job_id))
            .take_while(|job| job.is_some())
            .collect::<Vec<_>>();

        let result = candidates
            .iter()
            .filter_map(|job| {
                job.map(|job| {
                    evaluate_job_constraint_in_route(job, &constraint, &route_ctx, InsertionPosition::Last, 0., None)
                })
                .and_then(|result| match result {
                    InsertionResult::Success(success) => Some(success),
                    _ => None,
                })
            })
            .min_by(|a, b| compare_floats(a.cost, b.cost));

        if let Some(success) = result {
            let mut route_ctx = success.context;
            let route = route_ctx.route_mut();
            success.activities.into_iter().for_each(|(activity, index)| {
                route.tour.insert_at(activity, index + 1);
            });
            constraint.accept_route_state(&mut route_ctx);

            route_ctx
        } else {
            let mut route_ctx = route_ctx;

            if !candidates.is_empty() {
                route_ctx.state_mut().put_route_state(UNASSIGNABLE_ROUTE_KEY, true);
            }

            route_ctx
        }
    })
}