LinkData#

A dependency between two tasks: which end of the source task drives which end of the target task, and the working time between them. Links go in ProjectData.Links. Build one with an object initializer; Id, Source and Target are required.

Declaration#

namespace Plen;

public sealed class LinkData : IDeepCloneable<LinkData>

LinkData has no constructor that sets the required properties, so new LinkData() without Id, Source and Target doesn't compile.

Properties#

Property Type Default Description
Id Id required Link id, unique among the project's links.
Source Id required Id of the predecessor task.
Target Id required Id of the successor task.
LinkType LinkType LinkType.Fs Which end of Source drives which end of Target.
Lag double 0.0 Working time between the two ends, in LagUnit. Negative values are allowed.
LagUnit TimeUnit? null Unit of Lag. null uses Config.DurationUnit.

Id#

public required Id Id { get; set; }

The key that results use to refer to this link, for example in Note.DroppedLink, in a cycle's Links and in the critical path in Analytics. A string, int or double converts to Id implicitly.

Source, Target#

public required Id Source { get; set; }
public required Id Target { get; set; }

Ids of the two tasks the link connects. An id that names no task, or names an unscheduled task, doesn't fail validation. The link is dropped and reported as a Note.DroppedLink with the reason. See Links that get dropped for every case.

LinkType#

public LinkType LinkType { get; set; } = LinkType.Fs;

Fs (finish-to-start), Ss, Ff or Sf. See LinkType below.

Lag#

public double Lag { get; set; }

Working time added between the source end and the target end, in LagUnit. A negative value lets the target overlap the source. Must be a finite number. Lag is an absolute amount; a percentage of the predecessor's duration isn't supported.

LagUnit#

public TimeUnit? LagUnit { get; set; }

Unit for this link's Lag. null uses SchedulingConfig.DurationUnit.

Note

Without a per-link LagUnit, lag is read in Config.DurationUnit.

Methods#

DeepClone#

public LinkData DeepClone()

Returns a copy with the same property values.

ToString#

public override string ToString()

Returns a readable rendering of every property, for logging.

LinkData doesn't override Equals; two instances compare by reference.

LinkType#

The dependency type of a link: which end of the source task drives which end of the target task.

namespace Plen;

public enum LinkType { Fs, Ss, Ff, Sf }
Value Description
Fs Finish-to-start: the target starts after the source finishes. The default.
Ss Start-to-start: the target starts after the source starts.
Ff Finish-to-finish: the target finishes after the source finishes.
Sf Start-to-finish: the target finishes after the source starts.

Lag is added in every case. LinkAdd and LinkUpdate use the same enum. It has no parse or spelling helpers; JSON spellings are handled by the Plen.Wire package.

Errors#

The snapshot is validated when it's passed to Engine.Schedule or Project.Solve(data, …). A link makes the call throw InputException with Kind = InputErrorKind.Validation when:

  • its Id repeats the id of an earlier link: Path = "links[i].id"
  • its Lag is NaN or infinite: Path = "links[i].lag"

Validation.Repair repairs a repeated id instead of throwing: the later link replaces the earlier one at the earlier position, and a Note.Repaired records it.

Links added or edited after the first solve go through LinkAdd and LinkUpdate.

Example#

Task 2 starts when task 1 finishes, and task 3 starts four hours after task 2 starts, while the project counts durations in days:

using Plen;

var start = Moment.FromYmdHm(2026, 4, 6, 8, 0)
    ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData(1) { StartDate = start, Duration = 2.0 },
        new TaskData(2) { StartDate = start, Duration = 3.0 },
        new TaskData(3) { StartDate = start, Duration = 1.0 },
    ],
    Links =
    [
        new LinkData { Id = "a", Source = 1, Target = 2 },
        new LinkData
        {
            Id = "b",
            Source = 2,
            Target = 3,
            LinkType = LinkType.Ss,
            Lag = 4.0,
            LagUnit = TimeUnit.Hour,
        },
    ],
};

var result = Engine.Schedule(data, new SolveOptions());
foreach (var update in result.Updates)
{
    Console.WriteLine(update);
}