use ifc_model::{EntityId, Model, Transaction, Value};
use super::datetime::{patch, DateTimeValue, PLACEHOLDERS};
use super::procedure::{self, ProcedureDraft};
use super::{
blank, checks, is_user_defined, optional_enum, optional_text, reference_list, EventDraft,
ScheduleAuthoringResult, TaskDraft, WorkControlDraft,
};
use crate::error::ScheduleAuthoringError;
use crate::release::{bind, Release};
use crate::schedule::WorkControlKind;
use ifc_schema::SchemaVersion;
fn stage(
tx: &mut Transaction,
model: &Model,
release: Release,
entity: &'static str,
values: Vec<(&'static str, Value)>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let mut named = vec![("OwnerHistory", Value::Ref(owner_history))];
named.extend(values);
let record = release.record(entity, named)?;
release.require_owner_history(tx, model, entity, owner_history)?;
Ok(tx.create(record))
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum TimeLag {
LagTime(EntityId),
Seconds(f64),
}
impl TimeLag {
fn value(self) -> Value {
match self {
Self::LagTime(id) => Value::Ref(id),
Self::Seconds(seconds) => Value::Real(seconds),
}
}
}
fn refs(ids: &[EntityId]) -> Value {
Value::List(ids.iter().copied().map(Value::Ref).collect())
}
pub fn create_task_with_owner_history(
tx: &mut Transaction,
model: &Model,
draft: TaskDraft<'_>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
checks::task(&draft)?;
let values = vec![
("GlobalId", Value::Text(draft.global_id.into())),
("Name", optional_text(draft.name)),
("Description", optional_text(draft.description)),
("Identification", optional_text(draft.identification)),
("LongDescription", optional_text(draft.long_description)),
("Status", optional_text(draft.status)),
("WorkMethod", optional_text(draft.work_method)),
("IsMilestone", Value::Bool(draft.is_milestone)),
(
"Priority",
draft.priority.map_or(Value::Null, Value::Integer),
),
("TaskTime", draft.task_time.map_or(Value::Null, Value::Ref)),
("PredefinedType", optional_enum(draft.predefined_type)),
];
stage(tx, model, release, "IFCTASK", values, owner_history)
}
#[allow(clippy::too_many_arguments)]
pub fn create_sequence_with_owner_history(
tx: &mut Transaction,
model: &Model,
global_id: &str,
predecessor: EntityId,
successor: EntityId,
sequence_type: Option<&str>,
time_lag: Option<TimeLag>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
checks::sequence(global_id, predecessor, successor)?;
checks::time_lag(time_lag)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("RelatingProcess", Value::Ref(predecessor)),
("RelatedProcess", Value::Ref(successor)),
("TimeLag", time_lag.map_or(Value::Null, TimeLag::value)),
("SequenceType", optional_enum(sequence_type)),
];
stage(tx, model, release, "IFCRELSEQUENCE", values, owner_history)
}
pub fn create_work_control_with_owner_history(
tx: &mut Transaction,
model: &Model,
kind: WorkControlKind,
draft: WorkControlDraft<'_>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
let type_name = checks::work_control(kind, &draft)?;
let dates = [
Some(draft.creation_date),
Some(draft.start_time),
draft.finish_time,
];
let provisional = |slot: usize| {
dates[slot].map_or(Value::Null, |date: DateTimeValue<'_>| {
date.provisional(PLACEHOLDERS[slot])
})
};
let values = vec![
("OwnerHistory", Value::Ref(owner_history)),
("GlobalId", Value::Text(draft.global_id.into())),
("Name", optional_text(draft.name)),
("Description", optional_text(draft.description)),
("Identification", optional_text(draft.identification)),
("CreationDate", provisional(0)),
("Purpose", optional_text(draft.purpose)),
("Duration", optional_text(draft.duration)),
("TotalFloat", optional_text(draft.total_float)),
("StartTime", provisional(1)),
("FinishTime", provisional(2)),
("PredefinedType", optional_enum(draft.predefined_type)),
];
let mut record = release.record(type_name, values)?;
release.require_owner_history(tx, model, type_name, owner_history)?;
patch(tx, &mut record, &dates);
Ok(tx.create(record))
}
pub fn assign_tasks_to_control_with_owner_history(
tx: &mut Transaction,
model: &Model,
global_id: &str,
control: EntityId,
tasks: &[EntityId],
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
checks::assignment(global_id, tasks)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("RelatedObjects", refs(tasks)),
("RelatingControl", Value::Ref(control)),
];
stage(
tx,
model,
release,
"IFCRELASSIGNSTOCONTROL",
values,
owner_history,
)
}
pub fn nest_tasks_with_owner_history(
tx: &mut Transaction,
model: &Model,
global_id: &str,
parent: EntityId,
children: &[EntityId],
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
checks::nesting(global_id, parent, children)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("RelatingObject", Value::Ref(parent)),
("RelatedObjects", refs(children)),
];
stage(tx, model, release, "IFCRELNESTS", values, owner_history)
}
#[allow(clippy::too_many_arguments)]
pub fn create_work_calendar_with_owner_history(
tx: &mut Transaction,
model: &Model,
global_id: &str,
name: Option<&str>,
working_times: &[EntityId],
exception_times: &[EntityId],
predefined_type: Option<&str>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
checks::calendar(global_id, working_times, exception_times)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("Name", optional_text(name)),
("WorkingTimes", reference_list(working_times)),
("ExceptionTimes", reference_list(exception_times)),
("PredefinedType", optional_enum(predefined_type)),
];
stage(tx, model, release, "IFCWORKCALENDAR", values, owner_history)
}
pub fn create_event_with_owner_history(
tx: &mut Transaction,
model: &Model,
draft: EventDraft<'_>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
checks::event(&draft)?;
let values = vec![
("GlobalId", Value::Text(draft.global_id.into())),
("Name", optional_text(draft.name)),
("ObjectType", optional_text(draft.object_type)),
("Identification", optional_text(draft.identification)),
("LongDescription", optional_text(draft.long_description)),
("PredefinedType", optional_enum(draft.predefined_type)),
("EventTriggerType", optional_enum(draft.trigger_type)),
(
"UserDefinedEventTriggerType",
optional_text(draft.user_defined_trigger_type),
),
(
"EventOccurenceTime",
draft.occurence_time.map_or(Value::Null, Value::Ref),
),
];
stage(tx, model, release, "IFCEVENT", values, owner_history)
}
pub fn create_procedure_with_owner_history(
tx: &mut Transaction,
model: &Model,
draft: ProcedureDraft<'_>,
owner_history: EntityId,
) -> ScheduleAuthoringResult<EntityId> {
let release = bind(model)?;
procedure::check(&draft)?;
if release.version() == SchemaVersion::Ifc2x3
&& is_user_defined(draft.predefined_type)
&& blank(draft.user_defined_procedure_type)
{
return Err(ScheduleAuthoringError::InvalidValue {
entity: procedure::ENTITY,
attribute: "UserDefinedProcedureType",
expected: "a label when ProcedureType is USERDEFINED (IFC2X3 WR4)",
});
}
let values = vec![
("GlobalId", Value::Text(draft.global_id.into())),
("Name", optional_text(draft.name)),
("Description", optional_text(draft.description)),
("ObjectType", optional_text(draft.object_type)),
("Identification", optional_text(draft.identification)),
("LongDescription", optional_text(draft.long_description)),
("PredefinedType", optional_enum(draft.predefined_type)),
(
"UserDefinedProcedureType",
optional_text(draft.user_defined_procedure_type),
),
];
stage(tx, model, release, procedure::ENTITY, values, owner_history)
}