Skip to main content

Implementation of milkruns with multiple pickups per vehicle

In logistics, a milk run

is a delivery method where a single vehicle makes multiple stops along a pre-defined route to pick up or drop off goods at various locations. This approach aims to consolidate shipments, reduce transportation costs, and improve efficiency by avoiding the need for multiple individual deliveries.

Integration API provides multiple convenient features to support milkruns in various situations. Specific features that may be useful include:

  • Ability to auto select a simulation template for a simulation based on a schedule which is useful for operations that can vary from day to day, but are cyclical bookings, routes, vehicle assignments within a simulation template which is useful for static routes set or regular basis
  • Ability to adjust daily schedule due to operational reasons (such as driver is sick, vehicles aren't available etc.)
  • Ability to insert adhoc bookings into a simulation with immediate optimization to identify the best vehicle and sequence to execute that booking
  • Ability to allocate operations zone for vehicles and bookings so that vehicles assigned to a specific operations zone will be allocated to the bookings in this zone. Zones are defined as "tags"
  • Ability to notify stakeholders about changes in plans (such as sending text messages or other IMs)
  • Ability to automatically adjust and shift bookings to other time windows or to the following day
  • Ability to integrate the flow with existing OMS, CRM or ERP

Most of this functions can be fully automated thus minimizing required human effort.

Use case

A logistics company has a fleet of vehicles operating across multiple operation zones. These vehicles are executing the predefined set of routes across preassigned destinations that may very on daily basis, but repeat every week. At this destinations the vehicles are picking up goods and delivering those to the depot. The vehicles are operating in two shifts - in the morning and in the afternoon. Also the shift hours and a fleet composition varies based on a day of the week, also opening hours of pick up points are not the same each day, but are repeating each week. Occasionally, this schedule may change (new pickup locations can be added, or the old one removed, vehicle operating hours can also change long-term). Bookings are produced by clients at pickup location and are expected to be collected and delivered to the depot while meeting set SLA.

Each vehicle in the fleet is allocated to a specific zone where it is allowed to operate, so the bookings created for the pickup locations assigned to a zone should be allocated to the vehicles serving that zone. Each pickup location can belong to only one zone, likewise each vehicle can service only one operational zone.

During shifts, adhoc pick up orders may show up, and the vehicles are expected to execute those, so that the system has to choose the most appropriate and the most efficient way to achieve that. If there is no available vehicle for a booking at a given shift, the booking needs to be moved to the next shift (so from the morning shift to the afternoon shift). If it is not possible to deliver during the afternoon shift, then the booking needs to be cancelled. If the system can't accommodate bookings at any stage, a notification has to be sent to the stakeholders.

Bookings, fleet composition and pickup location with their opening hours are managed by the client in their ERP that is synchronizing this data with SWAT system through the APIs and also is expected to get proof of deliveries, ETAs and status of the bookings.

The goal of the use case is to optimize milk runs by minimizing travel distance and number of vehicles required while dynamically managing ad hoc bookings.

Setting the project up

A few number of APIs will need to be used and configured to achieve the scenario. The following guide includes steps that needs to be executed once (during project set up), rarely (changing fleet composition and pickup locations), and daily (managing notifications, adhoc bookings, proof of deliveries and ETAs). The goal of the workflow os to minimize number of requests needed daily and offload as much as possible of the configuration to the project setup.

Depending on the project, SWAT team may be setting up the project and the initial templates, however this guide will assume that all configuration and execution steps are done through the APIs.

warning

Since JSON is the data-only format and doesn't support comments, all comments in the examples below should be removed when trying them as a payload.

A general flow to configure simulation for milk runs includes the following steps:

  • Configure (or import) static pickup locations (consignee master), which change infrequently (e.g., once a month)
  • Set up multiple simulation templates that include static routes, vehicle fleets, and predefined routes (milk runs)
  • Configuring simulation templates to be applied based on the day of the week (e.g., a simulation template for Mondays and a separate template for the rest of the week).
  • Configuring vehicle constraints (groups of locations that vehicles can serve)
  • Executing initial optimization of simulations to establish optimal routes for milk runs
  • Configuring on-demand (real-time) insertions of bookings for ad hoc orders, and configuring the transfer of undelivered bookings to subsequent shifts
  • Test the flow and set up notifications

Data model representing milk runs and static data associated with templates

Some additional data models will need to be used and configured once to establish relations between opening hours, locations and operations areas. This data typically will need to imported and mainlined from the customer's ERP.

Initial Setup for Projects and Simulations

Project setup consists of two steps:

Geofences define operational areas for vehicles. For this scenario, we'll create one geofence encompassing the entire operational zone. To split operations zones between vehicles (which are not geofences in this example), we'll use operational zone groups.

Creating static locations for a milkrun (consignee list)

Operation zones represent a consignee master in a broad sense. They allow specifying predefined locations and their availability time windows (multiple time windows are supported) at a project level along with additional parameters. API Locations can be grouped into location groups, which can be used to restrict access to only certain vehicles API. Vehicle characteristics are used to apply this constraint. In addition, geofence constraint can be added to the locations group.

To create Location Operations group the following request can be used:

See JSON payload
Create a simulation template with recurrence

POST /api/v2/operationslocationgroup


{
"code": "E99",
"geofence": null,
"project": "/api/v2/project/{{project_id}}"
}

In this scenario, the company operates a milkrun with one central depot and multiple pickup points that are regularly visited. Since pickup locations rarely change, they are added to templates to save consumers of APIs the trouble of recreating them each day or other operational window. The data model representing milk runs can facilitate this. The project model can reference a list of locations (which can serve as pickup or drop-off points) with associated time windows and operational areas.

tip

Project also has public_holidays property that can have a list of public holidays for it. Time window based templates will respect this list of holidays, however this can be overridden by using a flag within the operations times windows object.

To request and view all available locations within a project.

Request all locations for a project
GET <project_id>/api/v2/operationslocation/<project_id>

Locations can be created via API and mulitple time windows can be set for a location using the request below.

See JSON payload
Create a location

POST /api/v2/operationslocation

{
"address": "Some address",
"code": "12345",
"created_at": "2024-09-05T18:44:32.593694+00:00",
"data": {},
"external_id": "12345",
"group": "/api/v2/operationslocationgroup/5", //group this location belongs to
"h3": "0040062446533530004660",
"id": 11,
"modified_at": "2024-09-05T18:44:32.593717+00:00",
"name": "Test name",
"project":"/api/v2/project/<project_id>",
"point": {
"coordinates": [
100.0000,
10.000
],
"type": "Point"
},
"time_windows": [
{
"time_window": {
"resource_uri": "/api/v2/operationstimewindow/<time_window_id>"
}
}
]
}

To create a location with multiple open time windows, you can use the following request:

See JSON payload
Create a simulation template with recurrence

POST /api/v2/operationslocationgroup


{
"address": " TAMPINES ST 111 #11-111",
"code": "A11",
"created_at": "2024-09-05T18:44:32.593694+00:00",
"data": {},
"external_id": "A111",
"group": "/api/v2/operationslocationgroup/26",
"modified_at": "2024-09-05T18:44:32.593717+00:00",
"name": "Main Location",
"project":"/api/v2/project/{{project_id}}",
"point": {
"coordinates": [
103.917644661626,
1.32892431398393
],
"type": "Point"
},
"time_windows": [
{
"time_window": {
"resource_uri": "/api/v2/operationstimewindow/1348"
}
}
]
}

In this scenario, the company has one drop-off point at a depot and several regularly visited pick-up points (i.e., milkrun operations). As pick-up locations seldom change, these locations will be added to templates to eliminate the need for the API consumer to recreate them daily or during other operational windows. The data model representing milk runs can be used for this purpose.

The project model can reference a list of locations (which can serve as pick-up or drop-off locations) with associated time windows and operational areas.

tip

Project also has public_holidays property that can have a list of public holidays for it. Time window based templates will respect this list of holidays, however this can be overridden by using a flag within the operations times windows object. Recurrence of time windows can be set in the operations time window object in iCal format. It also allows to respect or override public holiday rules configured at a project level.

See JSON payload
Create a time window with recurrence

POST /api/v2/operationstimewindow


{
"close_time_ts": "1900-01-01T13:00:00+00:00",
"name": "SOME DROP OFF - thursday Day Shift 1",
"open_time_ts": "1900-01-01T09:00:00+00:00",
"project": "/api/v2/project/{{project_id}}",
"public_holiday": false,
"recurrences": "DTSTART:20240905T184433Z\nRRULE:FREQ=DAILY;BYDAY=TH",
"strict": true
}

Creating simulation templates

Simulation template contains all data that is required to execute the milk run, such as:

  • Vehicles and vehicle constraints.
  • A predefined route in the form of predefined bookings (and nodes) is assigned to vehicles in an optimal sequence. Bookings and nodes are inferred from Operations Locations (the consignee list).
  • Operational constraints to be applied during initial optimization

To achieve this state of the simulation template, the following steps need to b e followed:

  • Create bookings (and consequently, nodes) from operations locations groups
  • Create vehicles and their constraint for the simulation
  • Execute optimizations and save assignments as a predefined milk run

To reflect automatic simulation creation based on the appropriate template for a chosen date and time (so different templates will be automatically created for each day of the week), a separate template for each period needs to be created (i.e., for Monday, Tuesday, Wednesday, etc.) with the desired settings and recurrence parameter configured.

tip

For more straightforward simulation setups, you can leverage additional APIs for duplicating objects, such as vehicles. API

The following example creates a single template, which should be repeated with appropriate settings for each day (or other time periods if more granular templating is required). Note the recurrence field, which sets a recurring template for each week on Wednesdays and Thursdays until 2024-09-29. The recurrence_priority field is used to set the priority of the template should multiple templates be available during simulation instantiation. Also, note that simulation_mode is set as template, which means that this simulation will be treated as a template. API

See JSON payload
Create a simulation template with recurrence

POST /api/v2/simulation

{
"absolute_max_trip_duration": null,
"acceptable_waiting_time": 900,
"adjust_driver_breaks_enabled": true,
"algo_optimize_quantity": "total_time",
"algo_type": "dynamic",
"allow_jump": false,
"analytics_export_completed": false,
"avg_journey_time_difference": null,
"avg_planned_actual_journey_time_difference": null,
"avg_waiting_time": null,
"avg_waiting_time_difference": null,
"bacchus_id": "2b3dfa37-594b-4a56-bffa-446c68e21a7d",
"booking_end_time": null,
"booking_start_time": null,
"bookings_count": 0,
"completed_at": null,
"completed_bookings_count": 0,
"completed_fixed_route_trips_count": null,
"completed_odbs_trips_count": null,
"conversion_rate": 100.0,
"created_at": "2024-08-02T05:05:34.027934+00:00",
"data": {
"logistics_api_settings": {
"algo_optimize_quantity": "total_time",
"algorithm": "static",
"allow_upload_after_simulation_start_time": false,
"allow_vehicle_late": false,
"average_travel_duration_to_node": 1200,
"booking_penalty": 1000,
"cvb_fleetmin_iterations_limit": 3000000,
"cvb_fleetmin_solutions_limit": 30000,
"cvb_fleetmin_time_limit": 600,
"cvb_local_search_iterations_limit": 100000,
"first_solution_strategies": [
0
],
"geofence_definition_strategy": "by_dropoff",
"geofence_vehicle_allocation_strategy": "strict",
"group_crossing_penalty": 10000.0,
"guided_local_search_lambda_coefficient": 0.1,
"lifo_order_check_on_all_vehicles": true,
"lns_time_limit_ms": 1000,
"log_search": false,
"max_dropoff_slack": null,
"max_pickup_slack": null,
"max_possible_lateness": null,
"mutually_exclusive_groups": [],
"only_pdp": false,
"optimization_step": 1,
"path_equalizer_weight": 100,
"pipeline_type": "simple_one_stage",
"savings_neighbors_ratio": 1.0,
"should_set_max_slack_start_location_zero": true,
"solution_limit": 100000000,
"stateless_api_login": null,
"stateless_api_password": null,
"stateless_api_server": null,
"strictly_exclusive_groups": [],
"time_limit_ms": 30000,
"trip_cost": 0.0,
"use_all_local_search_operators": false,
"use_cvb_local_search_operator": false,
"use_depth_first_search": false,
"use_lifo_order_check": false,
"use_local_search_metaheuristic": false,
"use_local_search_operators": [],
"use_mixed_time_matrix": false,
"use_node_weights_cost": true,
"use_path_equalizer": false,
"use_tsp_opt": false,
"use_walking_time_to_reduce_time_windows": false,
"vehicle_amortized_linear_cost_factor": null,
"vehicle_amortized_quadratic_cost_factor": null,
"vehicle_cost": 1000.0,
"vehicle_late_penalty_coefficient": 10,
"waypoints_optimization_second_phase": false,
"waypoints_solution_limit": 1000
}
},
"dataset": "/api/v2/dataset/111",
"dataset_csv_filename": null,
"dataset_name": "Default dataset for project 111",
"dead_mileage": null,
"deleted": false,
"deployment_label": null,
"description": "",
"driver_prep_time": 300,
"dropoff_transit_stops": "/api/v2/transitstopset/111",
"enable_offer_messages_generation": true,
"enable_waypoints_cache": true,
"end_time": "2021-01-01T23:59:59+00:00",
"explicit_stops": null,
"fixed_route_filter_model": null,
"fixed_route_schedule": null,
"geofence_id": "17103da2-4f92-4a82-8a93-b9b85f1af652",
"geofence_name": "Default geofence for project testing",
"geofences_zones": [],
"high_priority_stops": null,
"in_service_mileage": null,
"is_processor_based": true,
"journey_duration_source": "constant",
"max_additional_journey_time": 900,
"max_additional_journey_time_percent": 0.0,
"max_advance_booking_window": 0,
"max_journey_time_difference": null,
"max_planned_actual_journey_time_difference": null,
"max_waiting_time": null,
"max_waiting_time_difference": null,
"max_walking_distance": 0.0,
"min_advance_booking_window": 0,
"min_driver_rest_time": 360,
"min_journey_time_difference": null,
"min_planned_actual_journey_time_difference": null,
"min_waiting_time": null,
"min_waiting_time_difference": null,
"mixed_fleet": true,
"modified_at": "2024-09-12T08:29:12.287791+00:00",
"name": "Default Monday Tuesday template simulation for project",
"no_offer_count": null,
"number_of_vehicles": 0,
"odbs_trip_duration": 5400,
"odbs_trips_per_vehicle_per_hour": null,
"offer_generation_enabled": false,
"order_tracking_enabled": true,
"percentage_driver_rest_time": 8.0,
"performance_tracker_enabled": true,
"pickup_transit_stops": "/api/v2/transitstopset/673",
"pricing": null,
"processors": [],
"project": "/api/v2/project/111",
"project_name": "Testing",
"recurrence": "RRULE:FREQ=WEEKLY;UNTIL=20240929T110000Z;BYDAY=WE,TH",
"recurrence_priority": 0,
"result": {
"empty": true
},
"revenue": null,
"road_network": "van",
"routing_profile": null,
"routing_settings": {},
"simulation_mode": "template",
"start_time": "2021-01-01T00:00:00+00:00",
"state": "created",
"template": null,
"total_cost": null,
"transfer_rate": null,
"used_vehicles_count": null,
"utilization_rate": null,
"vehicle_capacity": 40,
"vehicle_ordering_in_one_by_one_stage": 1,
"walking_profile": null,
"walking_settings": {},
"write_events_log": true
}

The response will include the simulation object created.

Adding vehicles

Since the default simulation template will be used to create simulations for each day of operations, it can also include a list of vehicles that will be cloned for each simulation. In the following example, a vehicle with minimal configuration is created. For this use case, vehicles need to be assigned an operations_locations_group. API

Please note that capacities and vehicle characteristics are included in the payload. For example, for the vehicle model 4W, their capacity is defined as 2000 arbitrary weight units and 7000000 cubic cm.

tip

To enable time constraints for a vehicle (operating hours for that vehicle), please set start_time and end_time in the payload. When a vehicle is copied over from a simulation template, only the working hours (not the dates) will be copied, and the date portion of the time will be ignored. The consuming application can define its capacity needs in the form of arbitrary <string><number> pairs. When bookings are created, these same capacity names should be used for the optimizer to solve the capacitated routing problem.

See JSON payload
Create vehicles
POST /api/v2/vehicle

{
"agent_id": "a69cf9fc-62a9-4585-8f5a-c82372438160",
"capacity": {
"bts": 1,
"cbcm": 10000000,
"weight": 3000
},
"characteristics": {
"ALL": true,
"Fridge": false,
"Helper": false,
"weight": 5
},
"color": "#FFFC66",
"end_time": "2024-01-15T13:55:00+00:00",
"geofence_ids": [
2035
],
"in_use": "enabled",
"labels": [
"10"
],
"project": "/api/v2/project/<project_id>",
"routing_engine_settings": {
"batch_matrix_size": 50,
"continue_straight": true,
"curb": false,
"key": "<your key>",
"make_depot_zero": true,
"osrme_timestamp_mode": "start_time",
"road_network": "driving_thailand",
"routing_engine_name": "osrme",
"speed": null,
"time_factor": 1,
"URL": "<routing engine URL>",
"use_speed_in_routing": false,
"vehicle_model": "4W Jumbo"
},
"service_number": "4w jumbo-1",
"simulation": "/api/v2/simulation/<simulation_id>",
"start_time": "2024-01-14T22:00:00+00:00",
"vehicle_cost": 25000,
"operations_location_groups": ["/api/v2/operationslocationgroup/<operations_group_id>"]
}

warning

Given that the use case in this article involves using multiple simulation templates for different days of operations, each simulation_template must be updated with suitable vehicles. If the fleet remains unchanged between the days of operations, each simulation_template should have the same set of vehicles created. Note that updating the default simulation template or vehicles associated with that simulation template will not retroactively modify any simulation created before this change.

Adding booking templates

Simulation templates can capture bookings, nodes, and vehicle assignments, effectively representing designated routes. The distinction between the simulation template and a regular simulation is that, when a regular simulation is created from a template, it inherits all the bookings, nodes, and vehicles from the template. This enables templates to hold static routes for milk runs. To upload bookings, normal booking procedure can be applied. In this case, a booking represents a pickup at some Operations Location and a drop off at a depot. Upon creation, operation location time windows will be applied to relevant nodes for the booking. API

tip

Be aware of the capacities and vehicle characteristics provided in the payload. For example, vehicle model 4W has a capacity defined as 2,000 arbitrary weight units and 7,000,000 cubic centimeters. The consuming application can specify any capacity it requires in the form of arbitrary <string><number> pairs. When bookings are created, the optimizer will use these capacity names to solve the capacitated routing problem.

For this use case, upload should be done using

Upload bookings into the simulation
POST /api/v2/microservices/logisticsapi
See JSON payload
Upload orders (bookings) into the simulation
POST /api/v2/microservices/logisticsapi
{
"calculation_uid": "e33e7240-96ba-4c4a-b958-f903eca9b422",
"simulation_id": 1,
"bookings": [
{
"uid": "a87fdfe8-29c6-4483-8fcd-49448362473d",
"dropoff_postal_code": "2",
"dropoff_location_name": "Dropoff name",
"dropoff_location_lat": 13.8353723,
"dropoff_location_lon": 100.5732801,
"pickup_operations_location":"/api/v2/operationslocation/25",
"min_pickup_time": "2021-08-17T08:59:10.073329+00:00",
"max_pickup_time": "2021-08-17T09:14:10.073329+00:00",
"min_dropoff_time": "2021-08-17T08:59:10.073329+00:00",
"max_dropoff_time": "2021-08-17T09:59:10.073329+00:00",
"demand": {
"volume": 1
},
"data": {
"some": "thing",
"number": 1
},
"pickup_service_time": 0,
"dropoff_service_time": 0,
"groups": []
}
],
"upload_strategy": "clear_all"
}

The request will return a list of orders (bookings) IDs that have been added to the system.

Preassigning initial routes to vehicles

Once the preceding steps are complete, the simulation (which will act as a simulation template from that point forward) will contain all of the necessary data to create optimized routes for the milk run. If multiple simulations are created by applying recurrence rules, then optimization should be performed for each individual simulation. API

Optimizations should be executed through the optimize API, which will in turn generate all the necessary processing logic, including a task to run the optimization. The task's unique identifier will be returned in a response body for this request.

tip

Optimization process can take a long time depending on the settings, a number of bookings and a number of vehicles. Therefore, optimization request is asynchronous and a consumer needs to poll API and wait until the optimization run is completed.

Upload bookings into the simulation
POST /api/v2/microservices/logisticsapi_optimize
See JSON payload
Upload orders (bookings) into the simulation
POST /api/v2/microservices/logisticsapi
{
"calculation_uid": <arbitrary unique identifier for the calculation>,
"simulation_id": <simulation id created previously>,
"bookings": [],
"booking_uids": <bookings identifiers to be used for optimization from upload bookings request>
}

After the optimization run, the simulation will have generated assignments of nodes to vehicles (i.e., the routes) along with all required data elements that can now be executed in the field. In a milk run situation, however, the idea is that these pregenerated routes are executed each day (or with other recurrence) and optimization is not required for each operations slot. To achieve that, the simulation that has routes in it can be converted to become a template with applicable recurrence rules, allowing each operation to be instantiated from the template.

Upload bookings into the simulation
PATCH /api/v2/simulation/<simulation_id>
See JSON payload
Upload orders (bookings) into the simulation
PATCH /api/v2/simulation/<simulation_id>
{
"simulation_mode":"template",
"recurrence":"RRULE:FREQ=WEEKLY;UNTIL=20240929T110000Z;BYDAY=WE,TH"

}
tip

A project can contain several simulation templates, each with its recurrence rules, providing flexibility in automatically generating simulations for a specific time or day based on that rule. If multiple templates are available for the selected time or date, the recurrence_priority field will be used to select the simulation with the highest priority.iority.

Supporting ad-hoc orders

By default, the logistics application doesn't support ad hoc real-time addition of orders and doesn't support automatic re-optimization of the route. However, with specific configuration of the processing engine, it's possible to achieve automatic re-optimization of the orders. As described above, an entity called processor is used to perform optimizations for a simulation. Each simulation may have multiple processors associated with it that can potentially be used to partition the data, run optimizations with a different set of parameters, or optimize only for a specific fleet. API

If processors are attached to a simulation that is a template, the processors will also be copied to any instances of simulations created from that template, reducing the complexity of setting up the processing workflow.

Per the usecase, there are two cases when the orders will need to be processed automatically

  • An ad-hoc order arrives during operations and it needs to be allocated if possible to a vehicle
  • Unassigned orders, or orders that wre failed to pickup, should be automatically allocated to the next shift of drivers

Automatic insertion of orders

Automatic order insertion is achieved by creating a processor with a continuous type. This means that it will run continuously, awaiting new objects to change or appear based on set rules. It will also run within a given time boundary. To create such processor for a simulation (this can be done for a simulation template or a real simulation, in this case we will consider it creating for a template) execute the following request:

Live Editor
POST /api/v2/simulationprocessor
Result
Loading...
See JSON payload
Upload orders (bookings) into the simulation
POST /api/v2/simulationprocessor
// clean up existing processors first, and remove the from the template
{
"bookings_filter_expression": {
"state__in": [
// Prepared bookings are bookings that have just been uploaded, have their nodes created, and are ready for optimization
"prepared"
]
},
"calculation_parameters_logistics": "/api/v2/logisticscalculationparameters/43859",
"calculation_parameters_regenerator": {},
"fault_tolerant": false,
"geofences_zones": [],
"processor_lifecycle_type": "continuous",
"processor_type": "logistics",
// Simulation this processor should belong to
"simulation": "/api/v2/simulation/124868",
"vehicle_selectiSon_by_emptiness": false,
"vehicle_selection_in_service": false,
"vehicle_selection_in_use": true,
// Optionally, only specific vehicles can be chosen to allow for ad-hoc insertion, for example, vehicles with a specific label
"vehicles_filter_expression": {
"label__in": [
"adhoc"
]
},
//Timestamps should represent operations time bounds, for example for the first shift from 8 am to 11 am
"schedule_after": "2024-10-20T09:00:00+00:00",
"start_time": "2024-10-20T09:00:00+00:00",
"end_time": "2024-10-21T11:00:00+00:00",
"state":"new"
}
warning

Ensure there are no unused or unnecessary processors in your template and remove them. Processors start running once the simulation is instantiated, and may produce unexpected results.

In this example, the processor will only execute a simulation if there are new booking in prepared state show up in the simulation. Activity time of the processor is set with start_time and end_time field. If there is a need to apply a filter for vehicles (like for bookings), vehicle_filter_expression can be used following rules. Since this processor is created for a simulation template, it will not start. It will only start when an instance of a simulation is created from this template. All times will be adjusted accordingly to the time boundaries of that simulation instance.

Multiple processors can be generated for a simulation, one for each shift. During optimization, the processor will consider current vehicle locations and orders that have already been fulfilled in the simulation. However, the actual ability to allocate an order will depend on the processor meeting all constraints. New orders can be unassigned if there is not spare capacity available in the fleet. We recommend scheduling one continuous processor for each shift.

Automating reassignment of unassigned orders to the subsequent shift

To enable the logic of reallocating failed to deliver orders, or unassigned order. regenerator type of processor should be used. API Once executed, it will adjust all booking and nodes that are meeting its filtering criteria (so this processor needs to be configured as a single shot to be executed exactly once at a desired time).
Regenerator pipeline executes the following rules for all bookings meeting booking_filter_expression setting:

  • resets Booking.state to PREPARED
  • resets Node.state to NEW
  • resets Node.assigned_vehicle to None
  • resets Node.scheduled_ts to None
  • (optional) resets Booking.min_pickup_time to processor.calculation_parameters_regenerator['min_pickup_time']
  • (optional) resets Booking.max_pickup_time to processor.calculation_parameters_regenerator['max_pickup_time']
  • (optional) resets Booking.min_dropoff_time to processor.calculation_parameters_regenerator['min_dropoff_time']
  • (optional) resets Booking.max_dropoff_time to processor.calculation_parameters_regenerator['max_dropoff_time']
  • (optional) resets (pickup) Node.open_time_ts to processor.calculation_parameters_regenerator['min_pickup_time']
  • (optional) resets (pickup) Node.close_time_ts to processor.calculation_parameters_regenerator['max_pickup_time']
  • (optional) resets (dropoff) Node.open_time_ts to processor.calculation_parameters_regenerator['min_dropoff_time']
  • (optional) resets (dropoff) Node.close_time_ts to processor.calculation_parameters_regenerator['max_dropoff_time']
Move unassigned and failed bookings to the new shift
POST /api/v2/simulationprocessor
See JSON payload
Upload orders (bookings) into the simulation
POST /api/v2/simulationprocessor
// clean up existing processors first, and remove the from the template
{
"bookings_filter_expression": {
"state__in": [
// Driver failed to pickup and order
"fail_to_board",
// There was not enough capacity to deliver the order
"rejected_by_system"
]
},
"calculation_parameters_logistics": "/api/v2/logisticscalculationparameters/43859",
"calculation_parameters_regenerator": {
// The nodes should be scheduled with this time slot within the next shift
// This is optional, if not set, original time windows of the node will be applied
"min_pickup_time": "2024-10-20T11:00:00+00:00",
"max_pickup_time": "2024-10-20T15:00:00+00:00",
"min_dropoff_time": "2024-10-20T16:00:00+00:00",
"max_dropoff_time": "2024-10-20T16:00:00+00:00",
},

"fault_tolerant": false,
"geofences_zones": [],
// Execute the processor just once
"processor_lifecycle_type": "one_shot",
"processor_type": "regenerator",
// Simulation this processor should belong to
"simulation": "/api/v2/simulation/124868",
"vehicle_selectiSon_by_emptiness": false,
"vehicle_selection_in_service": false,
"vehicle_selection_in_use": true,
"vehicles_filter_expression": {},
// Schedule right before next shift starts
"schedule_after": "2024-10-20T10:45:00+00:00",
"state":"new"
}
warning

In this example, node time windows are modified, potentially leading to deviations from the initial delivery schedule. If a node's time windows overlap with the subsequent delivery shift or the node has multiple time windows, the calculation_parameters_regenerator is not required. In this scenario, the original node time windows are preserved.

A simulation may have mulitple processors of regenerator type for each shift.

Daily operations

Once all configurations are complete and the necessary simulation templates are created, the workflow for planning operations is similar to other use cases, such as FMCG. However, there is a crucial difference: when a simulation is established from the template, it will already have preassigned routes to the vehicles, and any bookings uploaded into the simulation will be treated as additional (ad-hoc) ones. Therefore, they will be automatically optimized along with the existing predefined routes, provided that the upload_strategy flag is set to keep_assigned API. The bookings can be uploaded at any point in time, and depending on the available fleet capacity, they can either be added to the current shift or pushed to the following one. The sequence of steps will include:

Wrapping up a day

When all shifts are scheduled and no regenerator type processors remain to be executed, the day is concluded. At the simulation's end time, all active continuous processors are terminated, signifying the day's completion.