use ifc_model::{Entity, EntityId, Model, Value};
use super::definition::{Task, TaskTimeAnomaly};
use crate::release::ReadRelease;
use crate::schedule::AuthoredDateTime;
use crate::SchemaVersion;
const ASSIGNS_TASKS: &str = "IFCRELASSIGNSTASKS";
const TIME_CONTROL: &str = "IFCSCHEDULETIMECONTROL";
#[derive(Debug, Clone, Copy)]
pub struct ScheduleTimeControl<'m> {
id: EntityId,
entity: &'m Entity,
assignment: EntityId,
work_control: Option<EntityId>,
release: ReadRelease,
}
impl<'m> ScheduleTimeControl<'m> {
#[must_use]
pub fn id(&self) -> EntityId {
self.id
}
#[must_use]
pub fn assignment(&self) -> EntityId {
self.assignment
}
#[must_use]
pub fn work_control(&self) -> Option<EntityId> {
self.work_control
}
#[must_use]
pub fn release(&self) -> SchemaVersion {
self.release.version()
}
fn value(&self, attribute: &'static str) -> Option<&'m Value> {
self.release.value(TIME_CONTROL, self.entity, attribute)
}
fn date_time(&self, attribute: &'static str) -> Option<AuthoredDateTime<'m>> {
match self.value(attribute)? {
Value::Ref(record) => Some(AuthoredDateTime::Record(*record)),
value => value.unwrap_typed().as_text().map(AuthoredDateTime::Text),
}
}
fn measure(&self, attribute: &'static str) -> Option<f64> {
self.value(attribute)?.unwrap_typed().as_f64()
}
#[must_use]
pub fn global_id(&self) -> Option<&'m str> {
self.release.text(TIME_CONTROL, self.entity, "GlobalId")
}
#[must_use]
pub fn name(&self) -> Option<&'m str> {
self.release.text(TIME_CONTROL, self.entity, "Name")
}
#[must_use]
pub fn actual_start(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("ActualStart")
}
#[must_use]
pub fn early_start(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("EarlyStart")
}
#[must_use]
pub fn late_start(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("LateStart")
}
#[must_use]
pub fn schedule_start(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("ScheduleStart")
}
#[must_use]
pub fn actual_finish(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("ActualFinish")
}
#[must_use]
pub fn early_finish(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("EarlyFinish")
}
#[must_use]
pub fn late_finish(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("LateFinish")
}
#[must_use]
pub fn schedule_finish(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("ScheduleFinish")
}
#[must_use]
pub fn status_time(&self) -> Option<AuthoredDateTime<'m>> {
self.date_time("StatusTime")
}
#[must_use]
pub fn schedule_duration(&self) -> Option<f64> {
self.measure("ScheduleDuration")
}
#[must_use]
pub fn actual_duration(&self) -> Option<f64> {
self.measure("ActualDuration")
}
#[must_use]
pub fn remaining_time(&self) -> Option<f64> {
self.measure("RemainingTime")
}
#[must_use]
pub fn free_float(&self) -> Option<f64> {
self.measure("FreeFloat")
}
#[must_use]
pub fn total_float(&self) -> Option<f64> {
self.measure("TotalFloat")
}
#[must_use]
pub fn start_float(&self) -> Option<f64> {
self.measure("StartFloat")
}
#[must_use]
pub fn finish_float(&self) -> Option<f64> {
self.measure("FinishFloat")
}
#[must_use]
pub fn is_critical(&self) -> Option<bool> {
self.value("IsCritical")?.as_bool()
}
#[must_use]
pub fn completion(&self) -> Option<f64> {
self.measure("Completion")
}
}
impl<'m> Task<'m> {
#[must_use]
pub fn schedule_time_controls(
&self,
model: &'m Model,
) -> (Vec<ScheduleTimeControl<'m>>, Vec<TaskTimeAnomaly>) {
let release = self.read_release();
let mut found = Vec::new();
let mut anomalies = Vec::new();
if !release.declares(ASSIGNS_TASKS) {
return (found, anomalies);
}
for assignment in release.instances_of(model, ASSIGNS_TASKS) {
let Some(relation) = model.get(assignment) else {
continue;
};
let mut related = false;
if let Some(objects) = release.value(ASSIGNS_TASKS, relation, "RelatedObjects") {
objects.for_each_ref(&mut |id| related |= id == self.id());
}
if !related {
continue;
}
let Some(target) = release.reference(ASSIGNS_TASKS, relation, "TimeForTask") else {
continue;
};
let Some(entity) = model.get(target) else {
continue;
};
if !release.is_a(&entity.type_name, TIME_CONTROL) {
anomalies.push(TaskTimeAnomaly::NotAScheduleTimeControl {
task: self.id(),
assignment,
target,
found: entity.type_name.to_string(),
});
continue;
}
found.push(ScheduleTimeControl {
id: target,
entity,
assignment,
work_control: release.reference(ASSIGNS_TASKS, relation, "RelatingControl"),
release,
});
}
(found, anomalies)
}
}