docs: bilingual comments for apsis-task

This commit is contained in:
ApsisRS contributors 2026-10-02 01:01:36 +08:00
parent 493fb2cf23
commit b370dea368
3 changed files with 241 additions and 4 deletions

View File

@ -3,8 +3,8 @@
use core::fmt; use core::fmt;
/// The category of a task error. /// The category of a task error.
/// ///
/// 任务错误的类别。 /// 任务错误的类别。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum TaskErrorKind { pub enum TaskErrorKind {
@ -64,8 +64,8 @@ impl fmt::Display for TaskErrorKind {
} }
/// A failure while building or querying a task. /// A failure while building or querying a task.
/// ///
/// 构建或查询任务时发生的失败。 /// 构建或查询任务时发生的失败。
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TaskError { pub struct TaskError {
kind: TaskErrorKind, kind: TaskErrorKind,

View File

@ -24,16 +24,27 @@ use std::collections::BTreeMap;
/// An edge from one task to another means the first must finish before the /// An edge from one task to another means the first must finish before the
/// second starts. The order of the tasks themselves is the caller's: the /// second starts. The order of the tasks themselves is the caller's: the
/// graph only records which pairs are related. /// graph only records which pairs are related.
///
/// 优先约束构成的有向无环图。
///
/// 从一个任务指向另一个任务的边表示前者必须先于后者完成。任务本身的次序
/// 由调用方决定:该图只记录哪些任务对之间存在关系。
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
pub struct PrecedenceGraph { pub struct PrecedenceGraph {
/// The successors of each task, in the order the edges were added. /// The successors of each task, in the order the edges were added.
///
/// 各任务的后继,按边添加顺序排列。
successors: BTreeMap<usize, Vec<usize>>, successors: BTreeMap<usize, Vec<usize>>,
/// The predecessors of each task, in the order the edges were added. /// The predecessors of each task, in the order the edges were added.
///
/// 各任务的前驱,按边添加顺序排列。
predecessors: BTreeMap<usize, Vec<usize>>, predecessors: BTreeMap<usize, Vec<usize>>,
} }
impl PrecedenceGraph { impl PrecedenceGraph {
/// Creates an empty graph. /// Creates an empty graph.
///
/// 创建一个空图。
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
successors: BTreeMap::new(), successors: BTreeMap::new(),
@ -42,16 +53,22 @@ impl PrecedenceGraph {
} }
/// Returns the number of edges. /// Returns the number of edges.
///
/// 返回边的数量。
pub fn edge_count(&self) -> usize { pub fn edge_count(&self) -> usize {
self.successors.values().map(Vec::len).sum() self.successors.values().map(Vec::len).sum()
} }
/// Returns `true` when there are no edges. /// Returns `true` when there are no edges.
///
/// 当没有边时返回 `true`。
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.successors.values().all(Vec::is_empty) self.successors.values().all(Vec::is_empty)
} }
/// Returns `true` when the constraint from `before` to `after` exists. /// Returns `true` when the constraint from `before` to `after` exists.
///
/// 当存在从 `before` 到 `after` 的约束时返回 `true`。
pub fn has_constraint(&self, before: TaskId, after: TaskId) -> bool { pub fn has_constraint(&self, before: TaskId, after: TaskId) -> bool {
self.successors self.successors
.get(&before.index()) .get(&before.index())
@ -66,6 +83,14 @@ impl PrecedenceGraph {
/// Adding a constraint that already exists is accepted and has no effect, /// Adding a constraint that already exists is accepted and has no effect,
/// so a caller may assemble a graph from overlapping sources without /// so a caller may assemble a graph from overlapping sources without
/// having to deduplicate. /// having to deduplicate.
///
/// 添加一条约束:`before` 必须在 `after` 开始之前完成。
///
/// 两个标识符相同时返回错误,因为任务不能先于自身;该约束会闭成环时
/// 亦返回错误。
///
/// 添加已存在的约束被接受且不起作用,故调用方可以从多个有重叠的数据源
/// 汇集一个图,无须先去重。
pub fn add(&mut self, before: TaskId, after: TaskId) -> Result<(), TaskError> { pub fn add(&mut self, before: TaskId, after: TaskId) -> Result<(), TaskError> {
if before == after { if before == after {
return Err(TaskError::new(TaskErrorKind::PrecedenceCycle) return Err(TaskError::new(TaskErrorKind::PrecedenceCycle)
@ -78,6 +103,8 @@ impl PrecedenceGraph {
// `before` is already reachable from `after`. That is checked before // `before` is already reachable from `after`. That is checked before
// the edge is inserted, so a rejected edge leaves the graph // the edge is inserted, so a rejected edge leaves the graph
// unchanged. // unchanged.
// 从 `before` 到 `after` 的新边恰在 `before` 已可自 `after` 到达时
// 闭成环。该检查先于插入,故被拒的边不会改变图。
if self.is_reachable(after, before) { if self.is_reachable(after, before) {
return Err( return Err(
TaskError::new(TaskErrorKind::PrecedenceCycle).with_detail(format!( TaskError::new(TaskErrorKind::PrecedenceCycle).with_detail(format!(
@ -98,6 +125,8 @@ impl PrecedenceGraph {
/// Returns `true` when `to` is reachable from `from` following the /// Returns `true` when `to` is reachable from `from` following the
/// constraints. /// constraints.
///
/// 当沿约束可自 `from` 到达 `to` 时返回 `true`。
pub fn is_reachable(&self, from: TaskId, to: TaskId) -> bool { pub fn is_reachable(&self, from: TaskId, to: TaskId) -> bool {
if from == to { if from == to {
return true; return true;
@ -123,6 +152,10 @@ impl PrecedenceGraph {
/// ///
/// The result is in the order the constraints were added. It is the /// The result is in the order the constraints were added. It is the
/// direct predecessors only, not the transitive closure. /// direct predecessors only, not the transitive closure.
///
/// 返回必须在某任务开始前完成的任务。
///
/// 结果按约束添加顺序排列。它只含直接前驱,不含传递闭包。
pub fn predecessors_of(&self, task: TaskId) -> Vec<TaskId> { pub fn predecessors_of(&self, task: TaskId) -> Vec<TaskId> {
self.predecessors self.predecessors
.get(&task.index()) .get(&task.index())
@ -134,6 +167,10 @@ impl PrecedenceGraph {
/// ///
/// The result is in the order the constraints were added. It is the /// The result is in the order the constraints were added. It is the
/// direct successors only, not the transitive closure. /// direct successors only, not the transitive closure.
///
/// 返回必须等待某任务完成的任务。
///
/// 结果按约束添加顺序排列。它只含直接后继,不含传递闭包。
pub fn successors_of(&self, task: TaskId) -> Vec<TaskId> { pub fn successors_of(&self, task: TaskId) -> Vec<TaskId> {
self.successors self.successors
.get(&task.index()) .get(&task.index())
@ -142,9 +179,12 @@ impl PrecedenceGraph {
} }
/// Returns the tasks with no predecessor. /// Returns the tasks with no predecessor.
///
/// 返回没有前驱的任务。
pub fn roots(&self) -> Vec<TaskId> { pub fn roots(&self) -> Vec<TaskId> {
// Every task that appears as a successor has a predecessor, so the // Every task that appears as a successor has a predecessor, so the
// roots are the tasks that appear only as a predecessor. // roots are the tasks that appear only as a predecessor.
// 凡出现为后继的任务都有前驱,故根是那些只作为前驱出现的任务。
let mut roots: Vec<usize> = Vec::new(); let mut roots: Vec<usize> = Vec::new();
for task in self.successors.keys() { for task in self.successors.keys() {
if !self.predecessors.contains_key(task) && !roots.contains(task) { if !self.predecessors.contains_key(task) && !roots.contains(task) {
@ -156,6 +196,8 @@ impl PrecedenceGraph {
} }
/// Returns the tasks with no successor. /// Returns the tasks with no successor.
///
/// 返回没有后继的任务。
pub fn leaves(&self) -> Vec<TaskId> { pub fn leaves(&self) -> Vec<TaskId> {
let mut leaves: Vec<usize> = Vec::new(); let mut leaves: Vec<usize> = Vec::new();
for task in self.predecessors.keys() { for task in self.predecessors.keys() {
@ -182,8 +224,20 @@ impl PrecedenceGraph {
/// tasks. The graph rejects cycles as they are added, so this can only /// tasks. The graph rejects cycles as they are added, so this can only
/// happen when a caller builds the graph from one task set and orders /// happen when a caller builds the graph from one task set and orders
/// another, or when the tasks are not all in the graph. /// another, or when the tasks are not all in the graph.
///
/// 返回所给任务的拓扑序。
///
/// 该次序满足每一条约束:任务出现在其所有前驱之后。平局按该任务在所给
/// 列表中的位置打破,使调用方可在本来无序的任务间表达自己的偏好。这
/// 正是让规划器在约束允许的自由度内先放置更有价值的任务。
///
/// 列表中无任何约束的任务同样出现。
///
/// 当图在所给任务之间含环时返回错误。该图在添加时就拒绝成环,故这只
/// 可能发生在调用方以一组任务建图却对另一组排序,或任务未全在图中时。
pub fn topological_order(&self, tasks: &[TaskId]) -> Result<Vec<TaskId>, TaskError> { pub fn topological_order(&self, tasks: &[TaskId]) -> Result<Vec<TaskId>, TaskError> {
// The position of each task in the supplied list, which breaks ties. // The position of each task in the supplied list, which breaks ties.
// 各任务在所给列表中的位置,用于打破平局。
let mut preference: BTreeMap<usize, usize> = BTreeMap::new(); let mut preference: BTreeMap<usize, usize> = BTreeMap::new();
for (position, task) in tasks.iter().enumerate() { for (position, task) in tasks.iter().enumerate() {
preference.insert(task.index(), position); preference.insert(task.index(), position);
@ -192,6 +246,8 @@ impl PrecedenceGraph {
// The number of predecessors of each task that are themselves in the // The number of predecessors of each task that are themselves in the
// list. A predecessor outside the list is ignored, because it cannot // list. A predecessor outside the list is ignored, because it cannot
// be scheduled and so cannot be waited for. // be scheduled and so cannot be waited for.
// 各任务在列表内的前驱个数。列表外的前驱被忽略,因为它无法被
// 调度,也就无从等待。
let mut pending: BTreeMap<usize, usize> = BTreeMap::new(); let mut pending: BTreeMap<usize, usize> = BTreeMap::new();
for task in tasks { for task in tasks {
let count = self let count = self
@ -208,6 +264,7 @@ impl PrecedenceGraph {
while order.len() < tasks.len() { while order.len() < tasks.len() {
// Among the tasks whose predecessors are all placed, take the // Among the tasks whose predecessors are all placed, take the
// one the caller prefers. // one the caller prefers.
// 在前驱均已放置的任务中,取调用方偏好者。
let next = tasks let next = tasks
.iter() .iter()
.map(|task| task.index()) .map(|task| task.index())
@ -233,6 +290,8 @@ impl PrecedenceGraph {
} }
/// Returns the number of tasks in the graph. /// Returns the number of tasks in the graph.
///
/// 返回图中的任务数。
pub fn task_count(&self) -> usize { pub fn task_count(&self) -> usize {
let mut all: Vec<usize> = self.successors.keys().copied().collect(); let mut all: Vec<usize> = self.successors.keys().copied().collect();
for task in self.predecessors.keys() { for task in self.predecessors.keys() {

View File

@ -4,16 +4,22 @@ use crate::error::{TaskError, TaskErrorKind};
use apsis_resource::ResourceId; use apsis_resource::ResourceId;
/// An identifier for a task. /// An identifier for a task.
///
/// 任务的标识符。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TaskId(pub usize); pub struct TaskId(pub usize);
impl TaskId { impl TaskId {
/// Creates an identifier. /// Creates an identifier.
///
/// 创建一个标识符。
pub const fn new(index: usize) -> Self { pub const fn new(index: usize) -> Self {
Self(index) Self(index)
} }
/// Returns the index. /// Returns the index.
///
/// 返回该下标。
pub const fn index(self) -> usize { pub const fn index(self) -> usize {
self.0 self.0
} }
@ -31,21 +37,34 @@ impl core::fmt::Display for TaskId {
/// while it runs. A demand that exceeds the resource's capacity makes the /// while it runs. A demand that exceeds the resource's capacity makes the
/// task impossible to schedule there, which a scheduler discovers by /// task impossible to schedule there, which a scheduler discovers by
/// comparing the two. /// comparing the two.
///
/// 任务对某个资源的需求。
///
/// 需求是任务运行期间消耗的资源容量大小。超出资源容量的需求使该任务无法在
/// 此处调度,调度器通过比较二者即可发现。
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Requirement { pub struct Requirement {
/// The resource required. /// The resource required.
///
/// 所要求的资源。
pub resource: ResourceId, pub resource: ResourceId,
/// The amount of the resource consumed. /// The amount of the resource consumed.
///
/// 消耗的资源量。
pub demand: f64, pub demand: f64,
} }
impl Requirement { impl Requirement {
/// Creates a requirement. /// Creates a requirement.
///
/// 创建一条需求。
pub const fn new(resource: ResourceId, demand: f64) -> Self { pub const fn new(resource: ResourceId, demand: f64) -> Self {
Self { resource, demand } Self { resource, demand }
} }
/// Returns `true` when the demand is positive and finite. /// Returns `true` when the demand is positive and finite.
///
/// 当该需求为正且有限时返回 `true`。
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
self.demand.is_finite() && self.demand > 0.0 self.demand.is_finite() && self.demand > 0.0
} }
@ -56,35 +75,65 @@ impl Requirement {
/// A task is immutable after construction. The builder methods take `self` by /// A task is immutable after construction. The builder methods take `self` by
/// value and return a new task, so a caller can share a base task and derive /// value and return a new task, so a caller can share a base task and derive
/// variants from it without the variants interfering. /// variants from it without the variants interfering.
///
/// 待调度的一份工作。
///
/// 任务在构造后不可变。各构建方法按值接收 `self` 并返回新任务,故调用方
/// 可以共享一个基础任务并由它派生出若干变体,而变体之间互不干扰。
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Task { pub struct Task {
/// The identifier of this task. /// The identifier of this task.
///
/// 该任务的标识符。
pub id: TaskId, pub id: TaskId,
/// The duration of the task, in seconds. /// The duration of the task, in seconds.
///
/// 任务时长,单位为秒。
pub duration: f64, pub duration: f64,
/// The earliest time at which the task may start, in seconds. /// The earliest time at which the task may start, in seconds.
/// ///
/// This is the start of the earliest window in which the task may run. /// This is the start of the earliest window in which the task may run.
/// A task with no window has no restriction, which is what the /// A task with no window has no restriction, which is what the
/// constructors give. /// constructors give.
///
/// 该任务可开始的最早时刻,单位为秒。
///
/// 即该任务可运行的最早窗口的起点。没有窗口的任务不受限制,构造函数
/// 给出的正是这种情形。
pub earliest_start: f64, pub earliest_start: f64,
/// The latest time at which the task may end, in seconds. /// The latest time at which the task may end, in seconds.
/// ///
/// A task with no window has no restriction. /// A task with no window has no restriction.
///
/// 该任务可结束的最晚时刻,单位为秒。
///
/// 没有窗口的任务不受限制。
pub latest_end: f64, pub latest_end: f64,
/// The resources the task requires, in the order they were added. /// The resources the task requires, in the order they were added.
///
/// 该任务要求的资源,按添加顺序排列。
pub requirements: Vec<Requirement>, pub requirements: Vec<Requirement>,
/// The priority of the task. /// The priority of the task.
/// ///
/// A larger value means the task is more important. The scale is the /// A larger value means the task is more important. The scale is the
/// caller's. A scheduler that maximises weighted value uses this number, /// caller's. A scheduler that maximises weighted value uses this number,
/// and one that maximises the count of scheduled tasks ignores it. /// and one that maximises the count of scheduled tasks ignores it.
///
/// 该任务的优先级。
///
/// 值越大表示任务越重要,量纲由调用方确定。最大化加权价值的调度器使用
/// 该数值,而最大化已调度任务数的调度器则忽略它。
pub priority: f64, pub priority: f64,
/// Whether the task must be scheduled. /// Whether the task must be scheduled.
/// ///
/// A mandatory task is scheduled even at the cost of other tasks. A /// A mandatory task is scheduled even at the cost of other tasks. A
/// scheduler may still fail to place one when no placement exists, and /// scheduler may still fail to place one when no placement exists, and
/// reports that rather than dropping it silently. /// reports that rather than dropping it silently.
///
/// 该任务是否必须被调度。
///
/// 强制任务即使以牺牲其他任务为代价也要调度。当不存在可行放置时调度器
/// 仍可能无法安排它,此时会报告,而不是静默丢弃。
pub mandatory: bool, pub mandatory: bool,
} }
@ -93,6 +142,10 @@ impl Task {
/// ///
/// The task may start at any finite time, because it has no window. It /// The task may start at any finite time, because it has no window. It
/// has a priority of zero and is not mandatory. /// has a priority of zero and is not mandatory.
///
/// 以给定时长、无资源需求创建任务。
///
/// 该任务没有窗口,故可在任意有限时刻开始。其优先级为零,且非强制。
pub fn new(id: TaskId, duration: f64) -> Self { pub fn new(id: TaskId, duration: f64) -> Self {
Self { Self {
id, id,
@ -110,6 +163,11 @@ impl Task {
/// The window is the interval in which the whole task must fit, so the /// The window is the interval in which the whole task must fit, so the
/// task may start no earlier than the window start and must end no later /// task may start no earlier than the window start and must end no later
/// than the window end. /// than the window end.
///
/// 返回带时间窗口的本任务副本。
///
/// 该窗口是整个任务必须容纳其中的区间,故任务起点不早于窗口起点、终点
/// 不晚于窗口终点。
pub fn with_window(mut self, start: f64, end: f64) -> Self { pub fn with_window(mut self, start: f64, end: f64) -> Self {
self.earliest_start = start; self.earliest_start = start;
self.latest_end = end; self.latest_end = end;
@ -117,12 +175,16 @@ impl Task {
} }
/// Returns a copy of this task with a priority. /// Returns a copy of this task with a priority.
///
/// 返回带给定优先级的本任务副本。
pub fn with_priority(mut self, priority: f64) -> Self { pub fn with_priority(mut self, priority: f64) -> Self {
self.priority = priority; self.priority = priority;
self self
} }
/// Returns a copy of this task marked mandatory or not. /// Returns a copy of this task marked mandatory or not.
///
/// 返回标记为强制或非强制的本任务副本。
pub fn with_mandatory(mut self, mandatory: bool) -> Self { pub fn with_mandatory(mut self, mandatory: bool) -> Self {
self.mandatory = mandatory; self.mandatory = mandatory;
self self
@ -133,6 +195,11 @@ impl Task {
/// Adding the same resource twice is allowed at this point, because a /// Adding the same resource twice is allowed at this point, because a
/// builder chain cannot report an error. [`Task::validate`] rejects it, /// builder chain cannot report an error. [`Task::validate`] rejects it,
/// and [`Task::try_require`] reports it immediately. /// and [`Task::try_require`] reports it immediately.
///
/// 返回附加一条资源需求的本任务副本。
///
/// 此处允许重复加入同一资源,因为构建者链无法报告错误。
/// [`Task::validate`] 会拒绝它,[`Task::try_require`] 则立即报告。
pub fn require(mut self, resource: ResourceId, demand: f64) -> Self { pub fn require(mut self, resource: ResourceId, demand: f64) -> Self {
self.requirements.push(Requirement::new(resource, demand)); self.requirements.push(Requirement::new(resource, demand));
self self
@ -140,6 +207,8 @@ impl Task {
/// Returns a copy of this task with an additional resource requirement, /// Returns a copy of this task with an additional resource requirement,
/// reporting a duplicate or an invalid demand immediately. /// reporting a duplicate or an invalid demand immediately.
///
/// 返回附加一条资源需求的本任务副本,并立即报告重复或无效需求。
pub fn try_require(mut self, resource: ResourceId, demand: f64) -> Result<Self, TaskError> { pub fn try_require(mut self, resource: ResourceId, demand: f64) -> Result<Self, TaskError> {
let requirement = Requirement::new(resource, demand); let requirement = Requirement::new(resource, demand);
if !requirement.is_valid() { if !requirement.is_valid() {
@ -161,6 +230,10 @@ impl Task {
/// Returns the demand the task makes on a resource. /// Returns the demand the task makes on a resource.
/// ///
/// Returns `None` when the task does not require the resource. /// Returns `None` when the task does not require the resource.
///
/// 返回该任务对某资源的需求。
///
/// 该任务不要求该资源时返回 `None`。
pub fn demand_for(&self, resource: ResourceId) -> Option<f64> { pub fn demand_for(&self, resource: ResourceId) -> Option<f64> {
self.requirements self.requirements
.iter() .iter()
@ -169,16 +242,22 @@ impl Task {
} }
/// Returns `true` when the task requires a resource. /// Returns `true` when the task requires a resource.
///
/// 当该任务要求某资源时返回 `true`。
pub fn requires(&self, resource: ResourceId) -> bool { pub fn requires(&self, resource: ResourceId) -> bool {
self.demand_for(resource).is_some() self.demand_for(resource).is_some()
} }
/// Returns the number of resources the task requires. /// Returns the number of resources the task requires.
///
/// 返回该任务要求的资源数量。
pub fn requirement_count(&self) -> usize { pub fn requirement_count(&self) -> usize {
self.requirements.len() self.requirements.len()
} }
/// Returns the identifiers of the required resources. /// Returns the identifiers of the required resources.
///
/// 返回所要求资源的标识符。
pub fn resources(&self) -> Vec<ResourceId> { pub fn resources(&self) -> Vec<ResourceId> {
self.requirements self.requirements
.iter() .iter()
@ -191,11 +270,17 @@ impl Task {
/// ///
/// Returns infinity when the task has no restriction at either end, and /// Returns infinity when the task has no restriction at either end, and
/// zero or a negative value when the window cannot hold the task at all. /// zero or a negative value when the window cannot hold the task at all.
///
/// 返回该任务必须容纳其中的窗口长度,单位为秒。
///
/// 任务两端均无限制时返回无穷;窗口完全容不下该任务时返回零或负值。
pub fn window_length(&self) -> f64 { pub fn window_length(&self) -> f64 {
self.latest_end - self.earliest_start self.latest_end - self.earliest_start
} }
/// Returns `true` when the window cannot hold the task. /// Returns `true` when the window cannot hold the task.
///
/// 当窗口容不下该任务时返回 `true`。
pub fn window_is_too_short(&self) -> bool { pub fn window_is_too_short(&self) -> bool {
self.duration > self.window_length() self.duration > self.window_length()
} }
@ -204,17 +289,25 @@ impl Task {
/// ///
/// Returns infinity when the task has no upper bound, and negative /// Returns infinity when the task has no upper bound, and negative
/// infinity when it has no lower bound. /// infinity when it has no lower bound.
///
/// 返回在其窗口下该任务可能结束的最早时刻。
///
/// 任务无上界时返回无穷,无下界时返回负无穷。
pub fn earliest_possible_end(&self) -> f64 { pub fn earliest_possible_end(&self) -> f64 {
self.earliest_start + self.duration self.earliest_start + self.duration
} }
/// Returns `true` when the task fits its window. /// Returns `true` when the task fits its window.
///
/// 当该任务能容纳于其窗口时返回 `true`。
pub fn fits_window(&self) -> bool { pub fn fits_window(&self) -> bool {
!self.window_is_too_short() !self.window_is_too_short()
} }
/// Returns `true` when the task uses any resource more than once, or /// Returns `true` when the task uses any resource more than once, or
/// demands a non-positive amount. /// demands a non-positive amount.
///
/// 当该任务重复使用某资源,或需求非正时返回 `true`。
pub fn has_duplicate_requirements(&self) -> bool { pub fn has_duplicate_requirements(&self) -> bool {
self.requirements self.requirements
.iter() .iter()
@ -232,6 +325,11 @@ impl Task {
/// is finite at the ends it states and holds the duration, every demand /// is finite at the ends it states and holds the duration, every demand
/// is positive and finite and names a distinct resource, and the weights /// is positive and finite and names a distinct resource, and the weights
/// are finite. /// are finite.
///
/// 校验该任务;不可用时返回原因。
///
/// 任务可用当且仅当:时长有限且为正;窗口在其给出的两端有限并容得下该
/// 时长;每条需求为正且有限并指向互不相同的资源;各权值有限。
pub fn validate(&self) -> Result<(), TaskError> { pub fn validate(&self) -> Result<(), TaskError> {
if !self.duration.is_finite() || self.duration <= 0.0 { if !self.duration.is_finite() || self.duration <= 0.0 {
return Err(TaskError::new(TaskErrorKind::InvalidDuration) return Err(TaskError::new(TaskErrorKind::InvalidDuration)
@ -282,6 +380,8 @@ impl Task {
} }
/// Returns `true` when the task is usable. /// Returns `true` when the task is usable.
///
/// 当该任务可用时返回 `true`。
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
self.validate().is_ok() self.validate().is_ok()
} }
@ -292,6 +392,11 @@ impl Task {
/// This is the quantity a scheduler maximises when it is asked to make /// This is the quantity a scheduler maximises when it is asked to make
/// the most of the available time: a long task of high priority is worth /// the most of the available time: a long task of high priority is worth
/// more than a short one of the same priority. /// more than a short one of the same priority.
///
/// 返回该任务的价值,即其优先级与时长的乘积。
///
/// 当调度器被要求充分利用可用时间时,最大化的正是该量:同等优先级下
/// 时长较长的任务价值更高。
pub fn weighted_value(&self) -> f64 { pub fn weighted_value(&self) -> f64 {
self.priority * self.duration self.priority * self.duration
} }
@ -315,38 +420,64 @@ impl core::fmt::Display for Task {
/// considered for several placements without being modified. A task that has /// considered for several placements without being modified. A task that has
/// been placed records when and where, so that a schedule can be examined /// been placed records when and where, so that a schedule can be examined
/// without a second lookup. /// without a second lookup.
///
/// 调度器为某任务指定的状态。
///
/// 状态与任务分开保存,故同一任务可被考虑多次放置而不被修改。已放置的
/// 任务记录了何时何地,从而无需二次查找即可检查一份计划。
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum TaskState { pub enum TaskState {
/// The task has not been placed and is still being considered. /// The task has not been placed and is still being considered.
///
/// 该任务尚未放置,仍在考虑之中。
Pending, Pending,
/// The task has been placed at a time. /// The task has been placed at a time.
///
/// 该任务已在某时刻放置。
Scheduled { Scheduled {
/// The start time of the placement, in seconds. /// The start time of the placement, in seconds.
///
/// 放置的起始时刻,单位为秒。
start: f64, start: f64,
}, },
/// The task was deliberately left out. /// The task was deliberately left out.
/// ///
/// A scheduler reports this rather than silently dropping a task, so that /// A scheduler reports this rather than silently dropping a task, so that
/// an operator can see what was given up and why. /// an operator can see what was given up and why.
///
/// 该任务被有意排除在外。
///
/// 调度器报告此状态而非静默丢弃任务,使操作者能看出放弃了什么以及
/// 为什么。
Deferred, Deferred,
/// The task cannot be scheduled at all. /// The task cannot be scheduled at all.
/// ///
/// A task is infeasible when its window is too short, when it demands /// A task is infeasible when its window is too short, when it demands
/// more of a resource than the resource has, or when a resource it needs /// more of a resource than the resource has, or when a resource it needs
/// does not exist. /// does not exist.
///
/// 该任务完全无法调度。
///
/// 任务不可行的情形有:窗口过短;需求超过某资源容量;所需资源不存在。
Infeasible { Infeasible {
/// A description of why the task cannot be scheduled. /// A description of why the task cannot be scheduled.
///
/// 该任务无法调度的原因说明。
reason: String, reason: String,
}, },
} }
impl TaskState { impl TaskState {
/// Returns `true` when the task has been placed. /// Returns `true` when the task has been placed.
///
/// 当该任务已被放置时返回 `true`。
pub const fn is_scheduled(&self) -> bool { pub const fn is_scheduled(&self) -> bool {
matches!(self, Self::Scheduled { .. }) matches!(self, Self::Scheduled { .. })
} }
/// Returns the start time of the placement, if there is one. /// Returns the start time of the placement, if there is one.
///
/// 返回放置的起始时刻,若有。
pub const fn start(&self) -> Option<f64> { pub const fn start(&self) -> Option<f64> {
match self { match self {
Self::Scheduled { start } => Some(*start), Self::Scheduled { start } => Some(*start),
@ -355,11 +486,15 @@ impl TaskState {
} }
/// Returns the end time of the placement, if there is one. /// Returns the end time of the placement, if there is one.
///
/// 返回放置的结束时刻,若有。
pub fn end(&self, duration: f64) -> Option<f64> { pub fn end(&self, duration: f64) -> Option<f64> {
self.start().map(|start| start + duration) self.start().map(|start| start + duration)
} }
/// Returns a short name for the state. /// Returns a short name for the state.
///
/// 返回该状态的简称。
pub const fn name(&self) -> &'static str { pub const fn name(&self) -> &'static str {
match self { match self {
Self::Pending => "pending", Self::Pending => "pending",
@ -371,6 +506,8 @@ impl TaskState {
} }
/// A collection of tasks, indexed by identifier. /// A collection of tasks, indexed by identifier.
///
/// 以标识符为索引的任务集合。
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
pub struct TaskSet { pub struct TaskSet {
tasks: Vec<Task>, tasks: Vec<Task>,
@ -378,6 +515,8 @@ pub struct TaskSet {
impl TaskSet { impl TaskSet {
/// Creates an empty set. /// Creates an empty set.
///
/// 创建一个空集合。
pub const fn new() -> Self { pub const fn new() -> Self {
Self { tasks: Vec::new() } Self { tasks: Vec::new() }
} }
@ -386,6 +525,10 @@ impl TaskSet {
/// ///
/// Returns an error when two tasks share an identifier or when any task /// Returns an error when two tasks share an identifier or when any task
/// is not usable. /// is not usable.
///
/// 由一组任务创建集合。
///
/// 两个任务标识符相同或任一任务不可用时返回错误。
pub fn from_tasks(tasks: Vec<Task>) -> Result<Self, TaskError> { pub fn from_tasks(tasks: Vec<Task>) -> Result<Self, TaskError> {
let mut set = Self::new(); let mut set = Self::new();
for task in tasks { for task in tasks {
@ -398,6 +541,10 @@ impl TaskSet {
/// ///
/// Returns an error when the identifier is already present or when the /// Returns an error when the identifier is already present or when the
/// task is not usable. /// task is not usable.
///
/// 插入一个任务。
///
/// 该标识符已存在或该任务不可用时返回错误。
pub fn insert(&mut self, task: Task) -> Result<(), TaskError> { pub fn insert(&mut self, task: Task) -> Result<(), TaskError> {
task.validate()?; task.validate()?;
if self.get(task.id).is_some() { if self.get(task.id).is_some() {
@ -410,6 +557,8 @@ impl TaskSet {
} }
/// Returns the task with the given identifier. /// Returns the task with the given identifier.
///
/// 返回具有所给标识符的任务。
pub fn get(&self, id: TaskId) -> Option<&Task> { pub fn get(&self, id: TaskId) -> Option<&Task> {
self.tasks.iter().find(|task| task.id == id) self.tasks.iter().find(|task| task.id == id)
} }
@ -417,6 +566,10 @@ impl TaskSet {
/// Removes a task. /// Removes a task.
/// ///
/// Returns an error when no task has the identifier. /// Returns an error when no task has the identifier.
///
/// 移除一个任务。
///
/// 没有任务具有该标识符时返回错误。
pub fn remove(&mut self, id: TaskId) -> Result<Task, TaskError> { pub fn remove(&mut self, id: TaskId) -> Result<Task, TaskError> {
let index = self let index = self
.tasks .tasks
@ -429,16 +582,22 @@ impl TaskSet {
} }
/// Returns the tasks in the order they were added. /// Returns the tasks in the order they were added.
///
/// 返回按添加顺序排列的任务。
pub fn tasks(&self) -> &[Task] { pub fn tasks(&self) -> &[Task] {
&self.tasks &self.tasks
} }
/// Returns the number of tasks. /// Returns the number of tasks.
///
/// 返回任务数量。
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.tasks.len() self.tasks.len()
} }
/// Returns `true` when there are no tasks. /// Returns `true` when there are no tasks.
///
/// 当没有任务时返回 `true`。
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.tasks.is_empty() self.tasks.is_empty()
} }
@ -447,16 +606,24 @@ impl TaskSet {
/// ///
/// This is the load the tasks would place on the schedule if each used /// This is the load the tasks would place on the schedule if each used
/// one unit of resource for its whole duration. /// one unit of resource for its whole duration.
///
/// 返回所有任务的总时长,单位为秒。
///
/// 这是各任务在整个时长内各占一个单位资源时,会施加到计划上的负载。
pub fn total_duration(&self) -> f64 { pub fn total_duration(&self) -> f64 {
self.tasks.iter().map(|task| task.duration).sum() self.tasks.iter().map(|task| task.duration).sum()
} }
/// Returns the total weighted value of every task. /// Returns the total weighted value of every task.
///
/// 返回所有任务的总加权价值。
pub fn total_weighted_value(&self) -> f64 { pub fn total_weighted_value(&self) -> f64 {
self.tasks.iter().map(Task::weighted_value).sum() self.tasks.iter().map(Task::weighted_value).sum()
} }
/// Returns the identifiers of the tasks that require a resource. /// Returns the identifiers of the tasks that require a resource.
///
/// 返回要求某资源的任务标识符。
pub fn tasks_requiring(&self, resource: ResourceId) -> Vec<TaskId> { pub fn tasks_requiring(&self, resource: ResourceId) -> Vec<TaskId> {
self.tasks self.tasks
.iter() .iter()
@ -466,6 +633,8 @@ impl TaskSet {
} }
/// Returns the identifiers of the mandatory tasks. /// Returns the identifiers of the mandatory tasks.
///
/// 返回强制任务的标识符。
pub fn mandatory(&self) -> Vec<TaskId> { pub fn mandatory(&self) -> Vec<TaskId> {
self.tasks self.tasks
.iter() .iter()
@ -486,6 +655,15 @@ impl TaskSet {
/// This is not guaranteed to find the best possible schedule. Scheduling /// This is not guaranteed to find the best possible schedule. Scheduling
/// with resource constraints is intractable in general, so a heuristic is the /// with resource constraints is intractable in general, so a heuristic is the
/// practical choice and the caller may supply its own order instead. /// practical choice and the caller may supply its own order instead.
///
/// 返回适合贪心调度器的任务次序。
///
/// 该次序按加权价值递减排列,这是一个有效启发式:先放置贡献最大的任务,
/// 把价值最小的留给填补空隙。平局先按更早的截止时刻、再按标识符打破,故
/// 对给定集合次序是确定的。
///
/// 它不保证找到最优计划。带资源约束的调度在一般情况下不可解,故启发式
/// 是现实的选择,调用方亦可自行提供次序。
pub fn task_order(tasks: &[Task]) -> Vec<TaskId> { pub fn task_order(tasks: &[Task]) -> Vec<TaskId> {
let mut ordered: Vec<&Task> = tasks.iter().collect(); let mut ordered: Vec<&Task> = tasks.iter().collect();
ordered.sort_by(|first, second| { ordered.sort_by(|first, second| {