diff --git a/examples/data/pragmatic/basics/required-break.tricky.matrix.json b/examples/data/pragmatic/basics/required-break.tricky.matrix.json new file mode 100644 index 000000000..b19fa68dd --- /dev/null +++ b/examples/data/pragmatic/basics/required-break.tricky.matrix.json @@ -0,0 +1 @@ +{"profile": "normal_car", "durations": [0, 635, 24, 580, 27, 2232, 625, 0, 650, 76, 653, 2507, 24, 660, 0, 605, 3, 2257, 570, 95, 595, 0, 598, 2449, 27, 663, 3, 608, 0, 2260, 2232, 2545, 2257, 2515, 2260, 0], "distances": [0, 8888, 192, 8510, 215, 52931, 8896, 0, 9088, 450, 9111, 56579, 192, 9080, 0, 8702, 23, 53123, 8518, 450, 8710, 0, 8733, 60163, 215, 9103, 23, 8725, 0, 53146, 52996, 56684, 53188, 60477, 53211, 0]} \ No newline at end of file diff --git a/examples/data/pragmatic/basics/required-break.tricky.problem.json b/examples/data/pragmatic/basics/required-break.tricky.problem.json new file mode 100644 index 000000000..77ab05952 --- /dev/null +++ b/examples/data/pragmatic/basics/required-break.tricky.problem.json @@ -0,0 +1,219 @@ +{ + "plan": { + "jobs": [ + { + "id": "0", + "pickups": null, + "deliveries": null, + "services": [ + { + "places": [ + { + "location": { + "index": 0 + }, + "duration": 10800.0, + "times": [ + [ + "2026-01-19T00:00:00+11:00", + "2026-01-19T23:59:59.999999+11:00" + ] + ], + "tag": null + } + ] + } + ], + "replacements": null, + "skills": null + }, + { + "id": "1", + "pickups": null, + "deliveries": null, + "services": [ + { + "places": [ + { + "location": { + "index": 1 + }, + "duration": 3600.0, + "times": [ + [ + "2026-01-18T22:30:00+00:00", + "2026-01-18T22:30:00+00:00" + ] + ], + "tag": null + } + ] + } + ], + "replacements": null, + "skills": null + }, + { + "id": "2", + "pickups": null, + "deliveries": null, + "services": [ + { + "places": [ + { + "location": { + "index": 2 + }, + "duration": 1800.0, + "times": [ + [ + "2026-01-19T00:15:00+00:00", + "2026-01-19T00:15:00+00:00" + ] + ], + "tag": null + } + ] + } + ], + "replacements": null, + "skills": null + }, + { + "id": "3", + "pickups": null, + "deliveries": null, + "services": [ + { + "places": [ + { + "location": { + "index": 3 + }, + "duration": 5400.0, + "times": [ + [ + "2026-01-18T21:00:00+00:00", + "2026-01-18T21:00:00+00:00" + ] + ], + "tag": null + } + ] + } + ], + "replacements": null, + "skills": null + }, + { + "id": "4", + "pickups": null, + "deliveries": null, + "services": [ + { + "places": [ + { + "location": { + "index": 4 + }, + "duration": 1800.0, + "times": [ + [ + "2026-01-19T00:45:00+00:00", + "2026-01-19T00:45:00+00:00" + ] + ], + "tag": null + } + ] + } + ], + "replacements": null, + "skills": null + } + ], + "relations": [ + { + "type": "sequence", + "jobs": [ + "3", + "1", + "2", + "4" + ], + "vehicleId": "37", + "shiftIndex": 0 + } + ] + }, + "fleet": { + "vehicles": [ + { + "typeId": "37", + "vehicleIds": [ + "37" + ], + "profile": { + "matrix": "van" + }, + "costs": { + "fixed": 22.0, + "distance": 0.0002, + "time": 0.004806 + }, + "shifts": [ + { + "start": { + "earliest": "2026-01-19T08:00:00+11:00", + "location": { + "index": 5 + }, + "latest": "2026-01-19T08:00:00+11:00" + }, + "end": { + "latest": "2026-01-19T16:00:00+11:00", + "location": { + "index": 5 + }, + "earliest": null + }, + "breaks": [ + { + "time": { + "latest": 15303, + "earliest": 15303 + }, + "duration": 1800 + } + ], + "reloads": null + } + ], + "capacity": [ + 999 + ], + "skills": [] + } + ], + "profiles": [ + { + "name": "van" + } + ] + }, + "objectives": [ + { + "type": "minimize-unassigned", + "breaks": 1.0 + }, + { + "type": "minimize-tours" + }, + { + "type": "minimize-arrival-time" + }, + { + "type": "minimize-cost" + } + ] +} diff --git a/examples/data/pragmatic/basics/required-break.tricky.solution.json b/examples/data/pragmatic/basics/required-break.tricky.solution.json new file mode 100644 index 000000000..7dcf536c8 --- /dev/null +++ b/examples/data/pragmatic/basics/required-break.tricky.solution.json @@ -0,0 +1,168 @@ +{ + "statistic": { + "cost": 133.735938, + "distance": 123184, + "duration": 18123, + "times": { + "driving": 5523, + "serving": 12600, + "waiting": 0, + "break": 0, + "commuting": 0, + "parking": 0 + } + }, + "tours": [ + { + "vehicleId": "37", + "typeId": "37", + "shiftIndex": 0, + "stops": [ + { + "location": { + "index": 5 + }, + "time": { + "arrival": "2026-01-18T21:00:00Z", + "departure": "2026-01-18T21:00:00Z" + }, + "distance": 0, + "load": [ + 0 + ], + "activities": [ + { + "jobId": "departure", + "type": "departure" + } + ] + }, + { + "location": { + "index": 3 + }, + "time": { + "arrival": "2026-01-18T21:41:55Z", + "departure": "2026-01-18T23:11:55Z" + }, + "distance": 60477, + "load": [ + 0 + ], + "activities": [ + { + "jobId": "3", + "type": "service" + } + ] + }, + { + "location": { + "index": 1 + }, + "time": { + "arrival": "2026-01-18T23:13:30Z", + "departure": "2026-01-19T00:13:30Z" + }, + "distance": 60927, + "load": [ + 0 + ], + "activities": [ + { + "jobId": "1", + "type": "service" + } + ] + }, + { + "location": { + "index": 2 + }, + "time": { + "arrival": "2026-01-19T00:24:20Z", + "departure": "2026-01-19T00:54:20Z" + }, + "distance": 70015, + "load": [ + 0 + ], + "activities": [ + { + "jobId": "2", + "type": "service" + } + ] + }, + { + "location": { + "index": 4 + }, + "time": { + "arrival": "2026-01-19T00:54:23Z", + "departure": "2026-01-19T01:24:23Z" + }, + "distance": 70038, + "load": [ + 0 + ], + "activities": [ + { + "jobId": "4", + "type": "service" + } + ] + }, + { + "location": { + "index": 5 + }, + "time": { + "arrival": "2026-01-19T02:02:03Z", + "departure": "2026-01-19T02:02:03Z" + }, + "distance": 123184, + "load": [ + 0 + ], + "activities": [ + { + "jobId": "arrival", + "type": "arrival" + } + ] + } + ], + "statistic": { + "cost": 133.735938, + "distance": 123184, + "duration": 18123, + "times": { + "driving": 5523, + "serving": 12600, + "waiting": 0, + "break": 0, + "commuting": 0, + "parking": 0 + } + } + } + ], + "unassigned": [ + { + "jobId": "0", + "reasons": [ + { + "code": "TIME_WINDOW_CONSTRAINT", + "description": "cannot be visited within time window", + "details": [ + { + "vehicleId": "37", + "shiftIndex": 0 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/json-pragmatic/src/main.rs b/examples/json-pragmatic/src/main.rs index 490de7638..c168849a7 100644 --- a/examples/json-pragmatic/src/main.rs +++ b/examples/json-pragmatic/src/main.rs @@ -8,8 +8,8 @@ use std::sync::Arc; use vrp_pragmatic::checker::CheckerContext; use vrp_pragmatic::core::models::{Problem as CoreProblem, Solution as CoreSolution}; use vrp_pragmatic::core::prelude::*; -use vrp_pragmatic::format::problem::{Matrix, PragmaticProblem, Problem, deserialize_matrix, deserialize_problem}; -use vrp_pragmatic::format::solution::{Solution, deserialize_solution, write_pragmatic}; +use vrp_pragmatic::format::problem::{deserialize_matrix, deserialize_problem, Matrix, PragmaticProblem, Problem}; +use vrp_pragmatic::format::solution::{deserialize_solution, write_pragmatic, Solution}; fn main() { let args: Vec = std::env::args().collect(); @@ -29,6 +29,7 @@ fn run_examples(base_path: &str) { ("basics/priorities.value", None), ("basics/profiles.basic", Some(vec!["basics/profiles.basic.matrix.car", "basics/profiles.basic.matrix.truck"])), ("basics/relation-strict.basic", None), + ("basics/required-break.tricky", Some(vec!["basics/required-break.tricky.matrix"])), ("basics/relation-any.basic", None), ("basics/reload.basic", None), ("basics/reload.multi", None),