PlanSolve 0.25.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package PlanSolve --version 0.25.2
                    
NuGet\Install-Package PlanSolve -Version 0.25.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PlanSolve" Version="0.25.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PlanSolve" Version="0.25.2" />
                    
Directory.Packages.props
<PackageReference Include="PlanSolve" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PlanSolve --version 0.25.2
                    
#r "nuget: PlanSolve, 0.25.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PlanSolve@0.25.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PlanSolve&version=0.25.2
                    
Install as a Cake Addin
#tool nuget:?package=PlanSolve&version=0.25.2
                    
Install as a Cake Tool

PlanSolve .NET SDK

A .NET client library for the PlanSolve optimization API. Solve complex field service routing, professional services task assignment, and shift scheduling problems with a simple, async API.

Installation

Install via NuGet:

dotnet add package PlanSolve

Or via Package Manager:

Install-Package PlanSolve

Requirements

  • .NET 10.0 or later
  • PlanSolve API key (required for authenticated requests)

API Workflow

All PlanSolve optimization APIs follow the same workflow:

  1. Construct Request - Build your optimization request with the required data
  2. Start Solver - Submit the request to start the optimization process
  3. Poll Status - Check the status until the solver completes
  4. Get Results - Retrieve the optimized solution

Quick Start

Field Service Optimization

To instantiate the client and perform a basic field service optimization:

using PlanSolve;
using PlanSolve.FieldService;

var client = new PlanSolveClient("YOUR_API_KEY");

// Create vehicles (field technicians)
var vehicles = new List<Vehicle>
{
    new()
    {
        Id = "tech1",
        Location = new[] { 40.7128, -74.0060 }, // NYC coordinates [lat, lng]
        Shifts = new List<Shift>
        {
            new()
            {
                Id = "morning-shift",
                MinStartTime = DateTime.Parse("2024-01-15T08:00:00"),
                MaxEndTime = DateTime.Parse("2024-01-15T17:00:00")
            }
        },
        Skills = new[] { "repair", "installation" }
    },
    new()
    {
        Id = "tech2",
        Location = new[] { 40.7128, -74.0060 },
        Shifts = new List<Shift>
        {
            new()
            {
                Id = "afternoon-shift",
                MinStartTime = DateTime.Parse("2024-01-15T10:00:00"),
                MaxEndTime = DateTime.Parse("2024-01-15T19:00:00")
            }
        },
        Skills = new[] { "repair" }
    }
};

// Create visits (customer appointments)
var visits = new List<Visit>
{
    new()
    {
        Id = "visit1",
        Name = "Times Square Repair",
        Location = new[] { 40.7589, -73.9851 }, // Times Square coordinates [lat, lng]
        TimeWindows = new List<TimeWindow>
        {
            new()
            {
                MinStartTime = DateTime.Parse("2024-01-15T09:00:00"),
                MaxEndTime = DateTime.Parse("2024-01-15T17:00:00")
            }
        },
        ServiceDuration = "PT30M", // ISO 8601 duration (30 minutes)
        Priority = "HIGH",
        RequiredSkills = new[] { "repair" }
    },
    new()
    {
        Id = "visit2",
        Name = "Penn Station Installation",
        Location = new[] { 40.7505, -73.9934 }, // Penn Station coordinates [lat, lng]
        TimeWindows = new List<TimeWindow>
        {
            new()
            {
                MinStartTime = DateTime.Parse("2024-01-15T10:00:00"),
                MaxEndTime = DateTime.Parse("2024-01-15T16:00:00")
            }
        },
        ServiceDuration = "PT45M", // 45 minutes
        Priority = "MEDIUM",
        RequiredSkills = new[] { "installation" }
    }
};

// Create the optimization request
var request = new FieldServiceStartRequest
{
    Vehicles = vehicles,
    Visits = visits
};

try
{
    // Option 1: Start and poll manually
    var response = await client.FieldService.StartAsync(request);
    Console.WriteLine($"Optimization started! Job ID: {response.JobId}");
    
    // Check status until completion
    SolverStatusResponse status;
    do
    {
        await Task.Delay(2000); // Wait 2 seconds
        status = await client.FieldService.GetStatusAsync(response.JobId);
        Console.WriteLine($"Status: {status.Status}");
    } while (status.Solving || status.SolverStatus == SolverStatus.SOLVING_SCHEDULED);
    
    if (status.Status == "COMPLETED")
    {
        // Get the final result
        var result = await client.FieldService.GetResultAsync(response.JobId);
        Console.WriteLine("Optimization completed!");
        
        // Print the optimized routes
        foreach (var vehicle in result.Vehicles)
        {
            Console.WriteLine($"\nRoute for {vehicle.Id}:");
            foreach (var visitId in vehicle.Visits)
            {
                var visit = result.Visits.FirstOrDefault(v => v.Id == visitId);
                if (visit != null)
                {
                    Console.WriteLine($"  - {visit.Name}: {visit.ArrivalTime} - {visit.DepartureTime}");
                }
            }
        }
    }
    
    // Option 2: Use the convenience method to wait for completion
    var result2 = await client.FieldService.StartAndWaitForCompletionAsync(request);
    Console.WriteLine("Optimization completed with result!");
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Optimization failed: {ex.Message}");
}
finally
{
    client.Dispose();
}

Professional Services Optimization

For professional services task assignment optimization:

using PlanSolve;
using PlanSolve.ProfessionalServices;

var client = new PlanSolveClient("YOUR_API_KEY");

// Create employees (consultants, developers, etc.)
var employees = new List<Employee>
{
    new()
    {
        Id = "emp1",
        Shifts = new List<Shift>
        {
            new("shift1", DateTime.Parse("2024-01-15T08:00:00"), DateTime.Parse("2024-01-15T18:00:00"))
        },
        Skills = new[] { "Java", "Spring", "Kotlin" }
    },
    new()
    {
        Id = "emp2",
        Shifts = new List<Shift>
        {
            new("shift2", DateTime.Parse("2024-01-15T09:00:00"), DateTime.Parse("2024-01-15T17:00:00"))
        },
        Skills = new[] { "Python", "SQL", "React" }
    }
};

// Create tasks (projects, assignments, etc.)
var tasks = new List<Task>
{
    new()
    {
        Id = "task1",
        Name = "Develop REST API",
        Deadline = DateTime.Parse("2024-01-20T17:00:00"),
        Duration = "PT16H", // ISO 8601 duration (16 hours)
        Priority = "HIGH",
        RequiredSkills = new[] { "Java", "Spring" }
    },
    new()
    {
        Id = "task2",
        Name = "Database Design",
        Deadline = DateTime.Parse("2024-01-22T17:00:00"),
        Duration = "PT8H", // 8 hours
        Priority = "MEDIUM",
        RequiredSkills = new[] { "SQL", "Database Design" }
    }
};

// Create the optimization request
var request = new ProfessionalServicesStartRequest
{
    Employees = employees,
    Tasks = tasks
};

try
{
    // Option 1: Start and poll manually
    var response = await client.ProfessionalServices.StartAsync(request);
    Console.WriteLine($"Optimization started! Job ID: {response.JobId}");
    
    // Poll for status
    SolverStatusResponse status;
    do
    {
        await Task.Delay(2000); // Wait 2 seconds
        status = await client.ProfessionalServices.GetStatusAsync(response.JobId);
        Console.WriteLine($"Status: {status.Status}");
    } while (status.Solving);
    
    if (status.Status == "COMPLETED")
    {
        // Get the final result
        var result = await client.ProfessionalServices.GetResultAsync(response.JobId);
        Console.WriteLine("Optimization completed!");
        
        // Print the optimized task assignments
        foreach (var employee in result.Employees)
        {
            Console.WriteLine($"\nEmployee {employee.Id} assigned tasks:");
            foreach (var taskId in employee.Tasks)
            {
                var task = result.Tasks.FirstOrDefault(t => t.Id == taskId);
                if (task != null)
                {
                    Console.WriteLine($"  - {task.Name}: {task.StartTime} - {task.EndTime}");
                }
            }
        }
    }
    
    // Option 2: Use the convenience method to wait for completion
    var result2 = await client.ProfessionalServices.StartAndWaitForCompletionAsync(request);
    Console.WriteLine("Optimization completed with result!");
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Optimization failed: {ex.Message}");
}
finally
{
    client.Dispose();
}

Shift Assignment Optimization

For shift scheduling and assignment:

using PlanSolve;
using PlanSolve.Shifts;

var client = new PlanSolveClient("YOUR_API_KEY");

// Create employees
var employees = new List<Employee>
{
    new()
    {
        Id = "emp1",
        Shifts = new List<Shift>
        {
            new("shift1", DateTime.Parse("2024-01-15T08:00:00"), DateTime.Parse("2024-01-15T18:00:00"))
        },
        Skills = new[] { "cashier", "inventory" }
    },
    new()
    {
        Id = "emp2",
        Shifts = new List<Shift>
        {
            new("shift2", DateTime.Parse("2024-01-15T09:00:00"), DateTime.Parse("2024-01-15T17:00:00"))
        },
        Skills = new[] { "manager", "cashier" }
    }
};

// Create tasks (shift assignments)
var tasks = new List<Task>
{
    new()
    {
        Id = "task1",
        Name = "Morning Cashier Shift",
        Deadline = DateTime.Parse("2024-01-15T12:00:00"),
        Duration = "PT4H", // 4 hours
        Priority = "HIGH",
        RequiredSkills = new[] { "cashier" }
    },
    new()
    {
        Id = "task2",
        Name = "Inventory Management",
        Deadline = DateTime.Parse("2024-01-15T16:00:00"),
        Duration = "PT2H", // 2 hours
        Priority = "MEDIUM",
        RequiredSkills = new[] { "inventory" }
    }
};

// Create the optimization request
var request = new ShiftStartRequest
{
    Employees = employees,
    Tasks = tasks
};

try
{
    // Option 1: Start and poll manually
    var response = await client.Shift.StartAsync(request);
    Console.WriteLine($"Optimization started! Job ID: {response.JobId}");
    
    // Poll for status
    SolverStatusResponse status;
    do
    {
        await Task.Delay(2000);
        status = await client.Shift.GetStatusAsync(response.JobId);
        Console.WriteLine($"Status: {status.Status}");
    } while (status.Solving);
    
    if (status.Status == "COMPLETED")
    {
        var result = await client.Shift.GetResultAsync(response.JobId);
        Console.WriteLine("Optimization completed!");
    }
    
    // Option 2: Use the convenience method to wait for completion
    var result2 = await client.Shift.StartAndWaitForCompletionAsync(request);
    Console.WriteLine("Optimization completed with result!");
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Optimization failed: {ex.Message}");
}
finally
{
    client.Dispose();
}

API Reference

PlanSolveClient

The main client class for interacting with the PlanSolve API.

Constructor
new PlanSolveClient(string apiKey)
new PlanSolveClient(string apiKey, HttpClient httpClient)
  • apiKey (required): Your PlanSolve API key for authenticated requests
  • httpClient (optional): Custom HttpClient instance for dependency injection
Properties
  • FieldService: Access to field service optimization methods
  • ProfessionalServices: Access to professional services optimization methods
  • Shift: Access to shift assignment optimization methods
Disposal

The client implements IDisposable and should be disposed when no longer needed, especially if you're not providing your own HttpClient.

FieldServiceApiClient

Handles field service optimization requests and results.

Methods
  • StartAsync(FieldServiceStartRequest request): Start a new field service optimization
  • GetResultAsync(string jobId): Get the completed optimization result
  • GetStatusAsync(string jobId): Get the status of a running optimization job
  • StartAndWaitForCompletionAsync(FieldServiceStartRequest request, int pollIntervalMs = 5000, int maxAttempts = 10, CancellationToken cancellationToken = default): Start optimization and wait for completion with polling
  • WaitForCompletionAsync(string jobId, int pollIntervalMs = 5000, int maxAttempts = 10, CancellationToken cancellationToken = default): Wait for an existing job to complete

ProfessionalServicesApiClient

Handles professional services task assignment optimization requests and results.

Methods
  • StartAsync(ProfessionalServicesStartRequest request): Start a new professional services optimization
  • GetResultAsync(string jobId): Get the completed optimization result
  • GetStatusAsync(string jobId): Get the status of a running optimization job
  • StartAndWaitForCompletionAsync(ProfessionalServicesStartRequest request, int pollIntervalMs = 5000, int maxAttempts = 1000, CancellationToken cancellationToken = default): Start optimization and wait for completion with polling
  • WaitForCompletionAsync(string jobId, int pollIntervalMs = 5000, int maxAttempts = 1000, CancellationToken cancellationToken = default): Wait for an existing job to complete

ShiftApiClient

Handles shift assignment optimization requests and results.

Methods
  • StartAsync(ShiftStartRequest request): Start a new shift assignment optimization
  • GetResultAsync(string jobId): Get the completed optimization result
  • GetStatusAsync(string jobId): Get the status of a running optimization job
  • StartAndWaitForCompletionAsync(ShiftStartRequest request, int pollIntervalMs = 5000, int maxAttempts = 1000, CancellationToken cancellationToken = default): Start optimization and wait for completion with polling
  • WaitForCompletionAsync(string jobId, int pollIntervalMs = 5000, int maxAttempts = 1000, CancellationToken cancellationToken = default): Wait for an existing job to complete

Data Models

Field Service Models

Vehicle
public class Vehicle
{
    public string Id { get; set; }
    public double[] Location { get; set; } // [latitude, longitude]
    public List<Shift> Shifts { get; set; }
    public string[] Skills { get; set; }
}
Shift
public class Shift
{
    public string Id { get; set; }
    public DateTime MinStartTime { get; set; }
    public DateTime MaxEndTime { get; set; }
}
Visit
public class Visit
{
    public string Id { get; set; }
    public string Name { get; set; }
    public double[] Location { get; set; } // [latitude, longitude]
    public List<TimeWindow> TimeWindows { get; set; }
    public string ServiceDuration { get; set; } // ISO 8601 duration string
    public string Priority { get; set; } // "HIGH", "MEDIUM", or "LOW"
    public string[] RequiredSkills { get; set; }
}
TimeWindow
public class TimeWindow
{
    public DateTime MinStartTime { get; set; }
    public DateTime MaxEndTime { get; set; }
}

Professional Services Models

Employee
public class Employee
{
    public string Id { get; set; }
    public List<Shift> Shifts { get; set; }
    public string[] Skills { get; set; }
}
Task
public class Task
{
    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime? Deadline { get; set; }
    public string Duration { get; set; } // ISO 8601 duration string
    public string Priority { get; set; }
    public string[] RequiredSkills { get; set; }
}

Shift Models

Employee
public class Employee
{
    public string Id { get; set; }
    public List<Shift> Shifts { get; set; }
    public string[] Skills { get; set; }
}
Task
public class Task
{
    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime? Deadline { get; set; }
    public string Duration { get; set; } // ISO 8601 duration string
    public string Priority { get; set; }
    public string[] RequiredSkills { get; set; }
}

Advanced Usage

Using Dependency Injection

// Register in your DI container
services.AddHttpClient<PlanSolveClient>(client =>
{
    client.BaseAddress = new Uri("https://plansolve.app/");
    client.DefaultRequestHeaders.Add("X-API-KEY", "YOUR_API_KEY");
});

// Or use with your own HttpClient
var httpClient = new HttpClient();
var client = new PlanSolveClient("YOUR_API_KEY", httpClient);

Error Handling

try
{
    var response = await client.FieldService.StartAsync(request);
    // Handle success
}
catch (HttpRequestException ex) when (ex.Message.Contains("401"))
{
    Console.Error.WriteLine("Invalid API key");
}
catch (HttpRequestException ex) when (ex.Message.Contains("400"))
{
    Console.Error.WriteLine("Invalid request data");
}
catch (HttpRequestException ex) when (ex.Message.Contains("429"))
{
    Console.Error.WriteLine("Rate limit exceeded");
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Unexpected error: {ex.Message}");
}

Cancellation Support

All async methods support CancellationToken:

var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); // 5 minute timeout

try
{
    var result = await client.FieldService.StartAndWaitForCompletionAsync(
        request, 
        cancellationToken: cts.Token
    );
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled");
}

Resilience

The SDK uses Polly for resilience patterns including retry logic and circuit breakers. Failed requests are automatically retried with exponential backoff.

Examples

See the Tests/ directory for additional usage examples and integration tests.

Support

For API support and questions, please refer to the main PlanSolve documentation or contact support.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.27.0 94 7/23/2026
0.26.0 86 7/22/2026
0.25.8 96 7/22/2026
0.25.7 88 7/21/2026
0.25.6 89 7/21/2026
0.25.5 91 7/21/2026
0.25.4 94 7/21/2026
0.25.3 85 7/21/2026
0.25.2 88 7/21/2026
0.25.1 83 7/21/2026
0.25.0 90 7/21/2026