diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py new file mode 100644 index 0000000000..4517a42421 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py @@ -0,0 +1,85 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, relating_flow_element=None, related_flow_control=None): + """Assigns to the flow element control element that either sense or control + some aspect of the flow element. + + Note that control can be assigned only to the one flow element. + + :param related_flow_control: IfcDistributionControlElement + which may be used to impart control on the flow element + :type related_flow_control: ifcopenshell.entity_instance.entity_instance + :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed + :type relating_flow_element: ifcopenshell.entity_instance.entity_instance + :return: Matching or newly created IfcRelFlowControlElements. If control + is already assigned to some other element method will return None. + :rtype: ifcopenshell.entity_instance.entity_instance, None + + Example: + + .. code:: python + + flow_element = model.createIfcFlowSegment() + flow_control = model.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", model, + related_flow_control=flow_control, relating_flow_element=flow_element + ) + """ + self.file = file + self.settings = { + "relating_flow_element": relating_flow_element, + "related_flow_control": related_flow_control, + } + + def execute(self): + if self.settings["related_flow_control"].AssignedToFlowElement: + # only 1 control per 1 flow element is possible + assignment = self.settings["related_flow_control"].AssignedToFlowElement[0] + if assignment.RelatingFlowElement == self.settings["relating_flow_element"]: + return assignment + # return None if this control is already assigned to another flow element + return + + if self.settings["relating_flow_element"].HasControlElements: + assignment = self.settings["relating_flow_element"].HasControlElements[0] + if self.settings["related_flow_control"] in assignment.RelatedControlElements: + return assignment + + related_flow_controls = set(assignment.RelatedControlElements) + related_flow_controls.add(self.settings["related_flow_control"]) + assignment.RelatedControlElements = list(related_flow_controls) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment}) + return assignment + + assignment = self.file.create_entity( + "IfcRelFlowControlElements", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), + "RelatedControlElements": [self.settings["related_flow_control"]], + "RelatingFlowElement": self.settings["relating_flow_element"], + }, + ) + return assignment diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py new file mode 100644 index 0000000000..63e3c58c6a --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py @@ -0,0 +1,72 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, relating_flow_element=None, related_flow_control=None): + """Unassigns flow control element from the flow element. + + :param related_flow_control: IfcDistributionControlElement controling the + flow element + :type related_flow_control: ifcopenshell.entity_instance.entity_instance + :param relating_flow_element: The IfcDistributionFlowElement that is being controlled + :type relating_flow_element: ifcopenshell.entity_instance.entity_instance + :return: If the control still is related to other objects, the + IfcRelFlowControlElements is returned, otherwise None. + :rtype: ifcopenshell.entity_instance.entity_instance, None + + Example: + + .. code:: python + + # assign control to the flow element + flow_element = self.file.createIfcFlowSegment() + flow_control = self.file.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", self.file, + relating_control=flow_control, related_object=flow_element + ) + + # und unassign it + ifcopenshell.api.run("system.unassign_flow_control", self.file, + relating_control=flow_control, related_object=flow_element + ) + """ + + self.file = file + self.settings = { + "relating_flow_element": relating_flow_element, + "related_flow_control": related_flow_control, + } + + def execute(self): + if not self.settings["related_flow_control"].AssignedToFlowElement: + return + assignment = self.settings["related_flow_control"].AssignedToFlowElement[0] + if assignment.RelatingFlowElement != self.settings["relating_flow_element"]: + return + if len(assignment.RelatedControlElements) == 1: + return self.file.remove(assignment) + related_flow_controls = list(assignment.RelatedControlElements) + related_flow_controls.remove(self.settings["related_flow_control"]) + assignment.RelatedControlElements = related_flow_controls + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment}) + return assignment diff --git a/src/ifcopenshell-python/test/api/system/test_assign_flow_control.py b/src/ifcopenshell-python/test/api/system/test_assign_flow_control.py new file mode 100644 index 0000000000..ac78e66633 --- /dev/null +++ b/src/ifcopenshell-python/test/api/system/test_assign_flow_control.py @@ -0,0 +1,68 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +class TestAssignFlowControl(test.bootstrap.IFC4): + def test_run(self): + flow_element = self.file.createIfcFlowSegment() + flow_control = self.file.createIfcController() + + # simple assignment + relation = ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control, + relating_flow_element=flow_element, + ) + assert len(self.file.by_type("IfcRelFlowControlElements")) == 1 + assert relation.RelatingFlowElement == flow_element + assert relation.RelatedControlElements == (flow_control,) + + # trying to establish existing relationship + relation0 = ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control, + relating_flow_element=flow_element, + ) + assert relation == relation0 + + # assigning same control to another object + flow_element1 = self.file.createIfcFlowSegment() + relation = ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control, + relating_flow_element=flow_element1, + ) + assert relation is None + + # assigning another control to the same object + flow_control1 = self.file.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control1, + relating_flow_element=flow_element, + ) + assert len(self.file.by_type("IfcRelFlowControlElements")) == 1 + assert relation.RelatingFlowElement == flow_element + assert set(relation.RelatedControlElements) == set((flow_control, flow_control1)) diff --git a/src/ifcopenshell-python/test/api/system/test_unassign_flow_control.py b/src/ifcopenshell-python/test/api/system/test_unassign_flow_control.py new file mode 100644 index 0000000000..bb881367e6 --- /dev/null +++ b/src/ifcopenshell-python/test/api/system/test_unassign_flow_control.py @@ -0,0 +1,64 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +class TestUnassignFlowControl(test.bootstrap.IFC4): + def test_run(self): + flow_element = self.file.createIfcFlowSegment() + flow_control = self.file.createIfcController() + + # assign and unassign + relation = ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control, + relating_flow_element=flow_element, + ) + ifcopenshell.api.run( + "system.unassign_flow_control", + self.file, + related_flow_control=flow_control, + relating_flow_element=flow_element, + ) + assert len(self.file.by_type("IfcRelFlowControlElements")) == 0 + + # 1 element 2 controls + flow_control1 = self.file.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control, + relating_flow_element=flow_element, + ) + ifcopenshell.api.run( + "system.assign_flow_control", + self.file, + related_flow_control=flow_control1, + relating_flow_element=flow_element, + ) + ifcopenshell.api.run( + "system.unassign_flow_control", + self.file, + related_flow_control=flow_control1, + relating_flow_element=flow_element, + ) + assert len(self.file.by_type("IfcRelFlowControlElements")) == 1 + assert relation.RelatedControlElements == (flow_control,)