LoadProfile#

Per-bucket load and capacity of a set of resources over a date range. Project.LoadProfile returns one for the solved baseline. ScheduleResult.Profile holds one after Project.Preview with PreviewOptions.Profile set. The ProfileQuery of the call decides the range, the resources, the bucket size and which buckets list their tasks.

Declaration#

namespace Plen;

public sealed class LoadProfile : IDeepCloneable<LoadProfile>, IEquatable<LoadProfile>
{
    public Basis Basis { get; set; }
    public List<ResourceLoad> Resources { get; set; } = [];

    public LoadProfile DeepClone();
    public bool Equals(LoadProfile? other);
}

Treat a returned profile as read-only. Call DeepClone() before changing one.

Properties#

Property Type Description
Basis Basis Bucket size of every bucket in the profile: Hour or Day. It's ProfileQuery.Basis when the query set it, otherwise the basis derived from SchedulingConfig.DurationUnit.
Resources List<ResourceLoad> One entry per requested resource, in input order. All resources of the project when ProfileQuery.Resources is null.

Methods#

DeepClone#

public LoadProfile DeepClone()

Returns a copy that shares no list or nested object with this one.

Equals#

public bool Equals(LoadProfile? other)
public override bool Equals(object? obj)

Value equality: Basis is equal and Resources is equal element by element. GetHashCode is consistent with it.

ToString#

public override string ToString()

Returns a readable dump of all properties, for logging.

ResourceLoad#

The buckets of one resource.

public sealed class ResourceLoad : IDeepCloneable<ResourceLoad>, IEquatable<ResourceLoad>
{
    public required Id Resource { get; set; }
    public CapacityMode CapacityMode { get; set; }
    public List<LoadBucket> Buckets { get; set; } = [];
}
Property Type Description
Resource Id Id of the resource. Required.
CapacityMode CapacityMode Where each bucket's Capacity comes from.
Buckets List<LoadBucket> Dense over the range: one bucket for every bucket start in [From, To) of the query, in time order, including buckets with no load.

ResourceLoad has value equality (Buckets compared element by element), DeepClone() and ToString(), like LoadProfile.

CapacityMode#

Where the Capacity of a resource's buckets comes from.

public enum CapacityMode
{
    Calendar,
    Flat,
}
Value Description
Calendar The resource has a calendar, SchedulingConfig.CalendarMode is Resource and UseCalendars is true. Buckets with working time in that calendar get the resource's capacity; all other buckets get 0.
Flat Every bucket gets the resource's capacity. Applies when the resource has no calendar, CalendarMode is Task or TaskInherited, or UseCalendars is false.

The resource's capacity is ResourceData.Capacity, or SchedulingConfig.DefaultResourceCapacity when that is null. CapacityMode is a plain enum with no parse or spelling helpers. The Plen.Wire package handles the JSON spellings "calendar" and "flat".

LoadBucket#

Load and capacity of one resource in one bucket.

public sealed class LoadBucket : IDeepCloneable<LoadBucket>, IEquatable<LoadBucket>
{
    public Moment Start { get; set; }
    public double Load { get; set; }
    public double Capacity { get; set; }
    public List<Id>? Tasks { get; set; }
}
Property Type Description
Start Moment Start of the bucket. It lies in [From, To) of the query.
Load double Load booked in the bucket, in assignment Value units (1.0 = 100 %).
Capacity double Capacity of the resource in the bucket, in the same units. See CapacityMode.
Tasks List<Id>? Ids of the tasks booked in the bucket, in input order. null with ProfileTasks.None. With ProfileTasks.Over, set in buckets where Load > Capacity and empty elsewhere. With ProfileTasks.All, set in every bucket. See ProfileTasks.

The bucket is overallocated when Load > Capacity. LoadBucket has value equality (Load and Capacity compared with ==, Tasks equal element by element or both null), DeepClone() and ToString(), like LoadProfile.

Example#

Reads the day buckets of one resource and prints the overallocated ones with their tasks:

using Plen;

static Moment Day(int day) =>
    Moment.FromYmdHm(2026, 8, day, 0, 0) ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData("a") { StartDate = Day(10), Duration = 2.0 },
        new TaskData("b") { StartDate = Day(11), Duration = 2.0 },
    ],
    Resources = [new ResourceData { Id = "dev" }],
    Assignments =
    [
        new AssignmentData { Task = "a", Resource = "dev", Value = 1.0 },
        new AssignmentData { Task = "b", Resource = "dev", Value = 1.0 },
    ],
};

var project = new Project();
project.Solve(data, new SolveOptions());

LoadProfile profile = project.LoadProfile(
    new ProfileQuery(Day(10), Day(15)) { Basis = Basis.Day, Tasks = ProfileTasks.Over });

foreach (var row in profile.Resources)
{
    Console.WriteLine($"{row.Resource} ({row.CapacityMode})");
    foreach (var bucket in row.Buckets)
    {
        if (bucket.Load > bucket.Capacity)
            Console.WriteLine($"  {bucket.Start}: {bucket.Load} / {bucket.Capacity}, tasks {string.Join(", ", bucket.Tasks ?? [])}");
    }
}