SkillLayer.slots#
Turns a Pool of workers and a list of Requirements into the Slots that Project#suggestAssignments takes. Existing active assignments are bound to the slots, so repeated runs stay stable after a commit. Needs a solved project with no unsolved edits.
Signature#
public static Vec
Synchronous and CPU-only. Reads the project's accepted state through Project#roster and never modifies the project. Not synchronized: don't call it while another thread uses the same Project.
Parameters#
| Name | Type | Description |
|---|---|---|
project |
Project |
A solved project with no unsolved edits. Tasks, resources and assignments are checked against its accepted state. Not null |
pool |
Pool |
Workers that may be assigned. Pool order sets the order of each slot's allowed. Not null |
requirements |
Vec<Requirement> |
What each task needs. An empty list returns an empty Vec. Not null |
Returns#
A new Vec<Slot>: for each requirement, count slots, in requirement order, then instance order. Slot fields:
| Field | Value |
|---|---|
id |
Id.str("<requirement id>#<n>"), n = 1..count (for example be#1, be#2), built by slotId |
task |
The requirement's task |
allowed |
Pool workers for which every SkillMatch of the requirement holds, in pool order. No matches allows every worker |
value |
The requirement's units |
assignment |
The existing active assignment bound to this slot, or null |
On the next call, an assignment created from a slot binds back to that slot by id. Keep each requirement id on the same task; see Requirement.
suggestAssignments takes the result as is.
Binding#
The task's existing active assignments are bound per task, across all requirements on that task, in three steps:
- Slot id. An assignment whose id is one of the requirement's slot ids binds to that slot, even when its resource isn't in
allowed. The slot is then broken (AssignTarget.BROKEN). This step runs for every requirement of the task before any explicit list. - Explicit list.
Requirement.assignmentsnon-nullreplaces automatic binding for that requirement. A listed slot-id assignment keeps its slot, the other entries take the remaining slots in order, and leftover slots stay empty. An empty list leaves every slot empty. - Matching. A deterministic maximum bipartite matching binds the remaining assignments to the empty slots of requirements whose
assignmentsisnull. An assignment can take a slot only when its resource is in the slot'sallowed. Input order decides between equal matchings, and an earlier requirement can't take the only worker a later one fits.
Assignments left unbound stay on the task without a slot.
Errors#
All exceptions are unchecked. The first failing check wins: the project state, then the pool, then each requirement's fields in input order, then the slot limit, then binding per task.
| Error | When |
|---|---|
MatchException.Project |
The project is unsolved or has unsolved edits, for example after apply. code() returns ProjectError.NO_BASELINE; error holds the ProjectException |
MatchException.Invalid |
The pool or a requirement is invalid. path names the item, detail the reason |
InputException (VALIDATION, path null) |
The summed count of all requirements reaches Integer.MAX_VALUE |
OutOfMemoryError |
The summed count is below that limit but too large to allocate |
NullPointerException |
project, pool or requirements is null |
MatchException.Invalid paths:
| Path | When |
|---|---|
pool.workers[i].resource |
Unknown resource, or the same resource listed as two workers |
pool.workers[i].skills[j].id |
The same skill listed twice on one worker |
pool.workers[i].skills[j].value |
Not finite |
requirements[i].id |
Duplicate requirement id, or one of its slot ids is an active assignment on another task |
requirements[i].task |
Unknown or unscheduled task |
requirements[i].count |
0 |
requirements[i].units |
Not finite or <= 0 |
requirements[i].matches[j].value |
Not finite |
requirements[i].assignments |
More entries than count, or an assignment with one of this requirement's slot ids left out of the list |
requirements[i].assignments[k] |
Unknown or inactive assignment, an assignment on another task, or one bound twice (the detail names the other requirement when it already holds the assignment) |
There's no STALE_BASELINE: the call takes no baseline token.
MatchException isn't a PlenException; toPlenException() converts it to the matching InputException or ProjectException. Its simple name clashes with java.lang.MatchException, so code that catches it needs import org.dhtmlx.plen.MatchException; next to import org.dhtmlx.plen.*;.
Collection overload#
public static Vec
Copies requirements into a Vec and calls the main form, so List.of(...) works. Same behavior and errors.
SkillLayer.slotId#
public static String
Returns the id of a requirement's n-th slot: the requirement id's key(), #, then n (slotId(Id.str("be"), 2) is "be#2"). slots uses it for every slot id, and an assignment created from a slot carries the same id. n isn't checked.
SkillLayer.validatePool#
public static void
Runs the pool checks of slots alone against a Roster from Project#roster. Returns normally when the pool is valid.
Throws MatchException.Invalid with the pool.workers[...] paths listed under Errors, and NullPointerException when roster or pool is null.
Example#
;
;
For the full workflow, see Skills and requirements.
Related#
Pool- the workers that may be assignedRequirement- what each task needsMatchException- the error it throwsProject#suggestAssignments- fills the slots it builds- Skills and requirements - pools, skills, requirements and matches