Skip to main content

Service

|

The Service is the core component of the VRP (Vehicle Routing Problem) system, managing various operations such as creating, updating, retrieving, and deleting objects like customers, vehicles, orders, territories, and miscellaneous locations. It also handles routes optimization, ensuring efficient deliveries and pickups. The Service class acts as a bridge between the backend system and users by handling asynchronous operations through listeners.

This documentation provides an in-depth explanation of the Service class, how it works, and example usage.

How Service Works

The Service class provides API endpoints for interacting with different objects in the VRP system. Operations execute asynchronously, requiring a ProgressListener to track execution status and completion.

Key Functionalities

  • Customer Management: Manage customers by adding, updating, retrieving, and deleting them.
  • Order Processing: Manage orders, including creation, retrieval, updates, and prioritization.
  • Miscellaneous Locations: Handle depots and custom locations. See Miscellaneous Locations.
  • Vehicle Management: Manage vehicles with attributes like type, capacity, and availability.
  • Territory Management: Define and modify territories for better operational control.
  • Optimizations: Optimize routes considering vehicle capacities, service times, and order constraints. See Optimization.
  • Routes: Managing and optimizing routes within a Vehicle Routing Problem (VRP). See Routes.
  • Fuel Prices: Manage fuel prices for different fuel types within the VRP system. See Fuel Prices.
  • Data Cleanup: Remove outdated or unnecessary data.

Each function follows an asynchronous execution pattern, requiring a listener to track progress and completion.

Progress Listener

The ProgressListener is a crucial component for tracking the progress and completion of asynchronous operations. It provides notifications when an operation starts, progresses, or finishes.

Example

ProgressListener listener;
gem::vrp::Service service;
int res = service.clean(listener);
if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 5000);

if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << "Cleanup completed successfully." << std::endl;
else
std::cout << "Cleanup failed: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send clean request." << std::endl;

Service Operations

Creating an Object

All objects such as customers, vehicles, orders, and territories must be created before use.

Example Adding a Customer

Note

If the operation is successful, the customer will have an id assigned; which can be retrieved using the method customer.getId().

How it works

  1. Create a vrp::Customer and set the desired fields.
  2. Create a ProgressListener and vrp::Service.
  3. Call the addCustomer() method from the vrp::Service using the vrp::Customer and ProgressListener and wait for the operation to be done.
// Define customer coordinates
gem::Coordinates coords(48.853543, 2.265137);

// Define customer address
gem::AddressInfo address;
address.setField("France", gem::EAddressField::Country);
address.setField("Île-de-France", gem::EAddressField::County);
address.setField("Paris", gem::EAddressField::City);
address.setField("75016", gem::EAddressField::PostalCode);
address.setField("Rue du Dr Blanche", gem::EAddressField::StreetName);
address.setField("38", gem::EAddressField::StreetNumber);

// Create a `vrp::Customer` and set the desired fields.
gem::vrp::Customer customer;
customer.setCoordinates(coords);
customer.setAlias("Auteuil");
customer.setAddress(address);
customer.setEmail("example@example.com");
customer.setPhoneNumber("+1234567890");

// Initialize Service and Listener
ProgressListener listener;
gem::vrp::Service service;

// Call the `addCustomer()` method from the `vrp::Service` using the `vrp::Customer` and `ProgressListener` and wait for the operation to be done.
int res = service.addCustomer(&listener, customer);

if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 5000);

// Check operation success
if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << "Customer added successfully. ID: " << customer.getId() << std::endl;
else
std::cout << "Failed to add customer: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send addCustomer request." << std::endl;

Retrieving Objects

Get a Specific Request

Example Get a Vehicle by ID

How it works

  1. Create a ProgressListener, a vrp::Service and a vrp::Vehicle.
  2. Call the getVehicle() method from the vrp::Service using the vrp::Vehicle from 1.), the ID of the vehicle that you want to retrieve and the ProgressListener.
  3. Once the operation completes, the vrp::Vehicle from 1.) will be populated.
// Retrieve a specific vehicle by id
ProgressListener listener;
gem::vrp::Service service;

LargeInteger vehicleId = 0; // set your vehicle id
int res = service.getVehicle(listener, vehicle, vehicleId);
if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 5000);
if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << "Vehicle returned successfully" << std::endl;
else
std::cout << "Failed to retrive vehicle: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send getVehicle request." << std::endl;

Example Get All Orders

Returns all the Orders of the API user. SearchTerm the parameter for general search across Orders.

How it works

  1. Create a ProgressListener and vrp::Serviceand a vrp::OrderList.
  2. Call the getOrders() method from the vrp::Service and the ProgressListener.
  3. Once the operation completes, the list from 1.) will be populated with orders that match the search criteria.
// Retrieve all orders, optionally filtering by a search term
ProgressListener listener;
gem::vrp::Service service;

gem::vrp::OrderList orders;
gem::String searchTerm = "John";
int res = service.getOrders(listener, orders, searchTerm);
if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 20000);
if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << orders.size() << " orders returned successfully" << std::endl;
else
std::cout << "Failed to retrive orders: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send getOrders request." << std::endl;

Updating Objects

Example Updating an Territory

Territories can be updated with new names, types, or geographical data.

note

If the shape or the size of the territory is updated, then the list of orders that are inside the territory was also updated. You can access it using the method territory.getOrders().

How it works

  1. Create a ProgressListener and a vrp::Service.
  2. Retrieve the territory you want to update (see Get Territory) example) in a vrp::Territory.
  3. Change the desired fields of the vrp::Territory.
  4. Call the updateTerritory() method from the vrp::Service using the vrp::Territory from 2.) and the ProgressListener and wait for the operation to be done.
// Retrieve an existing territory by id
ProgressListener listener;
gem::vrp::Service service;

gem::vrp::Territory territory;
LargeInteger territoryId = 0; // Set your territory id
int res = service.getTerritory(listener, territory, territoryId);
if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 20000);

if(listener.IsFinished() && listener.GetError() == gem::KNoError)
{
// Update territory properties
territory.setName("Updated Delivery Zone");
territory.setType(gem::vrp::ETerritoryType::TT_Rectangle);
gem::DoubleListList newData;
gem::DoubleList corner1 = { 46.575722, 0.345311 };
gem::DoubleList corner2 = { 46.582438, 0.358969 };
newData.push_back(corner1);
newData.push_back(corner2);
territory.setData(newData);

// Save the updated details
res = service.updateTerritory(listener, territory);
if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 5000);
if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << "Territory updated successfully" << std::endl;
else
std::cout << "Failed to update territory: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send updateTerritory request." << std::endl;
}
else
std::cout << "Failed to retrive territory: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send getTerritory request." << std::endl;

Deleting Objects

Objects can be removed individually or in bulk.

Example Delete Route

Delete the routes from the list. Routes can be deleted individually or in bulk.

Note

The orders of a deleted route will also be deleted from the optimization to which the route belongs (excepting the orders that are also used in other routes of the same optimization, such as depots). If the route is the only route of an optimization, then it cannot be deleted, instead delete the optimization (see Delete Optimization).

How it works

  1. Create a ProgressListener and vrp::Service.
  2. Call the deleteRoute() method from the vrp::Service using the routes' IDs and ProgressListener and wait for the operation to be done.
ProgressListener listener;
gem::vrp::Service service;

// Remove multiple routes by IDs
LargeIntList routesToDelete = {101, 202, 303};
int res = service.deleteRoute(listener, routesToDelete);
if (res == gem::KNoError)
{
// Wait for completion
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 5000);

// Check operation success
if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << "Routes deleted successfully." << std::endl;
else
std::cout << "Failed to delete route: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send deleteRoute request." << std::endl;

Cleaning All Data

The clean operation deletes all the date saved for an user.

Example Cleaning All Data

ProgressListener listener;
gem::vrp::Service service;
int res = service.clean(listener);
if (res == gem::KNoError)
{
WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &listener), 5000);

if (listener.IsFinished() && listener.GetError() == gem::KNoError)
std::cout << "Cleanup completed successfully." << std::endl;
else
std::cout << "Cleanup failed: Operation timed out or server returned an error." << std::endl;
}
else
std::cout << "Failed to send clean request." << std::endl;

Advanced Operations

This section covers advanced functionalities that allow users to perform complex operations such as retrieving customer order history, merging or unlinking routes, generating territories, and retrieving territory-specific orders. Each operation is designed to optimize workflows and improve efficiency.

Retrieving Customer Order History

Purpose: This operation allows you to retrieve the order history of a specific customer, including details about the optimizations and routes associated with their orders.

Use Case:

  • Dividing a city into neighborhoods (one territory for each neighborhood) then creating an optimization using the commands in each territory.

How It Works:
The system queries the database for all orders placed by the customer and returns information about the optimizations and routes involved. This helps in understanding customer patterns and improving service delivery.

Note

Important Notes:

  • Ensure the customer ID is valid and accessible.

For a detailed step-by-step guide, refer to Retrieving Customer Order History.

Merging Routes

Purpose: Combine multiple routes into a single, unified route.

Use Case:

  • Consolidate routes for better resource utilization.
  • Simplify route management by reducing the number of active routes.

How It Works:

  • Select multiple routes to merge.
  • A new optimization is created for the merged route, inheriting configuration parameters, vehicle constraints, and other fields from the first route in the list.
  • The merged route is not automatically optimized after creation.
Note

Important Notes:

  • The merged route retains the properties of the first route in the list.
  • Orders from the merged routes are combined into the new route.

For a detailed step-by-step guide, refer to Merging Routes.

Unlinking Routes

Purpose: Remove a route from its associated optimization, making it independent.

Use Case:

  • Separate a route for individual management or re-optimization.
  • Remove a route from an optimization without deleting its orders.

How It Works:

  • The selected route is unlinked from its optimization.
  • A new optimization is created for the unlinked route, preserving its original configuration parameters, vehicle constraints, and other fields.
  • Orders from the unlinked route are removed from the original optimization.
Note

Important Notes:

  • The unlinked route becomes a standalone entity.
  • The new optimization can be modified or re-optimized as needed.

For a detailed step-by-step guide, refer to Unlink Routes.

Generating Territories

Purpose: Automatically create territories based on a set of geographic coordinates.

Use Case:

  • Divide a large area into smaller, manageable territories for efficient order management.
  • Assign orders to specific territories for better organization.

How It Works:

  • Use the generateTerritories method to create territories.
  • Provide a set of coordinates (minimum 3 coordinates per territory).
  • If the number of territories is not specified, the algorithm determines the optimal number based on the input data.
Note

Important Notes:

  • At least 3 coordinates are required to generate a single territory.
  • The number of coordinates should be at least 3 times the number of territories to be created.

For a detailed step-by-step guide, refer to this Generate Territories.

Retrieving Territory Orders

Purpose: Fetch all orders assigned to a specific territory.

Use Case:

  • View and manage orders within a specific geographic area.
  • Analyze order distribution across territories.

How It Works:

  • Use the getTerritoriesOrders function to retrieve orders.
  • Provide the territory ID to fetch all orders located within that territory.
Note

Important Notes:

  • The function returns all orders associated with the specified territory ID.
  • Ensure the territory ID is valid and corresponds to an existing territory.

For a detailed step-by-step guide, refer to this Retrieving Territory Orders.

Error Handling

| Error Code | Description | Solution | | - | - | | | KInvalidInput | Missing required fields or invalid data. | Ensure all fields are properly filled. | | KNotFound | The specified object ID does not exist. | Verify the correct ID is used. | | KInternalAbort | Server-side issue or unexpected error. | Retry the request or check system logs. |

Conclusion

The Service is a powerful tool for managing logistics operations, offering asynchronous execution and flexibility in handling various objects. Whether dealing with orders, customers, vehicles, or territories, this API ensures efficient and scalable interactions.

For more details, refer to the linked object documentation: