use ifc_model::guid::Guid;
use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::connectivity::relation::slot as connects_slot;
use crate::port::definition::slot as port_slot;
use crate::system::group::slot as group_slot;
use crate::zone::spatial_group::slot as placement_slot;
mod distribution;
mod system_kind;
pub use distribution::{
create_distribution_element, create_spatial_zone, create_zone, DistributionElementKind,
ElementAttributes,
};
pub use system_kind::{create_classified_system, ClassifiedSystemDraft, SystemKind};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum SystemAuthoringError {
Invalid {
entity: &'static str,
attribute: &'static str,
value: String,
},
}
impl std::fmt::Display for SystemAuthoringError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Self::Invalid {
entity,
attribute,
value,
} = self;
write!(f, "{entity}.{attribute}: {value}")
}
}
impl std::error::Error for SystemAuthoringError {}
pub type SystemAuthoringResult<T> = Result<T, SystemAuthoringError>;
fn invalid(
entity: &'static str,
attribute: &'static str,
value: impl Into<String>,
) -> SystemAuthoringError {
SystemAuthoringError::Invalid {
entity,
attribute,
value: value.into(),
}
}
fn guid(entity: &'static str, global_id: &str) -> SystemAuthoringResult<()> {
if Guid::parse(global_id).is_none() {
return Err(invalid(entity, "GlobalId", global_id));
}
Ok(())
}
pub fn create_system(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
guid("IFCSYSTEM", global_id)?;
let mut attributes = vec![Value::Null; 5];
attributes[0] = Value::Text(global_id.into());
attributes[2] = name.map_or(Value::Null, |t| Value::Text(t.into()));
Ok(tx.create(Entity::new("IFCSYSTEM", attributes)))
}
pub fn create_port(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
flow_direction: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
guid("IFCDISTRIBUTIONPORT", global_id)?;
let mut attributes = vec![Value::Null; 10];
attributes[0] = Value::Text(global_id.into());
attributes[2] = name.map_or(Value::Null, |t| Value::Text(t.into()));
attributes[7] = flow_direction.map_or(Value::Null, |t| Value::Enum(t.into()));
Ok(tx.create(Entity::new("IFCDISTRIBUTIONPORT", attributes)))
}
pub fn assign_to_group(
tx: &mut Transaction,
global_id: &str,
group: EntityId,
members: &[EntityId],
) -> SystemAuthoringResult<EntityId> {
guid("IFCRELASSIGNSTOGROUP", global_id)?;
if members.is_empty() {
return Err(invalid("IFCRELASSIGNSTOGROUP", "RelatedObjects", "empty"));
}
if members.contains(&group) {
return Err(invalid(
"IFCRELASSIGNSTOGROUP",
"RelatedObjects",
"contains the group",
));
}
let width = group_slot::ASSIGNS_GROUP.max(group_slot::ASSIGNS_RELATED) + 1;
let mut attributes = vec![Value::Null; width];
attributes[0] = Value::Text(global_id.into());
attributes[group_slot::ASSIGNS_RELATED] =
Value::List(members.iter().copied().map(Value::Ref).collect());
attributes[group_slot::ASSIGNS_GROUP] = Value::Ref(group);
Ok(tx.create(Entity::new("IFCRELASSIGNSTOGROUP", attributes)))
}
pub fn nest_ports(
tx: &mut Transaction,
global_id: &str,
parent: EntityId,
children: &[EntityId],
) -> SystemAuthoringResult<EntityId> {
guid("IFCRELNESTS", global_id)?;
if children.is_empty() {
return Err(invalid("IFCRELNESTS", "RelatedObjects", "empty"));
}
if children.contains(&parent) {
return Err(invalid(
"IFCRELNESTS",
"RelatedObjects",
"contains the parent",
));
}
let width = port_slot::NESTS_PARENT.max(port_slot::NESTS_CHILDREN) + 1;
let mut attributes = vec![Value::Null; width];
attributes[0] = Value::Text(global_id.into());
attributes[port_slot::NESTS_PARENT] = Value::Ref(parent);
attributes[port_slot::NESTS_CHILDREN] =
Value::List(children.iter().copied().map(Value::Ref).collect());
Ok(tx.create(Entity::new("IFCRELNESTS", attributes)))
}
pub fn connect_port_to_element(
tx: &mut Transaction,
global_id: &str,
port: EntityId,
element: EntityId,
) -> SystemAuthoringResult<EntityId> {
guid("IFCRELCONNECTSPORTTOELEMENT", global_id)?;
let width = port_slot::PORT_TO_ELEMENT_PORT.max(port_slot::PORT_TO_ELEMENT_ELEMENT) + 1;
let mut attributes = vec![Value::Null; width];
attributes[0] = Value::Text(global_id.into());
attributes[port_slot::PORT_TO_ELEMENT_PORT] = Value::Ref(port);
attributes[port_slot::PORT_TO_ELEMENT_ELEMENT] = Value::Ref(element);
Ok(tx.create(Entity::new("IFCRELCONNECTSPORTTOELEMENT", attributes)))
}
pub fn connect_ports(
tx: &mut Transaction,
global_id: &str,
relating: EntityId,
related: EntityId,
realizing: Option<EntityId>,
) -> SystemAuthoringResult<EntityId> {
guid("IFCRELCONNECTSPORTS", global_id)?;
if relating == related {
return Err(invalid(
"IFCRELCONNECTSPORTS",
"RelatedPort",
"same as RelatingPort",
));
}
let width = connects_slot::REALIZING + 1;
let mut attributes = vec![Value::Null; width];
attributes[0] = Value::Text(global_id.into());
attributes[connects_slot::RELATING] = Value::Ref(relating);
attributes[connects_slot::RELATED] = Value::Ref(related);
attributes[connects_slot::REALIZING] = realizing.map_or(Value::Null, Value::Ref);
Ok(tx.create(Entity::new("IFCRELCONNECTSPORTS", attributes)))
}
pub fn contain_in_spatial_structure(
tx: &mut Transaction,
global_id: &str,
structure: EntityId,
elements: &[EntityId],
) -> SystemAuthoringResult<EntityId> {
place(
tx,
"IFCRELCONTAINEDINSPATIALSTRUCTURE",
global_id,
structure,
elements,
)
}
pub fn reference_in_spatial_structure(
tx: &mut Transaction,
global_id: &str,
structure: EntityId,
elements: &[EntityId],
) -> SystemAuthoringResult<EntityId> {
place(
tx,
"IFCRELREFERENCEDINSPATIALSTRUCTURE",
global_id,
structure,
elements,
)
}
fn place(
tx: &mut Transaction,
entity: &'static str,
global_id: &str,
structure: EntityId,
elements: &[EntityId],
) -> SystemAuthoringResult<EntityId> {
guid(entity, global_id)?;
if elements.is_empty() {
return Err(invalid(entity, "RelatedElements", "empty"));
}
if elements.contains(&structure) {
return Err(invalid(entity, "RelatedElements", "contains the structure"));
}
let width = placement_slot::RELATING_STRUCTURE.max(placement_slot::RELATED_ELEMENTS) + 1;
let mut attributes = vec![Value::Null; width];
attributes[0] = Value::Text(global_id.into());
attributes[placement_slot::RELATED_ELEMENTS] =
Value::List(elements.iter().copied().map(Value::Ref).collect());
attributes[placement_slot::RELATING_STRUCTURE] = Value::Ref(structure);
Ok(tx.create(Entity::new(entity, attributes)))
}
pub fn create_group(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
description: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
guid("IFCGROUP", global_id)?;
let mut attributes = vec![Value::Null; 5];
attributes[0] = Value::Text(global_id.into());
attributes[2] = name.map_or(Value::Null, |t| Value::Text(t.into()));
attributes[3] = description.map_or(Value::Null, |t| Value::Text(t.into()));
Ok(tx.create(Entity::new("IFCGROUP", attributes)))
}