GMPRO docs: Driver breaks
Ever take a long road trip and feel like you need a short nap at a highway rest stop before getting back behind the wheel? Those little breaks aren't just nice - they're essential for keeping you refreshed and ready for the road ahead. Professional drivers feel the same way too, which is why the Federal Motor Carrier Safety Administration (FMCSA), part of the United States Department of Transportation, requires a 30-minute break for drivers after 8 hours of work. The European Union has similar rules, and mandates a 45-minute break after 4.5 hours of driving.
In this blog post, I’ll show you how to incorporate driver breaks into a route plan, ensuring they are accounted for when using the Google Maps Platform Route Optimization API (GMPRO).
Part 1: GMPRO: Google Maps Platform route optimization API
Part 2: GMPRO TSP solver: Google Maps with more than 25 waypoints
Part 3: Google Maps route optimization: multi vehicle
Part 4: Fleet routing app - free Google Maps route planner for multiple stops
Part 5: GMPRO docs: Fixed vehicle costs
Part 6: GMPRO docs: Territory optimization and route planning
Part 7: GMPRO docs: Solving the VRP with route clustering and soft constraints
Part 8: GMPRO docs: Driver load balancing with soft constraints
Part 9: GMPRO docs: Driver breaks (this article)
Part 10: GMPRO docs: Complete deliveries before pickups in cargo bike logistics
What is a driver break?
A driver break is a planned pause in a route. The GMPRO documentation is even more specific, defining it as a contiguous period of time during which the vehicle remains idle at its current position and cannot perform any visit.
How do I add driver breaks to my route?
In GMPRO, adding driver breaks to a route is as simple as specifying a breakRule
in the vehicle
object like this:
{
"breakRule": {
"breakRequests": [
{
"earliestStartTime": "2024-07-08T17:00:00Z",
"latestStartTime": "2024-07-08T17:00:00Z",
"minDuration": "1800s"
}
]
}
}
The breakRequests[]
array contains breakRequest
(break) objects, each specifying the start time and duration of a break. To schedule multiple breaks, you can add additional breakRequest
objects to the array.
earliestStartTime
is the earliest time that the break should start by, in UTC "Zulu" format.
latestStartTime
specifies the latest possible time for the break to begin, provided in UTC "Zulu" format. To schedule a break at a fixed time, set both earliestStartTime
and latestStartTime
to the same value.
minDuration
is the total time spent by the driver on break, in string format e.g. "minDuration": "600s"
is 600 seconds, or 10 minutes.
GMPRO driver breaks example
Here's an example of a 30-minute break scheduled to start at 10:00 AM PST, incorporated into a delivery route in Vancouver, BC.
Input
{
"model": {
"shipments": [
{
"deliveries": [
{
"arrivalLocation": {
"latitude": 49.3208826,
"longitude": -123.0710097
},
"duration": "600s",
"timeWindows": [
{
"startTime": "2024-07-08T16:00:00Z",
"endTime": "2024-07-08T19:00:00Z"
}
]
}
],
"loadDemands": {
"weight": {
"amount": "1"
}
},
"label": "yvr123"
},
{
"deliveries": [
{
"arrivalLocation": {
"latitude": 49.313724,
"longitude": -123.0514561
},
"duration": "600s",
"timeWindows": [
{
"startTime": "2024-07-08T16:00:00Z",
"endTime": "2024-07-08T19:00:00Z"
}
]
}
],
"loadDemands": {
"weight": {
"amount": "1"
}
},
"label": "yvr789"
},
{
"deliveries": [
{
"arrivalLocation": {
"latitude": 49.343481,
"longitude": -123.0863414
},
"duration": "600s",
"timeWindows": [
{
"startTime": "2024-07-08T16:00:00Z",
"endTime": "2024-07-08T19:00:00Z"
}
]
}
],
"loadDemands": {
"weight": {
"amount": "1"
}
},
"label": "yvr987"
},
{
"deliveries": [
{
"arrivalLocation": {
"latitude": 49.3352081,
"longitude": -123.1453979
},
"duration": "600s",
"timeWindows": [
{
"startTime": "2024-07-08T16:00:00Z",
"endTime": "2024-07-08T19:00:00Z"
}
]
}
],
"loadDemands": {
"weight": {
"amount": "1"
}
},
"label": "yvr654"
},
{
"deliveries": [
{
"arrivalLocation": {
"latitude": 49.3513846,
"longitude": -123.2631103
},
"duration": "600s",
"timeWindows": [
{
"startTime": "2024-07-08T16:00:00Z",
"endTime": "2024-07-08T19:00:00Z"
}
]
}
],
"loadDemands": {
"weight": {
"amount": "1"
}
},
"label": "yvr321"
}
],
"vehicles": [
{
"startLocation": {
"latitude": 49.3191441,
"longitude": -122.9522224
},
"loadLimits": {
"weight": {
"maxLoad": 5
}
},
"startTimeWindows": [
{
"startTime": "2024-07-08T16:00:00Z"
}
],
"endTimeWindows": [
{
"endTime": "2024-07-08T19:00:00Z"
}
],
"breakRule": {
"breakRequests": [
{
"earliestStartTime": "2024-07-08T17:00:00Z",
"latestStartTime": "2024-07-08T17:00:00Z",
"minDuration": "1800s"
}
]
},
"label": "will-yvr",
"costPerKilometer": 1
}
],
"globalStartTime": "2024-07-08T07:00:00Z",
"globalEndTime": "2024-07-09T06:59:00Z"
},
"populatePolylines": true
}
Output
{
"routes": [
{
"vehicleLabel": "will-yvr",
"vehicleStartTime": "2024-07-08T16:00:00Z",
"vehicleEndTime": "2024-07-08T18:10:29Z",
"visits": [
{
"shipmentIndex": 1,
"startTime": "2024-07-08T16:12:11Z",
"detour": "0s",
"shipmentLabel": "yvr789",
"loadDemands": {
"weight": {
"amount": "-1"
}
}
},
{
"startTime": "2024-07-08T16:27:57Z",
"detour": "614s",
"shipmentLabel": "yvr123",
"loadDemands": {
"weight": {
"amount": "-1"
}
}
},
{
"shipmentIndex": 2,
"startTime": "2024-07-08T16:47:46Z",
"detour": "1908s",
"shipmentLabel": "yvr987",
"loadDemands": {
"weight": {
"amount": "-1"
}
}
},
{
"shipmentIndex": 3,
"startTime": "2024-07-08T17:37:27Z",
"detour": "4706s",
"shipmentLabel": "yvr654",
"loadDemands": {
"weight": {
"amount": "-1"
}
}
},
{
"shipmentIndex": 4,
"startTime": "2024-07-08T18:00:29Z",
"detour": "5660s",
"shipmentLabel": "yvr321",
"loadDemands": {
"weight": {
"amount": "-1"
}
}
}
],
"transitions": [
{
"travelDuration": "731s",
"travelDistanceMeters": 7966,
"waitDuration": "0s",
"totalDuration": "731s",
"startTime": "2024-07-08T16:00:00Z",
"vehicleLoads": {
"weight": {
"amount": "5"
}
}
},
{
"travelDuration": "346s",
"travelDistanceMeters": 2383,
"waitDuration": "0s",
"totalDuration": "346s",
"startTime": "2024-07-08T16:22:11Z",
"vehicleLoads": {
"weight": {
"amount": "4"
}
}
},
{
"travelDuration": "589s",
"travelDistanceMeters": 4089,
"waitDuration": "0s",
"totalDuration": "589s",
"startTime": "2024-07-08T16:37:57Z",
"vehicleLoads": {
"weight": {
"amount": "3"
}
}
},
{
"travelDuration": "581s",
"travelDistanceMeters": 6408,
"breakDuration": "1800s",
"waitDuration": "0s",
"totalDuration": "2381s",
"startTime": "2024-07-08T16:57:46Z",
"vehicleLoads": {
"weight": {
"amount": "2"
}
}
},
{
"travelDuration": "782s",
"travelDistanceMeters": 12161,
"waitDuration": "0s",
"totalDuration": "782s",
"startTime": "2024-07-08T17:47:27Z",
"vehicleLoads": {
"weight": {
"amount": "1"
}
}
},
{
"travelDuration": "0s",
"waitDuration": "0s",
"totalDuration": "0s",
"startTime": "2024-07-08T18:10:29Z",
"vehicleLoads": {
"weight": {}
}
}
],
"routePolyline": {
"points": "ks_lHbfmmV?l@s@??l@?LAp@L?F?rA@~@@x@A~AGJ?dAFdB?j@Az@AzAC?\\At@?v@?rAAlB@p@AdF?lB?b@CpC?lB?lBAv@?dD?zC?T?hC?PAdD?H?r@An@ArC?hA?t@?v@A`KAvB?f@Av@?fA?~@Gb@?^?N?pB@x@?XBjB@Z?h@@t@@lA@vA@v@BxBL|HH|H?LBlB@lBD~ED~E@n@@jAAnD?lB?r@?z@AdC?t@Av@?v@?lBAt@?nBCjBAnB?t@?vAAdBAtBC|EAdDAbDAtCChB?nA?|BAjBAzE?\\AdDAdB?p@AhBAtB?BAt@?v@?hB?B?j@?D?P?T@V@VDp@Ff@D^Ff@F\\BJF\\H\\FRDR`@zAj@|ATp@L\\?@L\\HVVr@t@xBL^JXNb@n@bBHTLTZl@bArBZj@pA~BnAxBTb@Vb@p@tAh@`A`@r@r@lANZr@bBTp@Np@TlAHdABl@@n@CbACh@ALGd@EZGh@G\\Mv@Kp@Kj@AFCP?DE`@Ej@?BEn@AZ?N?T?R?Z?NBj@@^@T@B@TH`ALlABVFv@JdA@HFt@Hz@Dd@@HFr@?@Db@B\\@^@\\?\\?B?@AVA\\CXCPMj@?DI\\GPCFGN]t@CFCDs@rAi@fAm@jAO\\M\\GLCFIVENENKd@W~AE^CXEh@Eh@?BEfA?@Ah@?f@AjAAd@?N?T?l@?l@?`@BjA@RFtADv@BdA?BD`@B\\DTNv@Lj@DNFVFVLd@Nn@FZFT?BDTFf@?p@?ZALAJCTEPITELGLEFEFGHEFABABABAD?De@TMFMFWNKLGLKPEJGPM\\IRQn@Md@CNITA@ETEJAHANAZA^Ih@El@?X?B?hA?D?X@|@A^?b@@lBAlAArC?X?JCbKAzAAPEr@?BAr@?@?J?j@?N?X?bA?d@Ab@ArBAvA?v@?t@?~@?p@?lECbA?lA?N@HBJ?v@?X?^Av@AfBAbCAjDA`ACtD?nBAhB?ZApAAnB?v@A~@MWKSJRLV?hB?lAAdD?v@?~@AlB?vAAh@AbBAt@Av@?t@Af@?nB?BAbB@RA\\?lA?D?n@AxA?zAAlCAz@AnBAlA?b@AtCCdDCfKC`BAJAJAFEJQf@GTA@Ql@Sj@A?e@zAOd@K^M`@e@vAUj@ADe@tAAD{BxGM`@KKKM{@w@OOk@k@[YSMGCSGA?MCa@?eA?kBCcBAC?_@?gAAuAAO?k@AK?e@AM?sACK?aAAI?a@?UAI?O?Y?[A?t@?R?x@?t@?r@?\\AF?N?j@?BAv@?D?p@D?d@@z@B?cB?mA?cA?e@@iDUAI?O?Y?[A[?kACU?sAEc@A{@AkBAiBAsB?}A@cBEiBAeBAiBAeBCmB?yACeBEoA@M?_BAAtA?v@AdD?t@Aj@?H?h@?dA?rAiAEc@CmAEI?o@Ai@?M?W?E?E?S?S@_@AaAAcCBc@@O@QFK?gACS?eBCc@?kBAs@Ac@?c@AQ?E?MAeAC@v@@|@?dD?v@AdD?vB?zE?JAzF?nA?^?dCApD?hA?lB?dDAlBAzA?tA?t@?v@@lB?v@?t@?pD?`D?@?t@A|E?v@?v@eACA?cBCS?A?wC@U?wAAq@Be@@]AMAIAICGCIEICGEGEIGiCcBuDcCcBkAkAu@gAo@qAy@On@It@Iz@INCHBIHOH{@Hu@No@pAx@fAn@jAt@bBjAtDbChCbBHFFDFDHBHDFBHBH@L@\\@d@Ap@CvA@T?vCA@?R?bBB@?dAB~ABhB?v@?N?hAB@?dA@vBBjAAPFjBB\\@lA@@l@?N?f@?F?F@d@?h@@T@\\@`@Bh@?BD|@@j@Bf@HzC?B?@HPAxA?vAAt@?x@A~A?TA`@?J?`@AR?p@ChBA^CpBAf@C~@An@A~@?XAd@?L?XA^?p@A~@Al@Ab@?ZAv@?LAf@?@Ab@Ad@Aj@Al@?PChACnAAX?`@Aj@A@A~@Ab@Ab@Af@?h@CdAA`AAp@A`@Ad@?^Af@C`BAh@AxAAvB?`A?r@?f@A|A?rA?\\?R?|E?f@?L?d@?z@?b@?j@?h@?|@?l@?Z?lA?~@?`AAlC?N?dC?rB?fB?nA?`@?hA?v@?hA?z@?~A?pA?|@?\\?h@?|@?j@?Z?^?d@?N?T?b@A`@?h@AZAFA`@Eb@?FEXG`@GZADI^K\\M\\EHIPM\\MTOZa@t@k@hACBOVQ\\]n@S`@U`@GL]n@QZOXQZOXOXQ\\O\\MVOXIPEJOZEFIP]r@MVMVKTEHWh@EFMZOVUNA@EHINa@r@i@`AQZKROZ]v@Qb@CFEJUp@Od@[dAM`@Mj@Mv@Gj@SxACZA^Cj@Ch@ClAChA?JCz@A|@AZ?`@AN?`AChBAr@CzCA`A^?r@@T@f@@pAEfAEP?PAh@?LA@?XIvB@H?|@@l@@z@@@x@?z@Az@?NAf@?J?h@A`@?FAD?DCDADE@C@C@Q?S@O?_@@I?[@S?O@G@S@G@UFSHURA?EHEDOPEHIJGLENG^I\\CTCRCTAXA^Af@?@?P?rA?tB?j@@H?J?H@HDh@B^@D?F?D?|@?F?p@?|@?n@?jA?lB?lB?lBAv@?lB?t@AlD?n@xA?zA??u@?o@?n@?t@{A?yA?}ACwA?_BC[@U?c@?@PAb@?l@?`B?T?vAAdE?J?F?xA?R?xAArB?t@?t@?@?L?X?N?L?h@@r@Al@?z@?pA?nA?Z?j@?^?^?h@?B?Z?`AAlA@H?H?~@?D?r@Aj@?P?n@?bB?J?lBS?gAAe@?s@?cA?U?cA?EAQAwA?{A?w@?c@?k@?q@?OAKAIAGAECGCIAICIEKEIGQOKKKKIMKOKOGMWw@AEKa@IWK_@CUAEEUEYAYM?E?C@A?EBA@A@A@A@CDCFADAD?@AB?D?HAN?H?`@?T?f@?F?n@?X?D?t@?VA`@?R@`@?v@AH?^?\\?|@?^ATAb@?VAH?@?BFXGfAAPCf@C`@Ch@C^Ch@APAHC`@A^Ch@Cb@AHARCf@AZEh@Cb@A\\?@Cd@CZ?F?BC\\Cf@Cb@AP?@AJA`@C\\Cd@Cd@C^Cf@A^Cd@ATEh@A^A`@?BAXCl@?XAh@Ab@Af@?\\Ah@?h@?X@`@?b@@^?J@\\@d@@b@?L@L@L@\\@d@Bd@@X?@FnABz@Bd@FdB@X@l@?@B`ABhA@b@@fA@f@?X?b@?^?H?Z?h@?^?@Af@?`@A`@?TAl@Af@A^A^Ch@A\\Ad@CV?DCd@Ab@Cb@C^IdAGbAGbAIhAGbACh@AXCf@A^AVCl@Aj@AXAb@Af@A\\A`@Aj@CdAAt@Ad@A\\C`BChAAn@Cp@Az@AHAbAAH?@Ap@?FAVAZ?@Cb@A\\ALATCb@Eb@?FCXGd@EXEXGb@I\\I^Mf@GVOd@O`@M^O^Q^MXS\\KT]p@S^Q\\Yj@S^CFEJO\\MVO\\MZKZM^GREPK\\I^CNCLK`@In@Kr@C`@E^Cb@Cf@Cb@A`@A`@Cb@Ad@?^Ab@?JEhCCjB?@?d@AbAAbA?B?|A?fB?B?n@?`@@h@?`@?`@@fA@b@?b@?B@^@hAB~@?JBz@@f@@b@@T@j@B`@B`ABd@@d@DhAB`@HhB@b@Dl@Bv@?FF|@@f@?@Bb@Bf@@ZFdA@b@FfA@ZB`@Bn@DdAFdABz@@FD~@D|@Bz@@BB|@FpAD`A@DBn@ZvGD`A?H@L@Z@L?LBd@Bh@@b@@^?DB^@b@@bA@fA@`@?N?N@h@?l@?T?d@A`@?d@?h@Ab@?JAX?^Ad@Ah@C`AAf@AZCx@?HCpAE~BAd@Cd@Av@Av@EhBGnBA^Ab@C`@A`@Cd@C^Cd@C\\?BE^APAPGx@Gh@KfAE\\Ed@ABE\\E\\Gd@E\\G`@G^EVCHE\\GXAFIb@EVAFG^ShAG`@I`@G^G\\G\\G^Ib@Kh@G`@Ib@Ib@AFCJCPG`@I`@G^G^ANCNAFEXIf@AFE\\Gd@CTCNGd@K`AG`@Ed@E^C`@Gb@Eb@Ed@C`@C^Ed@C\\Cb@Eb@C`@GhACb@A^Ef@Ab@Cb@A`@A^Cd@Ab@Aj@AZ?FAh@Ah@Af@Ad@?f@?d@Aj@?b@?f@?p@?\\?H?b@?h@BlC?Z?l@@x@?X?L?b@@`@?b@?`@@b@?b@?d@@b@?`@?f@@`@?`@?f@@`A?f@A`@?b@AZAd@Ad@C`@Cb@Eb@E\\G`@G`@?BGXCLCRCHGTI`@IZM^ITOJA??BEJ]|@Qb@g@pA?@gApCMb@Qj@[fA[nA[bBCJO|@G`@ENGJCDCBCBE@G@C@C?I?ME?VKtCEnB?NSjDC\\Gn@ABKr@a@|CMt@Ib@UdA?@CJUz@Qj@KXCJA?O^U^CBGHILONOLIBIDSFg@LC?E@YFGBMBo@JI@m@J{@De@D]A[Gw@MaBYsAYa@IOCSGSGMGWOGGg@i@kAyAYa@A?m@w@i@c@YUECi@]s@SG?iAGgAFcAXE@m@b@_@n@Ud@M`@E\\CTAZ?JDbAB\\Fp@?BF^PtAF|@@bAARC|@CrA@f@B`@Dt@@L@V@N@DBZ?D?L@p@?DA`@?BANANCXOv@If@G`@Gd@ANATIpA?BEh@AJIh@CHQh@M\\CJAFAD?J?LBFFJFHJHHDPDP@RARKNKHSLa@Hm@Hc@HQDIPOPMRGFATCD?d@A^Cx@Bb@@R@N@b@@b@@X@H@D@JHJJHNRv@FT@DLRJHVNRPJB^LZNFD^Td@f@Z`@TXJPJN\\ZXVNJB@\\J@?ZBh@?XYR]VeANi@Pm@DKLc@@ARi@J]@?h@mALUFKNSBAVSFALCR?TD`@LVLLFHJHSHKFCL?LBD@PHn@`@Z^RPHJVXt@z@HJVb@@@Rl@Rl@@DNr@Dj@D^?V@t@@rA?X@v@?x@@j@"
},
"breaks": [
{
"startTime": "2024-07-08T17:00:00Z",
"duration": "1800s"
}
],
"metrics": {
"performedShipmentCount": 5,
"travelDuration": "3029s",
"waitDuration": "0s",
"delayDuration": "0s",
"breakDuration": "1800s",
"visitDuration": "3000s",
"totalDuration": "7829s",
"travelDistanceMeters": 33007,
"maxLoads": {
"weight": {
"amount": "5"
}
}
},
"routeCosts": {
"model.vehicles.cost_per_kilometer": 33.007
},
"routeTotalCost": 33.007
}
],
"metrics": {
"aggregatedRouteMetrics": {
"performedShipmentCount": 5,
"travelDuration": "3029s",
"waitDuration": "0s",
"delayDuration": "0s",
"breakDuration": "1800s",
"visitDuration": "3000s",
"totalDuration": "7829s",
"travelDistanceMeters": 33007,
"maxLoads": {
"weight": {
"amount": "5"
}
}
},
"usedVehicleCount": 1,
"earliestVehicleStartTime": "2024-07-08T16:00:00Z",
"latestVehicleEndTime": "2024-07-08T18:10:29Z",
"totalCost": 33.007,
"costs": {
"model.vehicles.cost_per_kilometer": 33.007
}
}
}
Information about the break(s) can be found in the route
object in the response. In the breaks
array, you'll find:
{
"breaks": [
{
"startTime": "2024-07-08T17:00:00Z",
"duration": "1800s"
}
]
}
Structurally, breaks are a type of transition
(time interval in between visits
), so they are also included in the transitions
array of each route
object.
{
"travelDuration": "581s",
"travelDistanceMeters": 6408,
"breakDuration": "1800s",
"waitDuration": "0s",
"totalDuration": "2381s",
"startTime": "2024-07-08T16:57:46Z",
"vehicleLoads": {
"weight": {
"amount": "2"
}
}
}
Sequencing breaks and visits
While a break
is not technically a visit
(it does not have a fixed location), it is often helpful to treat it as one when presenting routes to drivers.
To achieve this, we simply combine breaks and visits into a single list and sort them by their startTime
. Here's how to do this for one route. First, create a new folder /route_printer
, and save gmpro_driver_breaks_output.json
to it. This file contains the response from an API call to the GMPRO /optimizeTours
endpoint.
In your favorite text editor, create a new file printRoute.js
. Add the code below and save the file in the same folder as gmpro_driver_breaks_output.json
.
/*** printRoute.js ***/
const fs = require('fs').promises;
async function loadJsonFile(filePath) {
try {
const fileData = await fs.readFile(filePath, 'utf8');
const jsonObject = JSON.parse(fileData);
// Print visits and driver breaks for a single driver
console.log("*** " + jsonObject.routes[0].vehicleLabel + " ***");
const visitsAndBreaks = sortVisitsAndBreaks(jsonObject.routes[0].visits, jsonObject.routes[0].breaks);
printVisitsAndBreaks(visitsAndBreaks);
return jsonObject;
} catch (error) {
console.error('Error loading JSON file:', error);
return null;
}
}
function sortVisitsAndBreaks(visits, breaks) {
const visitsAndBreaks = [...visits, ...breaks];
return visitsAndBreaks.sort((a, b) => new Date(a.startTime) - new Date(b.startTime));
}
function printVisitsAndBreaks(arr) {
for (const obj of arr) {
if (obj.hasOwnProperty('shipmentLabel')) {
console.log(`${obj.shipmentLabel} : ${obj.startTime}`);
} else {
console.log(`break : ${obj.startTime}`);
}
}
}
// Call the function with the file path
loadJsonFile('gmpro_driver_breaks_output.json');
The code above reads a JSON file gmpro_driver_breaks_output.json
and converts its contents into a Javascript object.
/*** printRoute.js ***/
const fs = require('fs').promises;
const fileData = await fs.readFile(filePath, 'utf8');
const jsonObject = JSON.parse(fileData);
Next, we extract the visits
and breaks
array from the output JSON and use the ...
spread operator to merge them into a single array. This array is then sorted by their startTime
in ascending order.
/*** printRoute.js ***/
function sortVisitsAndBreaks(visits, breaks) {
const visitsAndBreaks = [...visits, ...breaks];
return visitsAndBreaks.sort((a, b) => new Date(a.startTime) - new Date(b.startTime));
}
Finally, the array is specially formatted and printed out to show both breaks
and visits
, together with their respective startTimes
.
*** will-yvr ***
yvr789 : 2024-07-08T16:12:11Z
yvr123 : 2024-07-08T16:27:57Z
yvr987 : 2024-07-08T16:47:46Z
break : 2024-07-08T17:00:00Z
yvr654 : 2024-07-08T17:37:27Z
yvr321 : 2024-07-08T18:00:29Z
Scheduling a driver break at a specific location
If you need a driver to take his break at a specific location e.g. a truck rest stop or a gas station, you should instead a create a dummy shipment
with a well defined time window and add a high penaltyCost
(the cost of not delivering this shipment
) to it and a much lower penaltyCost
everywhere else. This will force the GMPRO solver to include the break in the final route solution.
{
"deliveries": [
{
"arrivalLocation": {
"latitude": 49.6312767,
"longitude": -121.0725622
},
"duration": "1800s",
"timeWindows": [
{
"startTime": "2024-07-08T16:00:00Z",
"endTime": "2024-07-08T16:30:00Z"
}
]
}
],
"penaltyCost": 9001,
"label": "lunch-break@coquihalla-lakes-rest-area"
}
Have a driver break, have a Kit Kat
Driver breaks are crucial for two main reasons: they ensure drivers remain well-rested and help your fleet stay compliant with national regulations. After reading this post, you should know how to include driver breaks in a GMPRO API call and integrate them optimally into driver schedules. They can also be used to plan multi day routes with overnight stays, but that is a topic for a different blog post altogether.
👋 As always, if you have any questions or suggestions for me, please reach out or say hello on LinkedIn.