ResourceOverallocation#
The periods in which one resource is booked above its capacity. It isn't built by hand: read it from ScheduleResult.Overallocations, which holds one entry per resource with at least one overallocated window. Results from Project.Level also say why leveling left each window in place.
Declaration#
namespace Plen;
public sealed class ResourceOverallocation
: IDeepCloneable<ResourceOverallocation>, IEquatable<ResourceOverallocation>
{
public required Id Resource { get; set; }
public List<OverallocationWindow> Windows { get; set; } = [];
public ResourceOverallocation DeepClone();
public bool Equals(ResourceOverallocation? other);
}
Equality is by value: Resource and Windows element by element. ToString() lists the fields, for debugging.
Properties#
| Property | Type | Description |
|---|---|---|
Resource |
Id |
The overallocated resource. |
Windows |
List<OverallocationWindow> |
Its overallocated windows. Never empty in a reported entry. |
Methods#
DeepClone#
public ResourceOverallocation DeepClone();
Returns a copy that shares no list or window with the original. Results are read-only by contract; copy one before changing it.
Returns: a new ResourceOverallocation equal to this one.
OverallocationWindow#
A maximal run of consecutive buckets in which the resource's load exceeds its capacity. Bucket size is the result's basis: derived from SchedulingConfig.DurationUnit, or LevelingOptions.Basis for Level.
namespace Plen;
public sealed class OverallocationWindow
: IDeepCloneable<OverallocationWindow>, IEquatable<OverallocationWindow>
{
public Moment From { get; set; }
public Moment To { get; set; }
public double Capacity { get; set; }
public double Peak { get; set; }
public double Overload { get; set; }
public List<Id> Tasks { get; set; } = [];
public string? Reason { get; set; }
public OverallocationWindow DeepClone();
public bool Equals(OverallocationWindow? other);
}
| Property | Type | Description |
|---|---|---|
From |
Moment |
Window start: the earliest working moment of the contending tasks in the first bucket. |
To |
Moment |
Window end: the latest working moment of the contending tasks in the last bucket. Both edges are clipped to the booked assignment slices, not to the whole task. |
Capacity |
double |
The resource's capacity, in assignment Value units (1 = 100 %). See ResourceData.Capacity. |
Peak |
double |
The highest bucket load in the window. |
Overload |
double |
Σ (load − capacity) over the window's buckets, in capacity × buckets. Per resource, the windows' Overload adds up to the overload metric at the same basis. |
Tasks |
List<Id> |
The contending tasks, in input order. |
Reason |
string? |
Why leveling left the window. Set only in results from Level; null otherwise. One of pinned-constraint, fixed-task, completed, priority-1000, slack-exhausted, out-of-scope. When several apply, the first in this list wins. |
DeepClone() returns a copy with its own Tasks list. Equality is by value, with Tasks compared element by element.
The reasons are explained in Leveling resources.
Example#
Two fixed tasks overlap on dev1 on Tuesday. Level can't move either, so the window stays and carries a reason:
using Plen;
static Moment Aug(int day) =>
Moment.FromYmdHm(2026, 8, day, 8, 0) ?? throw new InvalidOperationException("invalid date");
static AssignmentData Booking(string task) =>
new() { Task = task, Resource = "dev1", Value = 1.0 };
var data = new ProjectData
{
Tasks =
[
new TaskData("A") { StartDate = Aug(10), Duration = 2.0, AutoScheduling = false },
new TaskData("B") { StartDate = Aug(11), Duration = 2.0, AutoScheduling = false },
],
Resources = [new ResourceData { Id = "dev1" }],
Assignments = [Booking("A"), Booking("B")],
};
var project = new Project();
project.Solve(data, new SolveOptions());
var leveled = project.Level(new LevelingOptions());
foreach (var over in leveled.Schedule.Overallocations ?? [])
{
foreach (var window in over.Windows)
{
// dev1: 2026-08-11 08:00 .. 2026-08-11 17:00, peak 2 of 1, overload 1, tasks [A, B], reason fixed-task
Console.WriteLine(
$"{over.Resource}: {window.From} .. {window.To}, peak {window.Peak} of {window.Capacity}, " +
$"overload {window.Overload}, tasks [{string.Join(", ", window.Tasks)}], reason {window.Reason}");
}
}
The same window from Solve has Reason set to null.
Related#
ScheduleResult-Overallocationsholds these entriesProject.Level- leveling that resolves windows and reports why some remainOverload- the overload metric the windows add up toLoadProfile- per-bucket load and capacity of resources- Leveling resources - the reasons a window stays