Real-time planning: extended visit
There are many situations where Real-time planning is necessary.
Sometimes visits take longer than expected.
Consider the following shift schedule:
Carl has three visits scheduled for the day: Visit E, Visit B, and Visit D.
However, Visit E takes much longer than expected.
As a result, Visit B and Visit D need to be rescheduled.
1. Batch schedule: extended visit
Carl’s original schedule was generated from the following input dataset during the regular batch scheduling:
-
Input
-
Output
Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
|
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/field-service-routing/v1/route-plans [email protected]
{
"config": {
"run": {
"name": "Original shift plan: extended visit example"
}
},
"modelInput": {
"vehicles": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"visits": [
{
"id": "Visit E",
"location": [33.84475, -84.63649],
"serviceDuration": "PT1H"
},
{
"id": "Visit B",
"location": [33.90719, -84.28149],
"serviceDuration": "PT1H"
},
{
"id": "Visit D",
"location": [33.89351, -84.00649],
"serviceDuration": "PT1H"
}
]
}
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
|
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/field-service-routing/v1/route-plans/<ID>
{
"run": {
"id": "ID",
"name": "Original shift plan: extended visit example",
"submitDateTime": "2024-10-25T04:26:15.142743595Z",
"startDateTime": "2024-10-25T04:26:20.313758011Z",
"activeDateTime": "2024-10-25T04:26:20.413758011Z",
"completeDateTime": "2024-10-25T04:31:20.852585436Z",
"shutdownDateTime": "2024-10-25T04:31:20.952585436Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-174887soft",
"tags": null,
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"vehicles": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:56Z",
"startServiceTime": "2027-02-01T09:48:56Z",
"departureTime": "2027-02-01T10:48:56Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT48M56S",
"travelDistanceMetersFromPreviousStandstill": 54141,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T11:29:29Z",
"startServiceTime": "2027-02-01T11:29:29Z",
"departureTime": "2027-02-01T12:29:29Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT40M33S",
"travelDistanceMetersFromPreviousStandstill": 42302,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit D",
"kind": "VISIT",
"arrivalTime": "2027-02-01T13:01:19Z",
"startServiceTime": "2027-02-01T13:01:19Z",
"departureTime": "2027-02-01T14:01:19Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT31M50S",
"travelDistanceMetersFromPreviousStandstill": 34289,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT2H39M10S",
"travelTimeFromStartLocationToFirstVisit": "PT48M56S",
"travelTimeBetweenVisits": "PT1H12M23S",
"travelTimeFromLastVisitToEndLocation": "PT37M51S",
"totalTravelDistanceMeters": 165337,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 76591,
"travelDistanceFromLastVisitToEndLocationMeters": 34605,
"endLocationArrivalTime": "2027-02-01T14:39:10Z"
}
}
]
}
]
},
"kpis": {
"totalTravelTime": "PT2H39M10S",
"travelTimeFromStartLocationToFirstVisit": "PT48M56S",
"travelTimeBetweenVisits": "PT1H12M23S",
"travelTimeFromLastVisitToEndLocation": "PT37M51S",
"totalTravelDistanceMeters": 165337,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 76591,
"travelDistanceFromLastVisitToEndLocationMeters": 34605,
"totalUnassignedVisits": 0
}
}
modelOutput
contains Carl’s shift itinerary.
2. Real-time planning update: extended visit
The plan needs to be updated to reflect the situation in the field.
Visit E took three hours longer than expected.
Update the serviceDuration
for Visit E from 1 hour to 4 hours.
Add the minStartTravelTime
from the most recent planning output dataset (batch or real-time) to each visit.
{
"visits": [
{
"id": "Visit E",
"location": [33.84475, -84.63649],
"serviceDuration": "PT4H",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"location": [33.90719, -84.28149],
"serviceDuration": "PT1H",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit D",
"location": [33.89351, -84.00649],
"serviceDuration": "PT1H",
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
]
}
Add the itinerary to Carl’s shifts with Visit E, Visit B, and Visit D, including the visit IDs and the visit kind:
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit E",
"kind": "VISIT"
},
{
"id": "Visit B",
"kind": "VISIT"
},
{
"id": "Visit D",
"kind": "VISIT"
}
]
}
]
}
Freeze the departure times for visits that have already occurred and that technicians have begun traveling to by adding freezeDeparturesBeforeTime
:
{
"modelInput": {
"freezeDeparturesBeforeTime": "2027-02-01T14:10:00Z"
}
}
Because Carl finished Visit E at 13:48 and is already traveling to Visit B at the freezeDeparturesBeforeTime
, this will keep Visit B scheduled after Visit E with a new arrival time.
Submit the updated input dataset to generate the new real-time plan.
-
Input
-
Output
Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
|
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/field-service-routing/v1/route-plans [email protected]
{
"config": {
"run": {
"name": "Real-time planning: extended visit example"
}
},
"modelInput": {
"freezeDeparturesBeforeTime": "2027-02-01T14:10:00Z",
"vehicles": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit E",
"kind": "VISIT"
},
{
"id": "Visit B",
"kind": "VISIT"
},
{
"id": "Visit D",
"kind": "VISIT"
}
]
}
]
}
],
"visits": [
{
"id": "Visit E",
"location": [33.84475, -84.63649],
"serviceDuration": "PT4H",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"location": [33.90719, -84.28149],
"serviceDuration": "PT1H",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit D",
"location": [33.89351, -84.00649],
"serviceDuration": "PT1H",
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
]
}
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
|
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/field-service-routing/v1/route-plans/<ID>
{
"run": {
"id": "ID",
"name": "Real-time planning: extended visit example",
"submitDateTime": "2024-10-28T06:44:03.534487996Z",
"startDateTime": "2024-10-28T06:44:09.323825221Z",
"activeDateTime": "2024-10-28T06:44:09.423825221Z",
"completeDateTime": "2024-10-28T06:44:11.087208734Z",
"shutdownDateTime": "2024-10-28T06:44:11.187208734Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/-4medium/-1132701soft",
"tags": null,
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"vehicles": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:48:56Z",
"startServiceTime": "2027-02-01T09:48:56Z",
"departureTime": "2027-02-01T13:48:56Z",
"effectiveServiceDuration": "PT4H",
"travelTimeFromPreviousStandstill": "PT48M56S",
"travelDistanceMetersFromPreviousStandstill": 54141,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T14:29:29Z",
"startServiceTime": "2027-02-01T14:29:29Z",
"departureTime": "2027-02-01T15:29:29Z",
"effectiveServiceDuration": "PT1H",
"travelTimeFromPreviousStandstill": "PT40M33S",
"travelDistanceMetersFromPreviousStandstill": 42302,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H57M18S",
"travelTimeFromStartLocationToFirstVisit": "PT48M56S",
"travelTimeBetweenVisits": "PT40M33S",
"travelTimeFromLastVisitToEndLocation": "PT27M49S",
"totalTravelDistanceMeters": 125663,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 42302,
"travelDistanceFromLastVisitToEndLocationMeters": 29220,
"endLocationArrivalTime": "2027-02-01T15:57:18Z"
}
}
]
}
]
},
"kpis": {
"totalTravelTime": "PT1H57M18S",
"travelTimeFromStartLocationToFirstVisit": "PT48M56S",
"travelTimeBetweenVisits": "PT40M33S",
"travelTimeFromLastVisitToEndLocation": "PT27M49S",
"totalTravelDistanceMeters": 125663,
"travelDistanceFromStartLocationToFirstVisitMeters": 54141,
"travelDistanceBetweenVisitsMeters": 42302,
"travelDistanceFromLastVisitToEndLocationMeters": 29220,
"totalUnassignedVisits": 1
}
}
modelOutput
contains Carl’s updated shift itinerary.
Visit E took much longer than expected, Visit B is scheduled for later in the day, and Visit D must be assigned to another technician or be rescheduled for another day.
See Real-time planning: reassignment for more details.
Next
-
Understand the constraints of the Field Service Routing model.
-
See the full API spec or try the online API.
-
Manage shift times with Time zones and daylight-saving time (DST) changes.
-
Learn about real-time planning.
-
Real-time planning with pinned visits.
-
Real-time planning and emergency visits.