implement calculating task duration based on resource usage change for fixed person-hours and prevent constrained attributes from updating

This commit is contained in:
Sigma Dimensions (Yass)
2023-08-23 02:19:32 +01:00
parent 839e35f1da
commit 66f5081ada
5 changed files with 44 additions and 70 deletions
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on # BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com> # Copyright (C) 2021, 2022, 2023 Dion Moult, Yassine Oualid <dion@thinkmoult.com>
# #
# This file is part of BlenderBIM Add-on. # This file is part of BlenderBIM Add-on.
# #
@@ -22,8 +22,8 @@ import ifcopenshell.util.resource
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.module.pset.data import blenderbim.bim.module.pset.data
from blenderbim.bim.module.resource.data import refresh import blenderbim.bim.module.resource.data
from blenderbim.bim.module.sequence.data import refresh as refresh_sequence import blenderbim.bim.module.sequence.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
from bpy.props import ( from bpy.props import (
@@ -58,7 +58,7 @@ def updateResourceName(self, context):
if props.active_resource_id == self.ifc_definition_id: if props.active_resource_id == self.ifc_definition_id:
attribute = props.resource_attributes.get("Name") attribute = props.resource_attributes.get("Name")
attribute.string_value = self.name attribute.string_value = self.name
refresh() blenderbim.bim.module.resource.data.refresh()
tool.Sequence.refresh_task_resources() tool.Sequence.refresh_task_resources()
@@ -88,17 +88,15 @@ def updateResourceUsage(self, context):
if self.schedule_usage == "": if self.schedule_usage == "":
return return
resource = tool.Ifc.get().by_id(self.ifc_definition_id) resource = tool.Ifc.get().by_id(self.ifc_definition_id)
if not resource.Usage: tool.Resource.run_edit_resource_time(resource, attributes={
tool.Ifc.run( "ScheduleUsage": self.schedule_usage
"resource.add_resource_time", })
resource=resource,
)
resource.Usage.ScheduleUsage = self.schedule_usage
blenderbim.bim.module.pset.data.refresh()
refresh()
tool.Resource.load_resource_properties() tool.Resource.load_resource_properties()
tool.Sequence.load_task_properties()
blenderbim.bim.module.resource.data.refresh()
blenderbim.bim.module.sequence.data.refresh()
tool.Sequence.refresh_task_resources() tool.Sequence.refresh_task_resources()
blenderbim.bim.module.pset.data.refresh()
class ISODuration(PropertyGroup): class ISODuration(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -110,11 +110,11 @@ class BIM_PT_resources(Panel):
is_work_locked = True is_work_locked = True
row = self.layout.row() row = self.layout.row()
row.label(text="Resource Work") row.label(text="Resource Work")
schedule_usage = "Schedule Usage: {}".format(resource.get("ScheduleUsage"))
schedule_work = "Schedule Work: {}".format(resource.get("ScheduleWork")) schedule_work = "Schedule Work: {}".format(resource.get("ScheduleWork"))
row = self.layout.row() row = self.layout.row()
row.alignment = "LEFT" row.alignment = "LEFT"
row.label(text=schedule_usage, icon="ARMATURE_DATA") row.label(text="Schedule Usage:")
row.prop(self.tprops.resources[self.props.active_resource_index], "schedule_usage", text="")
row2 = self.layout.row() row2 = self.layout.row()
row2.alignment = "LEFT" row2.alignment = "LEFT"
row2.label(text=schedule_work, icon="ARMATURE_DATA") row2.label(text=schedule_work, icon="ARMATURE_DATA")
+15 -53
View File
@@ -32,7 +32,7 @@ import ifcopenshell.util.date as ifcdateutils
import ifcopenshell.util.cost import ifcopenshell.util.cost
import ifcopenshell.util.resource import ifcopenshell.util.resource
import blenderbim.bim.schema import blenderbim.bim.schema
import ifcopenshell.util.constraint
class Resource(blenderbim.core.tool.Resource): class Resource(blenderbim.core.tool.Resource):
@classmethod @classmethod
@@ -384,68 +384,30 @@ class Resource(blenderbim.core.tool.Resource):
@classmethod @classmethod
def get_constraints(cls, resource): def get_constraints(cls, resource):
constraints = [] return ifcopenshell.util.constraint.get_constraints(product=resource)
for rel in resource.HasAssociations or []:
if rel.is_a("IfcRelAssociatesConstraint"):
constraints.append(rel.RelatingConstraint)
return constraints
@classmethod @classmethod
def get_metrics(cls, constraint): def get_metrics(cls, constraint):
metrics = [] return ifcopenshell.util.constraint.get_metrics(constraint)
for metric in constraint.BenchmarkValues or []:
metrics.append(metric)
return metrics
@classmethod @classmethod
def get_metric_reference(cls, metric, is_deep=True): def get_metric_reference(cls, metric, is_deep=True):
def get_reference_Attribute(ref, path): return ifcopenshell.util.constraint.get_metric_reference(metric, is_deep=is_deep)
if ref:
if is_deep:
if not path:
path = ref.AttributeIdentifier
else:
path += ".{}".format(ref.AttributeIdentifier) if ref.AttributeIdentifier else ""
return get_reference_Attribute(ref.InnerReference, path)
else:
return ref.AttributeIdentifier
return path
reference = metric.ReferencePath
return get_reference_Attribute(reference, "")
@classmethod
def get_resource_benchmarks(cls, resource):
constraints = []
for constraint in cls.get_constraints(resource) or []:
metrics = []
for metric in cls.get_metrics(constraint) or []:
metrics.append(
{
"reference": cls.get_metric_reference(metric),
"Benchmark": metric.Benchmark,
"ConstraintGrade": metric.ConstraintGrade,
}
)
constraints.append({"ObjectiveQualifier": constraint.ObjectiveQualifier, "metrics": metrics})
return constraints
@classmethod @classmethod
def has_metric_constraint(cls, resource, attribute): def has_metric_constraint(cls, resource, attribute):
constraints = tool.Resource.get_constraints(resource) metrics = ifcopenshell.util.constraint.has_metric_constraints(resource, attribute)
metrics = [] return metrics[0] if metrics else None
for constraint in constraints:
for metric in tool.Resource.get_metrics(constraint) or []:
is_same_reference = bool(
tool.Resource.get_metric_reference(metric, is_deep=False) == attribute
or tool.Resource.get_metric_reference(metric, is_deep=True) == attribute
)
if is_same_reference:
metrics.append(metric)
if metrics:
return metrics[0]
return None
@classmethod @classmethod
def has_usage_metric(cls, resource): def has_usage_metric(cls, resource):
return cls.has_metric_constraint(resource, "Usage") return cls.has_metric_constraint(resource, "Usage")
@classmethod
def run_edit_resource_time(cls, resource, attributes):
if not resource.Usage:
tool.Ifc.run(
"resource.add_resource_time",
resource=resource,
)
tool.Ifc.run("resource.edit_resource_time", resource_time=resource.Usage, attributes=attributes)
@@ -60,6 +60,9 @@ class Usecase:
self.settings = {"resource": resource} self.settings = {"resource": resource}
def execute(self): def execute(self):
metrics= ifcopenshell.util.constraint.has_metric_constraints(self.settings["resource"], "Usage.ScheduleWork")
if metrics and metrics[0].ConstraintGrade == "HARD" and metrics[0].Benchmark == "EQUALTO":
return
amount_worked = ifcopenshell.util.resource.get_resource_required_work( amount_worked = ifcopenshell.util.resource.get_resource_required_work(
self.settings["resource"] self.settings["resource"]
) )
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import datetime import datetime
import ifcopenshell.util.date import ifcopenshell
class Usecase: class Usecase:
def __init__(self, file, resource_time=None, attributes=None): def __init__(self, file, resource_time=None, attributes=None):
@@ -77,6 +76,9 @@ class Usecase:
del self.settings["attributes"]["ActualFinish"] del self.settings["attributes"]["ActualFinish"]
for name, value in self.settings["attributes"].items(): for name, value in self.settings["attributes"].items():
metrics = ifcopenshell.util.constraint.has_metric_constraints(self.resource, "Usage." + name)
if metrics and self.is_hard_constraint(metrics[0]):
continue
if value: if value:
if "Start" in name or "Finish" in name or name == "StatusTime": if "Start" in name or "Finish" in name or name == "StatusTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
@@ -87,6 +89,15 @@ class Usecase:
): ):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["resource_time"], name, value) setattr(self.settings["resource_time"], name, value)
if name == "ScheduleUsage" and ifcopenshell.util.constraint.has_metric_constraints(self.resource, "Usage.ScheduleWork"):
for rel in self.resource.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProcess"):
continue
task = rel.RelatingProcess
ifcopenshell.api.run("sequence.calculate_task_duration", self.file, task=task)
def is_hard_constraint(self, metric):
return bool(metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO")
def get_resource(self): def get_resource(self):
return [ return [