Real-time planning: emergency visit
There are many situations where Real-time planning is necessary.
Sometimes emergency visits have to be added to an existing schedule.
Consider the following schedule. Ann has three visits scheduled, Beth has one visit scheduled, and an emergency visit needs to be added to the schedule.
The emergency visit is located close to Ann’s existing route, so Visit M is added to Ann’s schedule, and Visit E is reassigned from Ann to Beth.
1. Batch schedule: emergency visit
The 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: emergency visit example"
}
},
"modelInput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT1H30M"
},
{
"id": "Visit E",
"location": [33.90719, -84.28149],
"serviceDuration": "PT1H30M"
},
{
"id": "Visit B",
"location": [33.67590, -84.11845],
"serviceDuration": "PT1H30M"
},
{
"id": "Visit C",
"location": [33.76156, -84.27259],
"serviceDuration": "PT1H30M"
}
]
}
}
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: emergency visit example",
"submitDateTime": "2024-10-30T10:19:26.167918086Z",
"startDateTime": "2024-10-30T10:19:31.595864888Z",
"activeDateTime": "2024-10-30T10:19:31.695864888Z",
"completeDateTime": "2024-10-30T10:24:32.285382827Z",
"shutdownDateTime": "2024-10-30T10:24:32.385382827Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-157272soft",
"tags": null,
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit C",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:18:15Z",
"startServiceTime": "2027-02-01T09:18:15Z",
"departureTime": "2027-02-01T10:48:15Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT18M15S",
"travelDistanceMetersFromPreviousStandstill": 15033,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T11:30:35Z",
"startServiceTime": "2027-02-01T11:30:35Z",
"departureTime": "2027-02-01T13:00:35Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT42M20S",
"travelDistanceMetersFromPreviousStandstill": 44028,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T13:41:08Z",
"startServiceTime": "2027-02-01T13:41:08Z",
"departureTime": "2027-02-01T15:11:08Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT40M33S",
"travelDistanceMetersFromPreviousStandstill": 42302,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT2H8M57S",
"travelTimeFromStartLocationToFirstVisit": "PT18M15S",
"travelTimeBetweenVisits": "PT1H22M53S",
"travelTimeFromLastVisitToEndLocation": "PT27M49S",
"totalTravelDistanceMeters": 130583,
"travelDistanceFromStartLocationToFirstVisitMeters": 15033,
"travelDistanceBetweenVisitsMeters": 86330,
"travelDistanceFromLastVisitToEndLocationMeters": 29220,
"endLocationArrivalTime": "2027-02-01T15:38:57Z"
}
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T09:10:37Z",
"departureTime": "2027-02-01T10:40:37Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT21M15S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT0S",
"travelTimeFromLastVisitToEndLocation": "PT10M38S",
"totalTravelDistanceMeters": 17677,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 0,
"travelDistanceFromLastVisitToEndLocationMeters": 8834,
"endLocationArrivalTime": "2027-02-01T10:51:15Z"
}
}
]
}
]
},
"kpis": {
"totalTravelTime": "PT2H30M12S",
"travelTimeFromStartLocationToFirstVisit": "PT28M52S",
"travelTimeBetweenVisits": "PT1H22M53S",
"travelTimeFromLastVisitToEndLocation": "PT38M27S",
"totalTravelDistanceMeters": 148260,
"travelDistanceFromStartLocationToFirstVisitMeters": 23876,
"travelDistanceBetweenVisitsMeters": 86330,
"travelDistanceFromLastVisitToEndLocationMeters": 38054,
"totalUnassignedVisits": 0
}
}
modelOutput
contains Ann’s and Beth’s shift itineraries.
2. Real-time planning update: emergency visit
A little before midday, an emergency visit needs to be included in the plan for the same day.
Update the input dataset with the emergency visit (Visit M) included.
Because Visit M is a high priority, it includes "priority": "4"
.
The already planned visits need to be updated with the minStartTravelTime
from the most recent planning output dataset (batch or real-time) to each visit.
Visit M has not already been planned and does not include a minStartTravelTime
value:
{
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"location": [33.90719, -84.28149],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"location": [33.67590, -84.11845],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit C",
"location": [33.76156, -84.27259],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit M",
"location": [33.89104, -84.64711],
"serviceDuration": "PT1H30M",
"priority": "4"
}
]
}
Add the itinerary to Ann’s and Beth’s shifts from the previous output, including the visit IDs and the visit kind:
{
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit C",
"kind": "VISIT"
},
{
"id": "Visit A",
"kind": "VISIT"
},
{
"id": "Visit E",
"kind": "VISIT"
}
]
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT"
}
]
}
]
}
]
}
Freeze visits that have already occurred and that technicians have begun traveling to by adding freezeDeparturesBeforeTime
:
{
"modelInput": {
"freezeDeparturesBeforeTime": "2027-02-01T12:00:00Z"
}
}
The call for the emergency visit came in a little before 12:00 and the freezeDeparturesBeforeTime
is set to 12:00.
Finally, resubmit the updated input dataset:
-
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: emergency visit example"
}
},
"modelInput": {
"freezeDeparturesBeforeTime": "2027-02-01T12:00:00Z",
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit C",
"kind": "VISIT"
},
{
"id": "Visit A",
"kind": "VISIT"
},
{
"id": "Visit E",
"kind": "VISIT"
}
]
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startLocation": [33.70474, -84.06508],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT"
}
]
}
]
}
],
"visits": [
{
"id": "Visit A",
"location": [33.84475, -84.63649],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"location": [33.90719, -84.28149],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit B",
"location": [33.67590, -84.11845],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit C",
"location": [33.76156, -84.27259],
"serviceDuration": "PT1H30M",
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit M",
"location": [33.89104, -84.64711],
"serviceDuration": "PT1H30M",
"priority": "4"
}
]
}
}
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: emergency visit example",
"submitDateTime": "2024-10-31T06:12:03.588902424Z",
"startDateTime": "2024-10-31T06:12:09.384110012Z",
"activeDateTime": "2024-10-31T06:12:09.484110012Z",
"completeDateTime": "2024-10-31T06:17:09.840854161Z",
"shutdownDateTime": "2024-10-31T06:17:09.940854161Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-228042soft",
"tags": null,
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"vehicles": [
{
"id": "Ann",
"shifts": [
{
"id": "Ann-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit C",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:18:15Z",
"startServiceTime": "2027-02-01T09:18:15Z",
"departureTime": "2027-02-01T10:48:15Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT18M15S",
"travelDistanceMetersFromPreviousStandstill": 15033,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit A",
"kind": "VISIT",
"arrivalTime": "2027-02-01T11:30:35Z",
"startServiceTime": "2027-02-01T11:30:35Z",
"departureTime": "2027-02-01T13:00:35Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT42M20S",
"travelDistanceMetersFromPreviousStandstill": 44028,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit M",
"kind": "VISIT",
"arrivalTime": "2027-02-01T13:11:07Z",
"startServiceTime": "2027-02-01T13:11:07Z",
"departureTime": "2027-02-01T14:41:07Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT10M32S",
"travelDistanceMetersFromPreviousStandstill": 7592,
"minStartTravelTime": "2027-02-01T12:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT2H8M6S",
"travelTimeFromStartLocationToFirstVisit": "PT18M15S",
"travelTimeBetweenVisits": "PT52M52S",
"travelTimeFromLastVisitToEndLocation": "PT56M59S",
"totalTravelDistanceMeters": 130455,
"travelDistanceFromStartLocationToFirstVisitMeters": 15033,
"travelDistanceBetweenVisitsMeters": 51620,
"travelDistanceFromLastVisitToEndLocationMeters": 63802,
"endLocationArrivalTime": "2027-02-01T15:38:06Z"
}
}
]
},
{
"id": "Beth",
"shifts": [
{
"id": "Beth-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "Visit B",
"kind": "VISIT",
"arrivalTime": "2027-02-01T09:10:37Z",
"startServiceTime": "2027-02-01T09:10:37Z",
"departureTime": "2027-02-01T10:40:37Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT10M37S",
"travelDistanceMetersFromPreviousStandstill": 8843,
"minStartTravelTime": "2027-02-01T00:00:00Z"
},
{
"id": "Visit E",
"kind": "VISIT",
"arrivalTime": "2027-02-01T12:33:31Z",
"startServiceTime": "2027-02-01T12:33:31Z",
"departureTime": "2027-02-01T14:03:31Z",
"effectiveServiceDuration": "PT1H30M",
"travelTimeFromPreviousStandstill": "PT33M31S",
"travelDistanceMetersFromPreviousStandstill": 38703,
"minStartTravelTime": "2027-02-01T12:00:00Z"
}
],
"metrics": {
"totalTravelTime": "PT1H19M31S",
"travelTimeFromStartLocationToFirstVisit": "PT10M37S",
"travelTimeBetweenVisits": "PT33M31S",
"travelTimeFromLastVisitToEndLocation": "PT35M23S",
"totalTravelDistanceMeters": 85130,
"travelDistanceFromStartLocationToFirstVisitMeters": 8843,
"travelDistanceBetweenVisitsMeters": 38703,
"travelDistanceFromLastVisitToEndLocationMeters": 37584,
"endLocationArrivalTime": "2027-02-01T14:38:54Z"
}
}
]
}
]
},
"kpis": {
"totalTravelTime": "PT3H27M37S",
"travelTimeFromStartLocationToFirstVisit": "PT28M52S",
"travelTimeBetweenVisits": "PT1H26M23S",
"travelTimeFromLastVisitToEndLocation": "PT1H32M22S",
"totalTravelDistanceMeters": 215585,
"travelDistanceFromStartLocationToFirstVisitMeters": 23876,
"travelDistanceBetweenVisitsMeters": 90323,
"travelDistanceFromLastVisitToEndLocationMeters": 101386,
"totalUnassignedVisits": 0
}
}
modelOutput
contains Ann’s and Beth’s updated shift itineraries.
Because Ann is closer to Visit M, Visit M is assigned to Ann, and Visit E is reassigned to Ann.
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 extended visits.