ScheduleResult#

The output of one scheduling call: the tasks that changed, plus the cycles, conflicts and notes found on the way. Project#solve, Project#preview, Project#analyze and Engine.schedule return one. Project#level returns one in LevelingResult.schedule. Cycles, conflicts and non-convergence are reported here as data, not thrown.

Declaration#

package org.dhtmlx.plen;

public class ScheduleResult extends ValueRecord implements DeepCloneable<ScheduleResult> {
  public String baselineId = null;
  public Vec<Mutation> updates = new Vec<Mutation>();
  public Vec<Cycle> cycles = new Vec<Cycle>();
  public Vec<Conflict> conflicts = new Vec<Conflict>();
  public Vec<Note> notes = new Vec<Note>();
  public boolean converged = false;
  public Analytics analytics = null;
  public Vec<ResourceOverallocation> overallocations = null;
  public LoadProfile profile = null;

  public String acceptedId();
  public ScheduleResult deepClone();
  public ScheduleResult copy();
}

Each field has a fluent withX(value) setter, and each Vec field also has a withX(java.util.Collection<? extends T>) overload. ValueRecord gives the class structural equals / hashCode over all fields and a readable toString().

A ScheduleResult is read-only by contract. The project keeps the result of solve and level as its baseline, returns the same object from an unchanged re-solve, shares it with its copies, and reads it in evaluate and suggestAssignments. Call deepClone() before changing one.

Fields#

Field Type Description
baselineId String Token of the baseline this result produced or read. null when there is none.
updates Vec<Mutation> Tasks with new dates or fields. Only changed tasks unless includeUnchanged is set.
cycles Vec<Cycle> Every unique dependency cycle in the project. Empty for an acyclic project.
conflicts Vec<Conflict> Constraint violations and calendar conflicts.
notes Vec<Note> Non-fatal input notes: dropped links, tasks and assignments, and repairs.
converged boolean true when the planner reached a fixed point within the iteration limit.
analytics Analytics Critical path and slack. null unless the call's options request them.
overallocations Vec<ResourceOverallocation> Overallocated resources. null when the project has no assignments.
profile LoadProfile Resource load of the previewed dates. Set only by Project#preview with a profile query.

baselineId#

public String baselineId = null;

The opaque token of the accepted baseline:

Pass the token as baselineId in options and change sets to detect a stale baseline. Tokens are unique per process: don't store them or compare them across runs. With includeUnchanged, the extra entries in updates don't change the token. acceptedId() reads the field and throws when it's null.

updates#

public Vec<Mutation> updates = new Vec<Mutation>();

One Mutation per reported task, carrying only the fields that changed. With SolveOptions.includeUnchanged, every scheduled task is reported. updates is empty when:

  • cycles isn't empty and includeUnchanged is false, because a cyclic project keeps its dates
  • the result comes from analyze, which doesn't schedule

What the entries are relative to depends on the call; see Project#preview and Project#level.

cycles#

public Vec<Cycle> cycles = new Vec<Cycle>();

One Cycle per unique simple dependency cycle. Rotations of the same cycle are reported once. Two cycles that differ only in a parallel link are reported separately. Detection includes fixed and completed tasks and summary containment. When the list isn't empty, the call leaves all dates unchanged.

The number of cycles can grow exponentially in a densely cyclic graph.

conflicts#

public Vec<Conflict> conflicts = new Vec<Conflict>();

Constraint violations and calendar conflicts found by the planner. Always empty from analyze. The subclasses are on the Conflict page.

notes#

public Vec<Note> notes = new Vec<Note>();

Input the engine ignored or fixed instead of rejecting. The subclasses are on the Note page.

converged#

public boolean converged = false;

false when the planner stopped at SchedulingConfig.maxIterations before its dates settled. The result is still valid. Always true from analyze.

analytics#

public Analytics analytics = null;

null when neither slack nor criticalPath is set in the call's AnalyticsOptions. Otherwise an Analytics value. Check its status before reading the data.

overallocations#

public Vec<ResourceOverallocation> overallocations = null;

null when the project has no assignments. Otherwise one ResourceOverallocation per overallocated resource. The list is empty when no resource is overallocated. Windows carry a reason only in a result from level.

profile#

public LoadProfile profile = null;

Set only by Project#preview when PreviewOptions.profile is set. null otherwise. To read the load of the solved baseline, call Project#loadProfile.

Methods#

acceptedId#

public String acceptedId()

Returns baselineId. Use it where the result must carry a baseline, such as the result of solve or level.

Returns - the baseline token.

Errors - throws IllegalStateException when baselineId is null.

deepClone#

public ScheduleResult deepClone()

Returns a copy with new lists and new Mutation, Cycle, Analytics, ResourceOverallocation and LoadProfile objects. The Conflict and Note entries are shared with the original. Use it before changing a result.

copy#

public ScheduleResult copy()

Returns a shallow copy: the fields are copied, the lists and nested objects are shared with the original.

Cycle#

One dependency cycle: the tasks and links that form it.

public class Cycle extends ValueRecord implements DeepCloneable<Cycle> {
  public Vec<Id> tasks = new Vec<Id>();
  public Vec<Id> links = new Vec<Id>();

  public Cycle deepClone();
  public Cycle copy();
}
Field Type Description
tasks Vec<Id> Ids of the tasks on the cycle.
links Vec<Id> Ids of the links on the cycle.

Cycle has withTasks / withLinks setters, structural equals / hashCode, toString(), deepClone() and copy(), like ScheduleResult.

Example#

Solve a project with a dependency cycle and read the result:

import java.util.List;
import org.dhtmlx.plen.*;

public class ReadScheduleResult {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 1, 5, 8, 0);

    ProjectData data = new ProjectData()
        .withTasks(List.of(
            new TaskData(Id.str("a")).withStartDate(monday).withDuration(2.0),
            new TaskData(Id.str("b")).withStartDate(monday).withDuration(3.0)))
        .withLinks(List.of(
            new LinkData().withId(Id.str("l1")).withSource(Id.str("a")).withTarget(Id.str("b")),
            new LinkData().withId(Id.str("l2")).withSource(Id.str("b")).withTarget(Id.str("a"))));

    ScheduleResult result = new Project().solve(data, new SolveOptions());

    for (Cycle cycle : result.cycles) {
      System.out.println("cycle: tasks " + cycle.tasks + "; links " + cycle.links);
    }

    System.out.println("updates: " + result.updates.size() + ", converged: " + result.converged);

    for (Mutation update : result.updates) {
      System.out.println(update.id + ": " + update.startDate + " - " + update.endDate);
    }
  }
}