mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 17:57:02 +00:00
- BBIM 4D: features to review and edit Resource productivity data on the fly
- IOS: new resource utilitiy functions
This commit is contained in:
@@ -49,9 +49,12 @@ classes = (
|
|||||||
operator.DisableEditingResourceCostValue,
|
operator.DisableEditingResourceCostValue,
|
||||||
operator.CalculateResourceWork,
|
operator.CalculateResourceWork,
|
||||||
operator.ImportResources,
|
operator.ImportResources,
|
||||||
|
operator.EditProductivityData,
|
||||||
prop.Resource,
|
prop.Resource,
|
||||||
prop.BIMResourceProperties,
|
prop.BIMResourceProperties,
|
||||||
prop.BIMResourceTreeProperties,
|
prop.BIMResourceTreeProperties,
|
||||||
|
prop.ISODuration,
|
||||||
|
prop.BIMResourceProductivity,
|
||||||
ui.BIM_PT_resources,
|
ui.BIM_PT_resources,
|
||||||
ui.BIM_UL_resources,
|
ui.BIM_UL_resources,
|
||||||
)
|
)
|
||||||
@@ -60,8 +63,10 @@ classes = (
|
|||||||
def register():
|
def register():
|
||||||
bpy.types.Scene.BIMResourceProperties = bpy.props.PointerProperty(type=prop.BIMResourceProperties)
|
bpy.types.Scene.BIMResourceProperties = bpy.props.PointerProperty(type=prop.BIMResourceProperties)
|
||||||
bpy.types.Scene.BIMResourceTreeProperties = bpy.props.PointerProperty(type=prop.BIMResourceTreeProperties)
|
bpy.types.Scene.BIMResourceTreeProperties = bpy.props.PointerProperty(type=prop.BIMResourceTreeProperties)
|
||||||
|
bpy.types.Scene.BIMResourceProductivity = bpy.props.PointerProperty(type=prop.BIMResourceProductivity)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Scene.BIMResourceProperties
|
del bpy.types.Scene.BIMResourceProperties
|
||||||
del bpy.types.Scene.BIMResourceTreeProperties
|
del bpy.types.Scene.BIMResourceTreeProperties
|
||||||
|
del bpy.types.Scene.BIMResourceProductivity
|
||||||
|
|||||||
@@ -52,9 +52,25 @@ class ResourceData:
|
|||||||
if resource.BaseQuantity:
|
if resource.BaseQuantity:
|
||||||
base_quantity = resource.BaseQuantity.get_info()
|
base_quantity = resource.BaseQuantity.get_info()
|
||||||
del base_quantity["Unit"]
|
del base_quantity["Unit"]
|
||||||
results[resource.id()] = {"type": resource.is_a(), "BaseQuantity": base_quantity}
|
results[resource.id()] = {
|
||||||
|
"type": resource.is_a(),
|
||||||
|
"BaseQuantity": base_quantity,
|
||||||
|
}
|
||||||
|
if resource.is_a() in ["IfcLaborResource", "IfcConstructionEquipmentResource"]:
|
||||||
|
results[resource.id()]["Productivity"] = {}
|
||||||
|
productivity = cls.get_productivity(resource)
|
||||||
|
if productivity:
|
||||||
|
results[resource.id()]["Productivity"] = {
|
||||||
|
"QuantityProduced": ifcopenshell.util.resource.get_quantity_produced(productivity),
|
||||||
|
"TimeConsumed": ifcopenshell.util.resource.get_unit_consumed(productivity),
|
||||||
|
"QuantityProducedName": ifcopenshell.util.resource.get_quantity_produced_name(productivity),
|
||||||
|
}
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_productivity(cls, resource):
|
||||||
|
return ifcopenshell.util.resource.get_productivity(resource, should_inherit=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cost_values(cls):
|
def cost_values(cls):
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@@ -346,3 +346,13 @@ class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.import_resources(tool.Resource, file_path=self.filepath)
|
core.import_resources(tool.Resource, file_path=self.filepath)
|
||||||
|
|
||||||
|
|
||||||
|
class EditProductivityData(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.edit_productivity_data"
|
||||||
|
bl_description = "Apply"
|
||||||
|
bl_label = "Edit Productivity"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.edit_productivity_pset(tool.Ifc, tool.Resource)
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.resource
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim.module.pset.data
|
import blenderbim.bim.module.pset.data
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -71,6 +73,18 @@ def get_quantity_types(self, context):
|
|||||||
|
|
||||||
def update_active_resource_index(self, context):
|
def update_active_resource_index(self, context):
|
||||||
blenderbim.bim.module.pset.data.refresh()
|
blenderbim.bim.module.pset.data.refresh()
|
||||||
|
if self.should_show_productivity:
|
||||||
|
tool.Resource.load_productivity_data()
|
||||||
|
|
||||||
|
|
||||||
|
class ISODuration(PropertyGroup):
|
||||||
|
name: StringProperty(name="Name")
|
||||||
|
years: IntProperty(name="Years", default=0)
|
||||||
|
months: IntProperty(name="Months", default=0)
|
||||||
|
days: IntProperty(name="Days", default=0)
|
||||||
|
hours: IntProperty(name="Hours", default=0)
|
||||||
|
minutes: IntProperty(name="Minutes", default=0)
|
||||||
|
seconds: IntProperty(name="Seconds", default=0)
|
||||||
|
|
||||||
|
|
||||||
class Resource(PropertyGroup):
|
class Resource(PropertyGroup):
|
||||||
@@ -113,3 +127,11 @@ class BIMResourceProperties(PropertyGroup):
|
|||||||
quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types")
|
quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types")
|
||||||
is_editing_quantity: BoolProperty(name="Is Editing Quantity")
|
is_editing_quantity: BoolProperty(name="Is Editing Quantity")
|
||||||
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
||||||
|
should_show_productivity: BoolProperty(name="Edit Productivity", update=update_active_resource_index)
|
||||||
|
|
||||||
|
|
||||||
|
class BIMResourceProductivity(PropertyGroup):
|
||||||
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
|
quantity_consumed: CollectionProperty(name="Duration", type=ISODuration)
|
||||||
|
quantity_produced: FloatProperty(name="Quantity Produced")
|
||||||
|
quantity_produced_name: StringProperty(name="Quantity Produced Name")
|
||||||
@@ -65,7 +65,11 @@ class BIM_PT_resources(Panel):
|
|||||||
self.props,
|
self.props,
|
||||||
"active_resource_index",
|
"active_resource_index",
|
||||||
)
|
)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.alignment = "RIGHT"
|
||||||
|
row.prop(self.props, "should_show_productivity", icon="RECOVER_LAST")
|
||||||
|
if self.props.should_show_productivity:
|
||||||
|
self.draw_productivity_ui(context)
|
||||||
if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES":
|
if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES":
|
||||||
self.draw_editable_resource_attributes_ui()
|
self.draw_editable_resource_attributes_ui()
|
||||||
elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY":
|
elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY":
|
||||||
@@ -75,6 +79,45 @@ class BIM_PT_resources(Panel):
|
|||||||
elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
|
elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
|
||||||
self.draw_editable_resource_time_attributes_ui()
|
self.draw_editable_resource_time_attributes_ui()
|
||||||
|
|
||||||
|
def draw_productivity_ui(self, context):
|
||||||
|
total_resources = len(self.tprops.resources)
|
||||||
|
if not total_resources or self.props.active_resource_index >= total_resources:
|
||||||
|
return
|
||||||
|
|
||||||
|
ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id
|
||||||
|
resource = ResourceData.data["resources"][ifc_definition_id]
|
||||||
|
|
||||||
|
if not resource["type"] in ["IfcConstructionEquipmentResource", "IfcLaborResource"]:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Resource type cannot have productivity data", icon="ERROR")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.productivity_props = context.scene.BIMResourceProductivity
|
||||||
|
|
||||||
|
if resource["Productivity"]:
|
||||||
|
produtivitiy_rate_message = "Current Rate: {}/{}".format(
|
||||||
|
resource["Productivity"]["QuantityProduced"], resource["Productivity"]["TimeConsumed"]
|
||||||
|
)
|
||||||
|
row = self.layout.row()
|
||||||
|
row.alignment = "LEFT"
|
||||||
|
row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA")
|
||||||
|
else:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.alignment = "LEFT"
|
||||||
|
produtivitiy_rate_message = "No productivity data found"
|
||||||
|
row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA")
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.alignment = "RIGHT"
|
||||||
|
row.prop(self.productivity_props, "quantity_produced", text="Quantity Produced")
|
||||||
|
row.prop(self.productivity_props, "quantity_produced_name", text="Quantity Name")
|
||||||
|
row = self.layout.row()
|
||||||
|
row.alignment = "RIGHT"
|
||||||
|
self.draw_duration_property(self.productivity_props.quantity_consumed, row)
|
||||||
|
row = self.layout.row()
|
||||||
|
row.alignment = "RIGHT"
|
||||||
|
row.operator("bim.edit_productivity_data", text="Apply", icon="CHECKMARK")
|
||||||
|
|
||||||
def draw_resource_operators(self):
|
def draw_resource_operators(self):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT")
|
op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT")
|
||||||
@@ -111,8 +154,9 @@ class BIM_PT_resources(Panel):
|
|||||||
|
|
||||||
if not self.props.active_resource_id:
|
if not self.props.active_resource_id:
|
||||||
if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]:
|
if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]:
|
||||||
op = row.operator("bim.calculate_resource_work", text="", icon="TEMP")
|
if resource["Productivity"]:
|
||||||
op.resource = ifc_definition_id
|
op = row.operator("bim.calculate_resource_work", text="", icon="TEMP")
|
||||||
|
op.resource = ifc_definition_id
|
||||||
row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id
|
row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id
|
||||||
op = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES")
|
op = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES")
|
||||||
op.resource = ifc_definition_id
|
op.resource = ifc_definition_id
|
||||||
@@ -204,10 +248,20 @@ class BIM_PT_resources(Panel):
|
|||||||
op.parent = parent_id
|
op.parent = parent_id
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
|
|
||||||
|
def draw_duration_property(self, duration_props, layout):
|
||||||
|
for duration_prop in duration_props:
|
||||||
|
if duration_prop.name == "BaseQuantityConsumed":
|
||||||
|
layout.label(text=duration_prop.name)
|
||||||
|
layout.prop(duration_prop, "years", text="Y")
|
||||||
|
layout.prop(duration_prop, "months", text="M")
|
||||||
|
layout.prop(duration_prop, "days", text="D")
|
||||||
|
layout.prop(duration_prop, "hours", text="H")
|
||||||
|
layout.prop(duration_prop, "minutes", text="Min")
|
||||||
|
layout.prop(duration_prop, "seconds", text="S")
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_resources(UIList):
|
class BIM_UL_resources(UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
resource = ResourceData.data["resources"][item.ifc_definition_id]
|
|
||||||
icon_map = {
|
icon_map = {
|
||||||
"IfcSubContractResource": "TEXT",
|
"IfcSubContractResource": "TEXT",
|
||||||
"IfcCrewResource": "COMMUNITY",
|
"IfcCrewResource": "COMMUNITY",
|
||||||
@@ -217,6 +271,7 @@ class BIM_UL_resources(UIList):
|
|||||||
"IfcConstructionProductResource": "PACKAGE",
|
"IfcConstructionProductResource": "PACKAGE",
|
||||||
}
|
}
|
||||||
if item:
|
if item:
|
||||||
|
resource = ResourceData.data["resources"][item.ifc_definition_id]
|
||||||
props = context.scene.BIMResourceProperties
|
props = context.scene.BIMResourceProperties
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
for i in range(0, item.level_index):
|
for i in range(0, item.level_index):
|
||||||
|
|||||||
@@ -67,9 +67,11 @@ def parse_duration_as_blender_props(dt, simplify=True):
|
|||||||
|
|
||||||
|
|
||||||
def simplify_duration(durations_attributes, duration_type, prop_name):
|
def simplify_duration(durations_attributes, duration_type, prop_name):
|
||||||
for item in durations_attributes:
|
duration_props = None
|
||||||
if item.name == prop_name:
|
for collection in durations_attributes:
|
||||||
duration_props = item
|
if collection.name == prop_name:
|
||||||
|
duration_props = collection
|
||||||
|
break
|
||||||
if duration_props and not duration_type or duration_type == "ELAPSEDTIME":
|
if duration_props and not duration_type or duration_type == "ELAPSEDTIME":
|
||||||
duration_string = "P{}Y{}M{}DT{}H{}M{}S".format(
|
duration_string = "P{}Y{}M{}DT{}H{}M{}S".format(
|
||||||
duration_props.years if duration_props.years else 0,
|
duration_props.years if duration_props.years else 0,
|
||||||
@@ -114,11 +116,19 @@ def simplify_duration(durations_attributes, duration_type, prop_name):
|
|||||||
hours, seconds = divmod(seconds_left, 3600)
|
hours, seconds = divmod(seconds_left, 3600)
|
||||||
minutes, seconds = divmod(seconds, 60)
|
minutes, seconds = divmod(seconds, 60)
|
||||||
|
|
||||||
return "P{}Y{}M{}DT{}H{}M{}S".format(
|
duration_string = "P"
|
||||||
int(years),
|
if years > 0:
|
||||||
int(months),
|
duration_string += "{}Y".format(int(years))
|
||||||
int(days),
|
if months > 0:
|
||||||
int(hours),
|
duration_string += "{}M".format(int(months))
|
||||||
int(minutes),
|
if total_days > 0:
|
||||||
int(seconds),
|
duration_string += "{}D".format(int(total_days))
|
||||||
)
|
if hours > 0 or minutes > 0 or seconds > 0:
|
||||||
|
duration_string += "T"
|
||||||
|
if hours > 0:
|
||||||
|
duration_string += "{}H".format(int(hours))
|
||||||
|
if minutes > 0:
|
||||||
|
duration_string += "{}M".format(int(minutes))
|
||||||
|
if seconds > 0:
|
||||||
|
duration_string += "{}S".format(int(seconds))
|
||||||
|
return duration_string
|
||||||
|
|||||||
@@ -241,6 +241,22 @@ Scenario: Remove Resource Quantity
|
|||||||
When I press "bim.remove_resource_quantity(resource={labor_resource})"
|
When I press "bim.remove_resource_quantity(resource={labor_resource})"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Add Productivity data
|
||||||
|
Given an empty IFC project
|
||||||
|
When I press "bim.load_resources"
|
||||||
|
When I press "bim.add_resource(ifc_class="IfcCrewResource")"
|
||||||
|
And the variable "crew_resource" is "IfcStore.get_file().by_type('IfcCrewResource')[0].id()"
|
||||||
|
When I press "bim.add_resource(ifc_class="IfcLaborResource", parent_resource={crew_resource})"
|
||||||
|
And the variable "labor_resource" is "IfcStore.get_file().by_type('IfcLaborResource')[0].id()"
|
||||||
|
And I set "scene.BIMResourceProperties.active_resource_index" to "1"
|
||||||
|
And I set "scene.BIMResourceProperties.should_show_productivity" to "True"
|
||||||
|
And I set "scene.BIMResourceProductivity.quantity_produced" to "5.00"
|
||||||
|
And I set "scene.BIMResourceProductivity.quantity_produced_name" to "GrossSideArea"
|
||||||
|
And I set "scene.BIMResourceProductivity.quantity_consumed[0].hours" to "5"
|
||||||
|
When I press "bim.edit_productivity_data()"
|
||||||
|
And the variable "productivity_data" is "IfcStore.get_file().by_type('IfcPropertySet')[-1].id()"
|
||||||
|
|
||||||
|
|
||||||
Scenario: Calculate Resource Work
|
Scenario: Calculate Resource Work
|
||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
And I press "bim.add_work_schedule"
|
And I press "bim.add_work_schedule"
|
||||||
@@ -248,7 +264,7 @@ Scenario: Calculate Resource Work
|
|||||||
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
||||||
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||||
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||||
And I press "bim.enable_editing_task(task={task})"
|
And I press "bim.enable_editing_task_attributes(task={task})"
|
||||||
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
|
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
|
||||||
And I press "bim.edit_task"
|
And I press "bim.edit_task"
|
||||||
And I press "bim.enable_editing_task_time(task={task})"
|
And I press "bim.enable_editing_task_time(task={task})"
|
||||||
@@ -261,12 +277,23 @@ Scenario: Calculate Resource Work
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And the object "IfcWall/Cube" is selected
|
And the object "IfcWall/Cube" is selected
|
||||||
And I set "active_object.PsetProperties.qto_name" to "Qto_WallBaseQuantities"
|
And I set "active_object.PsetProperties.qto_name" to "Qto_WallBaseQuantities"
|
||||||
When I press "bim.add_pset(obj='IfcWall/Cube', obj_type='Object')"
|
When I press "bim.add_qto(obj='IfcWall/Cube', obj_type='Object')"
|
||||||
And the variable "qto" is "{ifc}.by_type('IfcElementQuantity')[-1].id()"
|
And I set "active_object.PsetProperties.properties[5].metadata.float_value" to "50.00"
|
||||||
And I press "bim.enable_pset_editing(obj='IfcWall/Cube', obj_type='Object', pset_id={qto})"
|
And I press "bim.edit_pset(obj='IfcWall/Cube', obj_type='Object')"
|
||||||
#And I set "active_object.PsetProperties.properties[2].metadata.float_value" to "3.00"
|
When I press "bim.load_resources"
|
||||||
# TO DO: complete
|
When I press "bim.add_resource(ifc_class="IfcCrewResource")"
|
||||||
Then nothing happens
|
And the variable "crew_resource" is "IfcStore.get_file().by_type('IfcCrewResource')[0].id()"
|
||||||
|
When I press "bim.add_resource(ifc_class="IfcLaborResource", parent_resource={crew_resource})"
|
||||||
|
And the variable "labor_resource" is "IfcStore.get_file().by_type('IfcLaborResource')[0].id()"
|
||||||
|
And I set "scene.BIMResourceProperties.active_resource_index" to "1"
|
||||||
|
And I set "scene.BIMResourceProperties.should_show_productivity" to "True"
|
||||||
|
And I set "scene.BIMResourceProductivity.quantity_produced" to "5.00"
|
||||||
|
And I set "scene.BIMResourceProductivity.quantity_produced_name" to "GrossSideArea"
|
||||||
|
And I set "scene.BIMResourceProductivity.quantity_consumed[0].hours" to "5"
|
||||||
|
When I press "bim.edit_productivity_data()"
|
||||||
|
And the variable "productivity_data" is "IfcStore.get_file().by_type('IfcPropertySet')[-1].id()"
|
||||||
|
When I press "bim.calculate_resource_work(resource={labor_resource})"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
|
||||||
Scenario: Assign Resource
|
Scenario: Assign Resource
|
||||||
|
|||||||
Reference in New Issue
Block a user