mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
UI for assigning controls to flow elements #3981
Example - https://imgur.com/a/CZQIoHp
This commit is contained in:
@@ -24,6 +24,7 @@ classes = (
|
||||
operator.AddSystem,
|
||||
operator.AddZone,
|
||||
operator.AssignSystem,
|
||||
operator.AssignUnassignFlowControl,
|
||||
operator.ConnectPort,
|
||||
operator.DisableEditingSystem,
|
||||
operator.DisableEditingZone,
|
||||
@@ -54,6 +55,7 @@ classes = (
|
||||
ui.BIM_PT_active_object_zones,
|
||||
ui.BIM_PT_ports,
|
||||
ui.BIM_PT_port,
|
||||
ui.BIM_PT_flow_controls,
|
||||
ui.BIM_UL_systems,
|
||||
ui.BIM_UL_zones,
|
||||
)
|
||||
|
||||
@@ -72,6 +72,7 @@ class ObjectSystemData:
|
||||
"systems": cls.systems(),
|
||||
# AFTER SYSTEMS
|
||||
"connected_elements": cls.connected_elements(),
|
||||
"flow_controls_data": cls.flow_controls_data(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -91,6 +92,26 @@ class ObjectSystemData:
|
||||
return set()
|
||||
return tool.System.get_connected_elements(cls.element)
|
||||
|
||||
@classmethod
|
||||
def flow_controls_data(cls):
|
||||
flow_controls_data = {}
|
||||
if not cls.element or not (
|
||||
cls.element.is_a("IfcDistributionControlElement") or cls.element.is_a("IfcDistributionFlowElement")
|
||||
):
|
||||
return flow_controls_data
|
||||
|
||||
if cls.element.is_a("IfcDistributionControlElement"):
|
||||
flow_controls_data["type"] = "IfcDistributionControlElement"
|
||||
flow_element = tool.System.get_flow_control_flow_element(cls.element)
|
||||
flow_element_obj = tool.Ifc.get_object(flow_element).name if flow_element else None
|
||||
flow_controls_data["flow_element"] = flow_element, flow_element_obj
|
||||
else:
|
||||
flow_controls_data["type"] = "IfcDistributionFlowElement"
|
||||
controls = [(c, tool.Ifc.get_object(c).name) for c in tool.System.get_flow_element_controls(cls.element)]
|
||||
flow_controls_data["controls"] = controls
|
||||
|
||||
return flow_controls_data
|
||||
|
||||
|
||||
class PortData:
|
||||
data = {}
|
||||
|
||||
@@ -421,3 +421,67 @@ class RemoveZone(bpy.types.Operator, Operator):
|
||||
def _execute(self, context):
|
||||
ifcopenshell.api.run("system.remove_system", tool.Ifc.get(), system=tool.Ifc.get().by_id(self.zone))
|
||||
bpy.ops.bim.load_zones()
|
||||
|
||||
|
||||
class AssignUnassignFlowControl(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.assign_unassign_flow_control"
|
||||
bl_label = "Assign/Unassign Flow Control"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
flow_element: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
flow_control: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
assign: bpy.props.BoolProperty(name="Assign/Unassign", default=True, options={"SKIP_SAVE"})
|
||||
|
||||
def _execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
flow_element = None
|
||||
flow_controls = []
|
||||
from_selected_objects = False
|
||||
|
||||
if self.flow_element != 0:
|
||||
flow_element = ifc_file.by_id(self.flow_element)
|
||||
if self.flow_control != 0:
|
||||
flow_controls = [ifc_file.by_id(self.flow_control)]
|
||||
|
||||
# if not provided as arguments tried to get them from
|
||||
# the selected objects
|
||||
if not flow_element or flow_controls:
|
||||
from_selected_objects = True
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
if element.is_a("IfcDistributionControlElement") and self.flow_control == 0:
|
||||
flow_controls.append(element)
|
||||
elif element.is_a("IfcDistributionFlowElement") and self.flow_element == 0:
|
||||
if flow_element:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"More than one flow element selected. Control can be assigned to only 1 flow element.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
flow_element = element
|
||||
|
||||
if not flow_element:
|
||||
self.report({"ERROR"}, "No flow element selected.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if not flow_controls:
|
||||
self.report({"ERROR"}, "No flow controls selected.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
for control in flow_controls:
|
||||
if self.assign:
|
||||
tool.Ifc.run(
|
||||
"system.assign_flow_control", relating_flow_element=flow_element, related_flow_control=control
|
||||
)
|
||||
else:
|
||||
tool.Ifc.run(
|
||||
"system.unassign_flow_control", relating_flow_element=flow_element, related_flow_control=control
|
||||
)
|
||||
|
||||
if from_selected_objects:
|
||||
self.report(
|
||||
{"INFO"}, f"{len(flow_controls)} flow controls were {'assigned' if self.assign else 'unassigned'}."
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
@@ -272,6 +273,68 @@ class BIM_PT_port(Panel):
|
||||
).direction = flow_direction
|
||||
|
||||
|
||||
class BIM_PT_flow_controls(Panel):
|
||||
bl_label = "Flow Controls"
|
||||
bl_idname = "BIM_PT_flow_controls"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
bl_order = 1
|
||||
bl_parent_id = "BIM_PT_tab_services_object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
if not element or not (
|
||||
element.is_a("IfcDistributionControlElement") or element.is_a("IfcDistributionFlowElement")
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectSystemData.is_loaded:
|
||||
ObjectSystemData.load()
|
||||
|
||||
def display_element(control_id, flow_element_id, displayed_object_name):
|
||||
displayed_object = bpy.data.objects[displayed_object_name]
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.assign_unassign_flow_control", text="", icon="X")
|
||||
op.flow_control = control_id
|
||||
op.flow_element = flow_element_id
|
||||
op.assign = False
|
||||
row.operator(
|
||||
"bim.select_entity", text="", icon="RESTRICT_SELECT_OFF"
|
||||
).ifc_id = displayed_object.BIMObjectProperties.ifc_definition_id
|
||||
row.label(text=f"{displayed_object_name}")
|
||||
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
flow_controls_data = ObjectSystemData.data["flow_controls_data"]
|
||||
if flow_controls_data["type"] == "IfcDistributionFlowElement":
|
||||
controls = flow_controls_data["controls"]
|
||||
row = self.layout.row(align=True)
|
||||
if controls:
|
||||
row.label(text="Controls assigned to the element:")
|
||||
else:
|
||||
row.label(text="No controls assigned to the flow element")
|
||||
row.operator("bim.assign_unassign_flow_control", text="", icon="ADD").assign = True
|
||||
if controls:
|
||||
for control_data in controls:
|
||||
control, control_obj_name = control_data
|
||||
display_element(control.id(), element.id(), control_obj_name)
|
||||
else:
|
||||
flow_element, flow_element_obj_name = flow_controls_data["flow_element"]
|
||||
row = self.layout.row(align=True)
|
||||
if flow_element:
|
||||
row.label(text="Flow element controlled by the flow control:")
|
||||
display_element(element.id(), flow_element.id(), flow_element_obj_name)
|
||||
else:
|
||||
row.label(text="No flow element controlled by the flow control")
|
||||
row.operator("bim.assign_unassign_flow_control", text="", icon="ADD").assign = True
|
||||
|
||||
|
||||
class BIM_PT_zones(Panel):
|
||||
bl_label = "Zones"
|
||||
bl_idname = "BIM_PT_zones"
|
||||
|
||||
@@ -368,3 +368,15 @@ class System(blenderbim.core.tool.System):
|
||||
@classmethod
|
||||
def is_mep_element(cls, element):
|
||||
return element.is_a("IfcFlowSegment") or element.is_a("IfcFlowFitting")
|
||||
|
||||
@classmethod
|
||||
def get_flow_element_controls(cls, element):
|
||||
if not element.HasControlElements:
|
||||
return []
|
||||
return [control for control in element.HasControlElements[0].RelatedControlElements]
|
||||
|
||||
@classmethod
|
||||
def get_flow_control_flow_element(cls, element):
|
||||
if not element.AssignedToFlowElement:
|
||||
return
|
||||
return element.AssignedToFlowElement[0].RelatingFlowElement
|
||||
|
||||
@@ -101,3 +101,28 @@ Scenario: Select system products
|
||||
And I press "bim.assign_system(system={system})"
|
||||
When I press "bim.select_system_products(system={system})"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Assign flow controls to flow element_type
|
||||
Given an empty IFC project
|
||||
And I add an empty
|
||||
And the object "Empty" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcActuator"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
And the object "Empty" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcActuator"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
And the object "Empty" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcFlowSegment"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcActuator/Empty" is selected
|
||||
And additionally the object "IfcActuator/Empty.001" is selected
|
||||
And additionally the object "IfcFlowSegment/Empty" is selected
|
||||
When I press "bim.assign_unassign_flow_control(assign=True)"
|
||||
And the variable "assigned_controls" is "set(tool.System.get_flow_element_controls({ifc}.by_type('IfcFlowSegment')[0]))"
|
||||
Then the variable "assigned_controls" is "set[{ifc}.by_type('IfcActuator')]"
|
||||
|
||||
|
||||
@@ -277,3 +277,30 @@ class TestSetActiveSystem(NewFile):
|
||||
system = ifcopenshell.api.run("system.add_system", ifc, ifc_class="IfcSystem")
|
||||
subject.set_active_edited_system(system)
|
||||
assert bpy.context.scene.BIMSystemProperties.edited_system_id == system.id()
|
||||
|
||||
|
||||
class TestFlowElementAndControls(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
flow_element = ifc.createIfcFlowSegment()
|
||||
flow_control = ifc.createIfcController()
|
||||
flow_control1 = ifc.createIfcController()
|
||||
|
||||
assert len(subject.get_flow_element_controls(flow_element)) == 0
|
||||
assert subject.get_flow_control_flow_element(flow_control) == None
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"system.assign_flow_control",
|
||||
ifc,
|
||||
related_flow_control=flow_control,
|
||||
relating_flow_element=flow_element,
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"system.assign_flow_control",
|
||||
ifc,
|
||||
related_flow_control=flow_control1,
|
||||
relating_flow_element=flow_element,
|
||||
)
|
||||
controls = subject.get_flow_element_controls(flow_element)
|
||||
assert set(controls) == set((flow_control, flow_control1))
|
||||
assert subject.get_flow_control_flow_element(flow_control) == flow_element
|
||||
|
||||
Reference in New Issue
Block a user