diff --git a/src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc b/src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc new file mode 100644 index 0000000000..283946eb80 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/data/pset/EPset_Productivity.ifc @@ -0,0 +1,13 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION((),'2;1'); +FILE_NAME('EPset_Productivity.ifc','2020-01-01T00:00:00',(),(),'EPset_Productivity','EPset_Productivity',$); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCPROPERTYSETTEMPLATE('24XxTA2ED4o83uiZBG59D_',$,'EPset_Productivity','',.PSET_TYPEDRIVENOVERRIDE.,'IfcConstructionEquipmentResource,IfcLaborResource',(#2,#3,#4)); +#2=IFCSIMPLEPROPERTYTEMPLATE('3mL27FQ4X4MQn5fiGqIt8x',$,'BaseQuantityConsumed','',.P_SINGLEVALUE.,'IfcDuration',$,$,$,$,$,.READWRITE.); +#3=IFCSIMPLEPROPERTYTEMPLATE('1DdJt5_Ar2mR4UDpO$98Ws',$,'BaseQuantityProducedName','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); +#4=IFCSIMPLEPROPERTYTEMPLATE('2OZPZ$Onb2gOvW20q7gacH',$,'BaseQuantityProducedValue','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.); +ENDSEC; +END-ISO-10303-21; diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index 8794902494..cb2f056939 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -36,6 +36,7 @@ classes = ( operator.EnableEditingResourceTime, operator.EditResourceTime, operator.DisableEditingResourceTime, + operator.CalculateResourceWork, prop.Resource, prop.BIMResourceProperties, prop.BIMResourceTreeProperties, diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 34b70a80f4..34dbdd12be 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -371,3 +371,17 @@ class EditResourceTime(bpy.types.Operator): return True attributes[prop.name] = helper.parse_duration(prop.string_value) return True + + +class CalculateResourceWork(bpy.types.Operator): + bl_idname = "bim.calculate_resource_work" + bl_label = "Calculate Resource Work" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=self.file.by_id(self.resource)) + Data.load(self.file) + bpy.ops.bim.load_resources() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index 8c63c25cab..ec10d592df 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -75,13 +75,21 @@ class BIM_PT_resources(Panel): and Data.resources[self.tprops.resources[self.props.active_resource_index].ifc_definition_id]["type"] != "IfcSubContractResource" ): + ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id + row = self.layout.row(align=True) for ifc_class, icon in icon_map.items(): label = ifc_class.replace("Ifc", "").replace("Construction", "").replace("Resource", "") op = row.operator("bim.add_resource", text=label, icon=icon) - op.resource = self.tprops.resources[self.props.active_resource_index].ifc_definition_id + op.resource = ifc_definition_id op.ifc_class = ifc_class + row = self.layout.row(align=True) + row.alignment = "RIGHT" + + op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") + op.resource = ifc_definition_id + self.layout.template_list( "BIM_UL_resources", "", diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 1997e93671..2522c36759 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -98,7 +98,7 @@ classes = ( operator.LoadTaskInputs, operator.LoadTaskResources, operator.LoadTaskOutputs, - operator.DeriveTaskDuration, + operator.CalculateTaskDuration, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 00c0e8bfd3..b908d80bab 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -797,6 +797,7 @@ class AssignProduct(bpy.types.Operator): related_object=self.file.by_id(self.task), ) Data.load(self.file) + bpy.ops.bim.load_task_outputs() return {"FINISHED"} @@ -825,6 +826,7 @@ class UnassignProduct(bpy.types.Operator): related_object=self.file.by_id(self.task), ) Data.load(self.file) + bpy.ops.bim.load_task_outputs() return {"FINISHED"} @@ -2106,9 +2108,9 @@ class LoadTaskOutputs(bpy.types.Operator): return {"FINISHED"} -class DeriveTaskDuration(bpy.types.Operator): - bl_idname = "bim.derive_task_duration" - bl_label = "Derive Task Duration" +class CalculateTaskDuration(bpy.types.Operator): + bl_idname = "bim.calculate_task_duration" + bl_label = "Calculate Task Duration" bl_options = {"REGISTER", "UNDO"} task: bpy.props.IntProperty() diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 42dea09f5e..37c4bd138e 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -371,7 +371,7 @@ class BIM_PT_task_icom(Panel): row2 = col.row(align=True) row2.label(text="Resources") - op = row2.operator("bim.derive_task_duration", text="", icon="TIME") + op = row2.operator("bim.calculate_task_duration", text="", icon="TEMP") op.task = task.ifc_definition_id total_resources = len(context.scene.BIMResourceTreeProperties.resources) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py new file mode 100644 index 0000000000..aa68a8fe54 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py @@ -0,0 +1,56 @@ +import math +import ifcopenshell.api +import ifcopenshell.util.date +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.productivity = ifcopenshell.util.element.get_psets(self.settings["resource"]).get( + "EPset_Productivity", None + ) + if not self.productivity: + return + unit_consumed = self.get_unit_consumed() + self.unit_produced_name = self.productivity.get("BaseQuantityProducedName", None) + unit_produced = self.productivity.get("BaseQuantityProducedValue", None) + total_produced = self.get_total_produced() + if not unit_consumed or not unit_produced or not total_produced: + return + seconds_worked = total_produced / unit_produced * unit_consumed + self.set_schedule_work(seconds_worked) + + def get_unit_consumed(self): + duration = self.productivity.get("BaseQuantityConsumed", None) + if not duration: + return + return ifcopenshell.util.date.ifc2datetime(duration).seconds + + def get_total_produced(self): + total = 0 + for rel in self.settings["resource"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProcess"): + continue + for rel2 in rel.RelatingProcess.HasAssignments or []: + if not rel2.is_a("IfcRelAssignsToProduct"): + continue + psets = ifcopenshell.util.element.get_psets(rel2.RelatingProduct) + for pset in psets.values(): + for name, value in pset.items(): + if name == self.unit_produced_name: + try: + total += float(value) + except: + pass + return total + + def set_schedule_work(self, seconds): + if not self.settings["resource"].Usage: + ifcopenshell.api.run("resource.add_resource_time", self.file, resource=self.settings["resource"]) + self.settings["resource"].Usage.ScheduleWork = f"PT{seconds / 60 / 60}H"