Optimisations for construction sequencing task trees where there are thousands of tasks

This commit is contained in:
Dion Moult
2021-04-22 18:34:45 +10:00
parent ee0f7f1677
commit 761b4c035f
4 changed files with 71 additions and 25 deletions
@@ -42,10 +42,12 @@ classes = (
operator.UnassignProduct,
operator.GenerateGanttChart,
operator.ImportP6,
operator.LoadTaskProperties,
prop.WorkPlan,
prop.BIMWorkPlanProperties,
prop.Task,
prop.BIMWorkScheduleProperties,
prop.BIMTaskTreeProperties,
prop.WorkCalendar,
prop.BIMWorkCalendarProperties,
ui.BIM_PT_work_plans,
@@ -64,6 +66,7 @@ def menu_func_import(self, context):
def register():
bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties)
bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties)
bpy.types.Scene.BIMTaskTreeProperties = bpy.props.PointerProperty(type=prop.BIMTaskTreeProperties)
bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
@@ -71,5 +74,6 @@ def register():
def unregister():
del bpy.types.Scene.BIMWorkPlanProperties
del bpy.types.Scene.BIMWorkScheduleProperties
del bpy.types.Scene.BIMTaskTreeProperties
del bpy.types.Scene.BIMWorkCalendarProperties
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
@@ -223,32 +223,22 @@ class EnableEditingTasks(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.BIMWorkScheduleProperties
self.tprops = context.scene.BIMTaskTreeProperties
self.props.active_work_schedule_id = self.work_schedule
while len(self.props.tasks) > 0:
self.props.tasks.remove(0)
while len(self.tprops.tasks) > 0:
self.tprops.tasks.remove(0)
self.contracted_tasks = json.loads(self.props.contracted_tasks)
for related_object_id in Data.work_schedules[self.work_schedule]["RelatedObjects"]:
self.create_new_task_li(related_object_id, 0)
bpy.ops.bim.load_task_properties()
self.props.is_editing = "TASKS"
return {"FINISHED"}
def create_new_task_li(self, related_object_id, level_index):
task = Data.tasks[related_object_id]
new = self.props.tasks.add()
new = self.tprops.tasks.add()
new.ifc_definition_id = related_object_id
new.name = task["Name"] or "Unnamed"
new.identification = task["Identification"] or "XXX"
if task["TaskTime"]:
task_time = Data.task_times[task["TaskTime"]]
new.start = self.canonicalise_time(task_time["ScheduleStart"])
new.finish = self.canonicalise_time(task_time["ScheduleFinish"])
# TODO: duration
new.duration = "-"
else:
new.start = "-"
new.finish = "-"
new.duration = "-"
new.is_expanded = related_object_id not in self.contracted_tasks
new.level_index = level_index
if task["RelatedObjects"]:
@@ -258,6 +248,38 @@ class EnableEditingTasks(bpy.types.Operator):
self.create_new_task_li(related_object_id, level_index + 1)
return {"FINISHED"}
class LoadTaskProperties(bpy.types.Operator):
bl_idname = "bim.load_task_properties"
bl_label = "Load Task Properties"
task: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMWorkScheduleProperties
self.tprops = context.scene.BIMTaskTreeProperties
self.props.is_task_update_enabled = False
for item in self.tprops.tasks:
if self.task and item.ifc_definition_id != self.task:
continue
task = Data.tasks[item.ifc_definition_id]
item.name = task["Name"] or "Unnamed"
item.identification = task["Identification"] or "XXX"
if self.props.active_task_id:
item.is_predecessor = self.props.active_task_id in task["IsPredecessorTo"]
item.is_successor = self.props.active_task_id in task["IsSuccessorFrom"]
if task["TaskTime"]:
task_time = Data.task_times[task["TaskTime"]]
item.start = self.canonicalise_time(task_time["ScheduleStart"])
item.finish = self.canonicalise_time(task_time["ScheduleFinish"])
# TODO: duration
item.duration = "-"
else:
item.start = "-"
item.finish = "-"
item.duration = "-"
self.props.is_task_update_enabled = True
return {"FINISHED"}
def canonicalise_time(self, time):
if not time:
return "-"
@@ -400,6 +422,7 @@ class EnableEditingTaskTime(bpy.types.Operator):
new.enum_value = data[attribute.name()]
props.active_task_time_id = task_time_id
props.active_task_id = self.task
bpy.ops.bim.load_task_properties()
return {"FINISHED"}
def add_task_time(self):
@@ -448,7 +471,7 @@ class EditTaskTime(bpy.types.Operator):
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_task_time()
bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
bpy.ops.bim.load_task_properties(task=props.active_task_id)
return {"FINISHED"}
def convert_strings_to_date_times(self, attributes):
@@ -498,6 +521,7 @@ class EnableEditingTask(bpy.types.Operator):
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_task_id = self.task
bpy.ops.bim.load_task_properties()
return {"FINISHED"}
@@ -535,7 +559,7 @@ class EditTask(bpy.types.Operator):
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_task()
bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
bpy.ops.bim.load_task_properties(task=props.active_task_id)
return {"FINISHED"}
@@ -554,6 +578,7 @@ class AssignPredecessor(bpy.types.Operator):
related_process=IfcStore.get_file().by_id(props.active_task_id),
)
Data.load(self.file)
bpy.ops.bim.load_task_properties(task=self.task)
return {"FINISHED"}
@@ -572,6 +597,7 @@ class AssignSuccessor(bpy.types.Operator):
related_process=IfcStore.get_file().by_id(self.task),
)
Data.load(self.file)
bpy.ops.bim.load_task_properties(task=self.task)
return {"FINISHED"}
@@ -590,6 +616,7 @@ class UnassignPredecessor(bpy.types.Operator):
related_process=IfcStore.get_file().by_id(props.active_task_id),
)
Data.load(self.file)
bpy.ops.bim.load_task_properties(task=self.task)
return {"FINISHED"}
@@ -608,6 +635,7 @@ class UnassignSuccessor(bpy.types.Operator):
related_process=self.file.by_id(self.task),
)
Data.load(self.file)
bpy.ops.bim.load_task_properties(task=self.task)
return {"FINISHED"}
@@ -18,10 +18,10 @@ from bpy.props import (
def updateTaskName(self, context):
if self.name == "Unnamed":
props = context.scene.BIMWorkScheduleProperties
if not props.is_task_update_enabled or self.name == "Unnamed":
return
self.file = IfcStore.get_file()
props = context.scene.BIMWorkScheduleProperties
ifcopenshell.api.run(
"sequence.edit_task",
self.file,
@@ -34,10 +34,10 @@ def updateTaskName(self, context):
def updateTaskIdentification(self, context):
if self.identification == "X":
props = context.scene.BIMWorkScheduleProperties
if not props.is_task_update_enabled or self.identification == "XXX":
return
self.file = IfcStore.get_file()
props = context.scene.BIMWorkScheduleProperties
ifcopenshell.api.run(
"sequence.edit_task",
self.file,
@@ -58,6 +58,11 @@ def updateTaskTimeFinish(self, context):
def updateTaskTimeDateTime(self, context, startfinish):
props = context.scene.BIMWorkScheduleProperties
if not props.is_task_update_enabled:
return
def canonicalise_time(time):
if not time:
return "-"
@@ -112,6 +117,8 @@ class Task(PropertyGroup):
duration: StringProperty(name="Duration")
start: StringProperty(name="Start", update=updateTaskTimeStart)
finish: StringProperty(name="Finish", update=updateTaskTimeFinish)
is_predecessor: BoolProperty(name="Is Predecessor")
is_successor: BoolProperty(name="Is Successor")
class WorkPlan(PropertyGroup):
@@ -132,7 +139,6 @@ class BIMWorkScheduleProperties(PropertyGroup):
is_editing: StringProperty(name="Is Editing")
active_work_schedule_index: IntProperty(name="Active Work Schedules Index")
active_work_schedule_id: IntProperty(name="Active Work Schedules Id")
tasks: CollectionProperty(name="Tasks", type=Task)
active_task_index: IntProperty(name="Active Task Index")
active_task_id: IntProperty(name="Active Task Id")
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
@@ -140,6 +146,13 @@ class BIMWorkScheduleProperties(PropertyGroup):
active_task_time_id: IntProperty(name="Active Task Id")
task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute)
contracted_tasks: StringProperty(name="Contracted Task Items", default="[]")
is_task_update_enabled: BoolProperty(name="Is Task Update Enabled", default=True)
class BIMTaskTreeProperties(PropertyGroup):
# This belongs by itself for performance reasons.
# In Blender if you add thousands of tasks it makes other property access in the same group really slow.
tasks: CollectionProperty(name="Tasks", type=Task)
class WorkCalendar(PropertyGroup):
@@ -81,6 +81,7 @@ class BIM_PT_work_schedules(Panel):
def draw(self, context):
self.props = context.scene.BIMWorkScheduleProperties
self.tprops = context.scene.BIMTaskTreeProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
@@ -130,7 +131,7 @@ class BIM_PT_work_schedules(Panel):
self.layout.template_list(
"BIM_UL_tasks",
"",
self.props,
self.tprops,
"tasks",
self.props,
"active_task_index",
@@ -214,7 +215,7 @@ class BIM_UL_tasks(UIList):
row.operator("bim.edit_task", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_task", text="", icon="CANCEL")
elif props.active_task_id:
if props.active_task_id in Data.tasks[item.ifc_definition_id]["IsPredecessorTo"]:
if item.is_predecessor:
row.operator(
"bim.unassign_predecessor", text="", icon="BACK", emboss=False
).task = item.ifc_definition_id
@@ -223,7 +224,7 @@ class BIM_UL_tasks(UIList):
"bim.assign_predecessor", text="", icon="TRACKING_BACKWARDS", emboss=False
).task = item.ifc_definition_id
if props.active_task_id in Data.tasks[item.ifc_definition_id]["IsSuccessorFrom"]:
if item.is_successor:
row.operator(
"bim.unassign_successor", text="", icon="FORWARD", emboss=False
).task = item.ifc_definition_id