use std::collections::{HashMap, HashSet};
use ifc_model::{EntityId, Model, Value};
use crate::sequence::{sequences, SequenceCycle};
pub mod assigns {
pub const GLOBAL_ID: usize = 0;
pub const RELATED: usize = 4;
pub const RELATING: usize = 6;
}
pub mod nests {
pub const GLOBAL_ID: usize = 0;
pub const RELATING: usize = 4;
pub const RELATED: usize = 5;
}
#[must_use]
pub fn tasks_of_schedule(model: &Model, schedule: EntityId) -> Vec<EntityId> {
let mut out = Vec::new();
for (_, entity) in model.of_type("IFCRELASSIGNSTOCONTROL") {
let relating = match entity.attribute(assigns::RELATING) {
Some(Value::Ref(id)) => *id,
_ => continue,
};
if relating != schedule {
continue;
}
if let Some(v) = entity.attribute(assigns::RELATED) {
v.for_each_ref(&mut |id| {
if !out.contains(&id) {
out.push(id);
}
});
}
}
out
}
#[must_use]
pub fn subtasks_of(model: &Model, parent: EntityId) -> Vec<EntityId> {
let mut out = Vec::new();
for (_, entity) in model.of_type("IFCRELNESTS") {
let relating = match entity.attribute(nests::RELATING) {
Some(Value::Ref(id)) => *id,
_ => continue,
};
if relating != parent {
continue;
}
if let Some(v) = entity.attribute(nests::RELATED) {
v.for_each_ref(&mut |id| out.push(id));
}
}
out
}
#[must_use]
pub fn start_tasks(model: &Model) -> Vec<EntityId> {
let links = sequences(model);
let has_predecessor: HashSet<EntityId> = links.iter().map(|s| s.successor).collect();
model
.ids_of_type("IFCTASK")
.iter()
.copied()
.filter(|id| !has_predecessor.contains(id))
.collect()
}
#[must_use]
pub fn end_tasks(model: &Model) -> Vec<EntityId> {
let links = sequences(model);
let has_successor: HashSet<EntityId> = links.iter().map(|s| s.predecessor).collect();
model
.ids_of_type("IFCTASK")
.iter()
.copied()
.filter(|id| !has_successor.contains(id))
.collect()
}
pub fn execution_order(model: &Model) -> Result<Vec<EntityId>, SequenceCycle> {
let links = sequences(model);
let tasks: Vec<EntityId> = model.ids_of_type("IFCTASK").to_vec();
let position: HashMap<EntityId, usize> =
tasks.iter().enumerate().map(|(i, id)| (*id, i)).collect();
let mut indegree: HashMap<EntityId, usize> = tasks.iter().map(|id| (*id, 0)).collect();
let mut edges: HashMap<EntityId, Vec<EntityId>> = HashMap::new();
for link in &links {
if !position.contains_key(&link.predecessor) || !position.contains_key(&link.successor) {
continue;
}
edges
.entry(link.predecessor)
.or_default()
.push(link.successor);
*indegree.entry(link.successor).or_insert(0) += 1;
}
let mut ready: Vec<EntityId> = tasks
.iter()
.copied()
.filter(|id| indegree.get(id).copied().unwrap_or(0) == 0)
.collect();
ready.sort_by_key(|id| position.get(id).copied().unwrap_or(usize::MAX));
let mut out = Vec::new();
while let Some(next) = ready.first().copied() {
ready.remove(0);
out.push(next);
for successor in edges.get(&next).cloned().unwrap_or_default() {
let degree = indegree.entry(successor).or_insert(0);
*degree = degree.saturating_sub(1);
if *degree == 0 {
ready.push(successor);
ready.sort_by_key(|id| position.get(id).copied().unwrap_or(usize::MAX));
}
}
}
if out.len() != tasks.len() {
if let Some(cycle) = crate::sequence::find_cycle(model) {
return Err(cycle);
}
let stalled = tasks
.iter()
.find(|id| !out.contains(id))
.copied()
.unwrap_or(EntityId(0));
return Err(SequenceCycle {
repeated: stalled,
path: vec![stalled],
});
}
Ok(out)
}