From bbd3cb0c3f08d0199c08d67a932edc8acf7174ee Mon Sep 17 00:00:00 2001 From: ApsisRS contributors Date: Fri, 2 Oct 2026 01:04:12 +0800 Subject: [PATCH] docs: bilingual comments for apsis-conflict --- crates/ops/apsis-conflict/src/conflict.rs | 88 +++++++++++++++++++++++ crates/ops/apsis-conflict/src/detect.rs | 44 ++++++++++++ 2 files changed, 132 insertions(+) diff --git a/crates/ops/apsis-conflict/src/conflict.rs b/crates/ops/apsis-conflict/src/conflict.rs index afea035..2d2a8fd 100644 --- a/crates/ops/apsis-conflict/src/conflict.rs +++ b/crates/ops/apsis-conflict/src/conflict.rs @@ -5,18 +5,30 @@ use apsis_task::TaskId; use core::fmt; /// The kind of problem a conflict describes. +/// +/// 冲突所描述的问题类别。 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum ConflictKind { /// Two placements need more of a resource than it has. + /// + /// 两处放置所需的资源超过其容量。 Capacity, /// A placement falls outside the time window of its task. + /// + /// 某处放置落在其任务的时间窗口之外。 Window, /// A placement does not agree with its task. + /// + /// 某处放置与其任务不一致。 Requirement, /// A placement falls outside the availability of a resource. + /// + /// 某处放置落在某资源的可用性之外。 Availability, /// A task that must follow another starts too early. + /// + /// 某任务未待其前驱完成即过早开始。 Precedence, } @@ -38,28 +50,50 @@ impl fmt::Display for ConflictKind { /// The conflict names the tasks involved and, where it applies, the resource /// and the instant at which the problem occurs. That is enough for an /// operator to locate the placement without re-deriving the analysis. +/// +/// 计划中发现的单个问题。 +/// +/// 冲突指明所涉任务,并在适用时指明资源及问题发生的时刻。这足以让操作 +/// 者定位放置,而无须重新推导分析。 #[derive(Debug, Clone, PartialEq)] pub struct Conflict { /// The kind of problem. + /// + /// 问题类别。 pub kind: ConflictKind, /// The task the conflict is centred on. + /// + /// 该冲突所围绕的任务。 pub task: TaskId, /// The other task involved, for a conflict between two placements. + /// + /// 所涉的另一个任务,用于两处放置之间的冲突。 pub other: Option, /// The resource involved, where the conflict is about one. + /// + /// 所涉资源,当冲突与之相关时。 pub resource: Option, /// The time at which the problem occurs, in seconds. /// /// For a capacity conflict this is the instant at which the demand /// exceeds the capacity. For a window conflict it is the offending edge /// of the placement. + /// + /// 问题发生的时刻,单位为秒。 + /// + /// 对容量冲突而言,是需求超出容量的那一瞬;对窗口冲突而言,是该 + /// 放置的越界之处。 pub at: f64, /// A description of the problem. + /// + /// 该问题的说明。 pub detail: String, } impl Conflict { /// Creates a conflict. + /// + /// 创建一个冲突。 pub fn new(kind: ConflictKind, task: TaskId, at: f64, detail: impl Into) -> Self { Self { kind, @@ -72,12 +106,16 @@ impl Conflict { } /// Returns a copy of this conflict involving another task. + /// + /// 返回涉及另一任务的本冲突副本。 pub fn with_other(mut self, other: TaskId) -> Self { self.other = Some(other); self } /// Returns a copy of this conflict involving a resource. + /// + /// 返回涉及某资源的本冲突副本。 pub fn with_resource(mut self, resource: ResourceId) -> Self { self.resource = Some(resource); self @@ -89,6 +127,11 @@ impl Conflict { /// A capacity or availability conflict can often be repaired by moving /// the placement, while a requirement conflict means the placement does /// not belong in the schedule at all. + /// + /// 当该冲突涉及一处应当移动而非丢弃的放置时返回 `true`。 + /// + /// 容量或可用性冲突常可通过移动放置来修复,而需求冲突意味着该 + /// 放置本就不应出现在计划中。 pub const fn is_movable(&self) -> bool { matches!( self.kind, @@ -120,6 +163,11 @@ impl fmt::Display for Conflict { /// follows the plan. The counts by kind are provided because a planner /// usually wants to know how many of each problem there are before deciding /// what to do. +/// +/// 计划中发现的所有冲突。 +/// +/// 报告按时间、再按类别排序,故阅读它即是在阅读该计划。各类计数一并 +/// 提供,因为规划器通常希望先知道每类问题各有若干,再决定对策。 #[derive(Debug, Clone, PartialEq, Default)] pub struct ConflictReport { conflicts: Vec, @@ -132,6 +180,11 @@ impl ConflictReport { /// /// The conflicts are sorted by time, then by kind, then by task, so that /// the order does not depend on the order they were found. + /// + /// 由一组冲突与任务计数创建报告。 + /// + /// 冲突按时间、类别、任务依次排序,故其次序不取决于发现它们的 + /// 顺序。 pub fn new(mut conflicts: Vec, offered_tasks: usize, placed_tasks: usize) -> Self { conflicts.sort_by(|first, second| { first @@ -150,11 +203,15 @@ impl ConflictReport { } /// Returns the conflicts, in time order. + /// + /// 返回各冲突,按时间排序。 pub fn conflicts(&self) -> &[Conflict] { &self.conflicts } /// Returns the number of conflicts. + /// + /// 返回冲突数量。 pub fn len(&self) -> usize { self.conflicts.len() } @@ -163,6 +220,10 @@ impl ConflictReport { /// /// This is the conventional spelling of [`ConflictReport::is_clear`], and /// the two always agree. + /// + /// 当没有冲突时返回 `true`。 + /// + /// 这是 [`ConflictReport::is_clear`] 的惯用拼写,二者始终一致。 pub fn is_empty(&self) -> bool { self.conflicts.is_empty() } @@ -171,16 +232,25 @@ impl ConflictReport { /// /// A clear report means the schedule is feasible with respect to every /// check this crate performs, not that it is optimal. + /// + /// 当没有冲突时返回 `true`。 + /// + /// 报告无冲突意味着该计划就本 crate 执行的每项检查而言是可行的, + /// 并不意味着它最优。 pub fn is_clear(&self) -> bool { self.conflicts.is_empty() } /// Returns `true` when there is at least one conflict. + /// + /// 当至少存在一个冲突时返回 `true`。 pub fn has_conflicts(&self) -> bool { !self.conflicts.is_empty() } /// Returns the number of conflicts of one kind. + /// + /// 返回某一类别的冲突数量。 pub fn count_of(&self, kind: ConflictKind) -> usize { self.conflicts .iter() @@ -189,6 +259,8 @@ impl ConflictReport { } /// Returns the conflicts of one kind, in time order. + /// + /// 返回某一类别的冲突,按时间排序。 pub fn of_kind(&self, kind: ConflictKind) -> Vec<&Conflict> { self.conflicts .iter() @@ -200,6 +272,11 @@ impl ConflictReport { /// /// A task appears once even when it has several conflicts, which is what /// a repair pass wants: the set of tasks to reconsider. + /// + /// 返回至少卷入一个冲突的任务。 + /// + /// 同一任务即使有多个冲突也只出现一次,这正是一个修复轮所需:待重新 + /// 考虑的任务集合。 pub fn conflicting_tasks(&self) -> Vec { let mut tasks: Vec = Vec::new(); for conflict in &self.conflicts { @@ -216,16 +293,22 @@ impl ConflictReport { } /// Returns the number of tasks that were offered to the schedule. + /// + /// 返回提交给该计划的任务数量。 pub fn offered_tasks(&self) -> usize { self.offered_tasks } /// Returns the number of tasks that were placed. + /// + /// 返回已被放置的任务数量。 pub fn placed_tasks(&self) -> usize { self.placed_tasks } /// Returns a one line summary of the report. + /// + /// 返回该报告的单行摘要。 pub fn summary(&self) -> String { format!( "{} conflict(s) among {} placed of {} offered task(s)", @@ -241,6 +324,11 @@ impl ConflictReport { /// The order runs from the problems that invalidate a plan to the ones that /// merely make it expensive, so that reading the report top to bottom /// addresses the most serious problems first. +/// +/// 返回使各类别按可读次序分组的排序键。 +/// +/// 该次序从使计划失效的问题排到仅使计划昂贵的问题,故自顶向下阅读报告 +/// 时先处理最严重者。 const fn kind_order(kind: ConflictKind) -> u8 { match kind { ConflictKind::Requirement => 0, diff --git a/crates/ops/apsis-conflict/src/detect.rs b/crates/ops/apsis-conflict/src/detect.rs index 4d4b567..e0ebbae 100644 --- a/crates/ops/apsis-conflict/src/detect.rs +++ b/crates/ops/apsis-conflict/src/detect.rs @@ -6,22 +6,32 @@ use apsis_schedule::Schedule; use apsis_task::{Task, TaskId}; /// A precedence constraint: one task must finish before another starts. +/// +/// 一条优先约束:一个任务必须先于另一个开始而完成。 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Precedence { /// The task that must finish first. + /// + /// 必须先完成的任务。 pub before: TaskId, /// The task that must start after. + /// + /// 必须后开始的任务。 pub after: TaskId, } impl Precedence { /// Creates a precedence constraint. + /// + /// 创建一条优先约束。 pub const fn new(before: TaskId, after: TaskId) -> Self { Self { before, after } } } /// Returns the placements of a task, which is at most one. +/// +/// 返回某任务的放置,至多一处。 pub fn placements_of(schedule: &Schedule, task: TaskId) -> Option<&apsis_schedule::Placement> { schedule.placement_of(task) } @@ -45,11 +55,28 @@ pub fn placements_of(schedule: &Schedule, task: TaskId) -> Option<&apsis_schedul /// total demand on a resource is constant between them, so the check is exact /// and takes time proportional to the number of placements rather than to the /// length of the plan. +/// +/// 返回计划中的所有冲突。 +/// +/// 检查是完整执行的:报告包含所有发现的问题,而非只有首个。这是有意 +/// 的,因为拿到一份计划的规划器通常希望先知道它有多糟,再决定修复还是 +/// 重建。 +/// +/// 检查项为: +/// +/// - 每处放置的任务都在列表中,且时长一致; +/// - 每处放置都落在其任务窗口内; +/// - 任何瞬时没有资源超出其容量; +/// - 每处放置都落在其所要求的每个资源的可用窗口内。 +/// +/// 资源扫掠检查各放置的边界,因为资源上的总需求在相邻边界之间恒定, +/// 故该检查是精确的,耗时正比于放置数而非计划长度。 pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> ConflictReport { let mut conflicts = Vec::new(); // The requirement and window checks need only the placement and its // task. + // 需求与窗口检查只需放置及其任务。 for placement in schedule.placements() { let task = tasks.iter().find(|task| task.id == placement.task); let Some(task) = task else { @@ -104,6 +131,7 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C // The availability check: a placement must fall inside a window of every // resource it requires. + // 可用性检查:每处放置必须落在其所要求的每个资源的某个窗口内。 for placement in schedule.placements() { let Some(task) = tasks.iter().find(|task| task.id == placement.task) else { continue; @@ -123,6 +151,7 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C }; // A demand greater than the capacity can never be met, which is a // requirement problem rather than a placement problem. + // 超过容量的需求永远无法满足,故属需求问题而非放置问题。 if requirement.demand > resource.capacity() + 1e-9 { conflicts.push( Conflict::new( @@ -158,9 +187,11 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C } // The capacity check, one resource at a time. + // 容量检查,逐个资源进行。 for resource in resources.resources() { // Each event is a time, a change in demand, and the task responsible, // so that a conflict can name the pair. + // 每个事件含时刻、需求变化与责任任务,使冲突得以指明这一对。 let mut events: Vec<(f64, f64, TaskId)> = Vec::new(); for placement in schedule.placements() { let Some(task) = tasks.iter().find(|task| task.id == placement.task) else { @@ -176,6 +207,7 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C } // A release sorts before an acquisition at the same instant, so two // placements that touch are not counted as overlapping. + // 同一瞬先排释放后排获取,使两个相接的放置不计为重叠。 events.sort_by(|first, second| { first .0 @@ -203,6 +235,8 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C // Report the pair that first pushes the resource over, so // that the conflict names two tasks rather than only the one // that arrived last. + // 报告首次把该资源推过量的一对,使冲突指明两个任务而 + // 不仅是最后到达的那个。 let mut involved = holders.clone(); involved.sort(); let first = involved.first().copied(); @@ -223,6 +257,8 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C // Report each resource once, at the first instant it is over // committed. A second report at a later instant would add // nothing a repair pass can use. + // 每个资源只报告一次,取其首次超订的时刻。较晚时刻 + // 的第二次报告对一个修复轮毫无用处。 break; } } @@ -240,6 +276,14 @@ pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> C /// /// The constraints are checked as given, so they must be acyclic. A cycle is /// a contradiction in the caller's own model and is not detected here. +/// +/// 返回一组约束中的优先冲突。 +/// +/// 当本应居次的任务在应居首者完成之前开始,即为优先冲突。若某约束的两 +/// 个任务未被同时放置,则不构成冲突,因为对它们未作任何承诺。 +/// +/// 约束按给定形式检查,故必须无环。环是调用方自身模型中的矛盾,此处 +/// 不予检测。 pub fn detect_precedence(schedule: &Schedule, constraints: &[Precedence]) -> Vec { let mut conflicts = Vec::new(); for constraint in constraints {