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 error;
mod owned;
mod release;
mod system_kind;
pub use distribution::{
create_distribution_element, create_distribution_element_with_owner_history,
create_spatial_zone, create_spatial_zone_with_owner_history, create_zone,
create_zone_with_owner_history, DistributionElementKind, ElementAttributes,
};
pub use error::{SystemAuthoringError, SystemAuthoringResult};
pub use owned::{
assign_to_group_with_owner_history, connect_port_to_element_with_owner_history,
connect_ports_with_owner_history, contain_in_spatial_structure_with_owner_history,
create_group_with_owner_history, create_port_with_owner_history,
create_system_with_owner_history, nest_ports_with_owner_history,
reference_in_spatial_structure_with_owner_history,
};
pub use system_kind::{
create_classified_system, create_classified_system_with_owner_history, ClassifiedSystemDraft,
SystemKind,
};
use error::invalid;
fn guid(entity: &'static str, global_id: &str) -> SystemAuthoringResult<()> {
if Guid::parse(global_id).is_none() {
return Err(invalid(entity, "GlobalId", global_id));
}
Ok(())
}
fn check_related(
entity: &'static str,
attribute: &'static str,
global_id: &str,
relating: EntityId,
related: &[EntityId],
relating_word: &str,
) -> SystemAuthoringResult<()> {
guid(entity, global_id)?;
if related.is_empty() {
return Err(invalid(entity, attribute, "empty"));
}
if related.contains(&relating) {
return Err(invalid(
entity,
attribute,
format!("contains the {relating_word}"),
));
}
Ok(())
}
fn check_ports(
global_id: &str,
relating: EntityId,
related: EntityId,
) -> SystemAuthoringResult<()> {
guid("IFCRELCONNECTSPORTS", global_id)?;
if relating == related {
return Err(invalid(
"IFCRELCONNECTSPORTS",
"RelatedPort",
"same as RelatingPort",
));
}
Ok(())
}
fn refs(ids: &[EntityId]) -> Value {
Value::List(ids.iter().copied().map(Value::Ref).collect())
}
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> {
check_related(
"IFCRELASSIGNSTOGROUP",
"RelatedObjects",
global_id,
group,
members,
"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] = refs(members);
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> {
check_related(
"IFCRELNESTS",
"RelatedObjects",
global_id,
parent,
children,
"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] = refs(children);
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> {
check_ports(global_id, relating, related)?;
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> {
check_related(
entity,
"RelatedElements",
global_id,
structure,
elements,
"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] = refs(elements);
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)))
}