New feature to visualise construction progress at a particular date

This commit is contained in:
Dion Moult
2021-05-05 17:07:17 +10:00
parent a1eb2a1ec3
commit da5a2625a9
5 changed files with 134 additions and 26 deletions
@@ -63,6 +63,8 @@ classes = (
operator.ImportP6, operator.ImportP6,
operator.LoadTaskProperties, operator.LoadTaskProperties,
operator.SelectTaskRelatedProducts, operator.SelectTaskRelatedProducts,
operator.VisualiseWorkScheduleDate,
operator.VisualiseWorkScheduleDateRange,
prop.WorkPlan, prop.WorkPlan,
prop.BIMWorkPlanProperties, prop.BIMWorkPlanProperties,
prop.Task, prop.Task,
@@ -0,0 +1,20 @@
from ifcopenshell.api.sequence.data import Data
def derive_date(ifc_definition_id, attribute_name, date=None, is_earliest=False, is_latest=False):
task = Data.tasks[ifc_definition_id]
if task["TaskTime"]:
current_date = Data.task_times[task["TaskTime"]][attribute_name]
if current_date:
return current_date
for subtask in task["RelatedObjects"]:
current_date = derive_date(
subtask, attribute_name, date=date, is_earliest=is_earliest, is_latest=is_latest
)
if is_earliest:
if current_date and (date is None or current_date < date):
date = current_date
if is_latest:
if current_date and (date is None or current_date > date):
date = current_date
return date
@@ -7,6 +7,7 @@ import pystache
import webbrowser import webbrowser
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.date import ifcopenshell.util.date
import blenderbim.bim.module.sequence.helper as helper
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
from dateutil import parser from dateutil import parser
@@ -311,8 +312,8 @@ class LoadTaskProperties(bpy.types.Operator):
isodate.duration_isoformat(task_time["ScheduleDuration"]) if task_time["ScheduleDuration"] else "-" isodate.duration_isoformat(task_time["ScheduleDuration"]) if task_time["ScheduleDuration"] else "-"
) )
else: else:
derived_start = self.derive_start(item.ifc_definition_id) derived_start = helper.derive_date(item.ifc_definition_id, "ScheduleStart", is_earliest=True)
derived_finish = self.derive_finish(item.ifc_definition_id) derived_finish = helper.derive_date(item.ifc_definition_id, "ScheduleFinish", is_latest=True)
item.derived_start = self.canonicalise_time(derived_start) if derived_start else "" item.derived_start = self.canonicalise_time(derived_start) if derived_start else ""
item.derived_finish = self.canonicalise_time(derived_finish) if derived_finish else "" item.derived_finish = self.canonicalise_time(derived_finish) if derived_finish else ""
if derived_start and derived_finish: if derived_start and derived_finish:
@@ -324,30 +325,6 @@ class LoadTaskProperties(bpy.types.Operator):
self.props.is_task_update_enabled = True self.props.is_task_update_enabled = True
return {"FINISHED"} return {"FINISHED"}
def derive_start(self, ifc_definition_id, start=None):
task = Data.tasks[ifc_definition_id]
if task["TaskTime"]:
schedule_start = Data.task_times[task["TaskTime"]]["ScheduleStart"]
if schedule_start:
return schedule_start
for subtask in task["RelatedObjects"]:
schedule_start = self.derive_start(subtask, start)
if schedule_start and (start is None or schedule_start < start):
start = schedule_start
return start
def derive_finish(self, ifc_definition_id, finish=None):
task = Data.tasks[ifc_definition_id]
if task["TaskTime"]:
schedule_finish = Data.task_times[task["TaskTime"]]["ScheduleFinish"]
if schedule_finish:
return schedule_finish
for subtask in task["RelatedObjects"]:
schedule_finish = self.derive_finish(subtask, finish)
if schedule_finish and (finish is None or schedule_finish > finish):
finish = schedule_finish
return finish
def canonicalise_time(self, time): def canonicalise_time(self, time):
if not time: if not time:
return "-" return "-"
@@ -1421,3 +1398,72 @@ class SelectTaskRelatedProducts(bpy.types.Operator):
if obj.BIMObjectProperties.ifc_definition_id in related_products: if obj.BIMObjectProperties.ifc_definition_id in related_products:
obj.select_set(True) obj.select_set(True)
return {"FINISHED"} return {"FINISHED"}
class VisualiseWorkScheduleDate(bpy.types.Operator):
bl_idname = "bim.visualise_work_schedule_date"
bl_label = "Visualise Work Schedule Date"
work_schedule: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMWorkScheduleProperties
self.file = IfcStore.get_file()
self.date = parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
self.preprocess_tasks()
for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
obj.color = (1.0, 1.0, 1.0, 1)
obj.hide_set(False)
if obj.BIMObjectProperties.ifc_definition_id in self.not_yet_started:
obj.hide_set(True)
elif obj.BIMObjectProperties.ifc_definition_id in self.started:
obj.color = (0.0, 1.0, 0.0, 1)
elif obj.BIMObjectProperties.ifc_definition_id in self.completed:
pass
else:
obj.color = (1.0, 0.0, 0.0, 1)
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
def preprocess_tasks(self):
self.not_yet_started = set()
self.started = set()
self.completed = set()
for rel in self.file.by_id(self.work_schedule).Controls or []:
for related_object in rel.RelatedObjects:
if related_object.is_a("IfcTask"):
self.preprocess_task(related_object)
def preprocess_task(self, task):
for rel in task.IsNestedBy or []:
for related_object in rel.RelatedObjects:
self.preprocess_task(related_object)
start = helper.derive_date(task.id(), "ScheduleStart", is_earliest=True)
finish = helper.derive_date(task.id(), "ScheduleFinish", is_latest=True)
if not start or not finish:
return
products = [r.RelatingProduct.id() for r in task.HasAssignments or [] if r.is_a("IfcRelAssignsToProduct")]
if not products:
return
if self.date < start:
self.not_yet_started.update(products)
elif self.date < finish:
self.started.update(products)
else:
self.completed.update(products)
class VisualiseWorkScheduleDateRange(bpy.types.Operator):
bl_idname = "bim.visualise_work_schedule_date_range"
bl_label = "Visualise Work Schedule Date Range"
work_schedule: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
# for obj in bpy.context.visible_objects:
# obj.select_set(False)
# if obj.BIMObjectProperties.ifc_definition_id in related_products:
# obj.select_set(True)
return {"FINISHED"}
@@ -107,6 +107,32 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, canonicalise_time(startfinish_datetime)) setattr(self, startfinish, canonicalise_time(startfinish_datetime))
def updateVisualisationStart(self, context):
updateVisualisationStartFinish(self, context, "visualisation_start")
def updateVisualisationFinish(self, context):
updateVisualisationStartFinish(self, context, "visualisation_finish")
def updateVisualisationStartFinish(self, context, startfinish):
def canonicalise_time(time):
if not time:
return "-"
return time.strftime("%d/%m/%y")
startfinish_value = getattr(self, startfinish)
try:
startfinish_datetime = parser.isoparse(startfinish_value)
except:
try:
startfinish_datetime = parser.parse(startfinish_value, dayfirst=True, fuzzy=True)
except:
setattr(self, startfinish, "-")
return
canonical_value = canonicalise_time(startfinish_datetime)
if startfinish_value != canonical_value:
setattr(self, startfinish, canonical_value)
workschedule_enum = [] workschedule_enum = []
@@ -159,6 +185,7 @@ class BIMWorkScheduleProperties(PropertyGroup):
active_task_index: IntProperty(name="Active Task Index") active_task_index: IntProperty(name="Active Task Index")
active_task_id: IntProperty(name="Active Task Id") active_task_id: IntProperty(name="Active Task Id")
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute) task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=False)
should_show_times: BoolProperty(name="Should Show Times", default=False) should_show_times: BoolProperty(name="Should Show Times", default=False)
active_task_time_id: IntProperty(name="Active Task Id") active_task_time_id: IntProperty(name="Active Task Id")
task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute) task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute)
@@ -168,6 +195,8 @@ class BIMWorkScheduleProperties(PropertyGroup):
active_sequence_id: IntProperty(name="Active Sequence Id") active_sequence_id: IntProperty(name="Active Sequence Id")
sequence_attributes: CollectionProperty(name="Sequence Attributes", type=Attribute) sequence_attributes: CollectionProperty(name="Sequence Attributes", type=Attribute)
time_lag_attributes: CollectionProperty(name="Time Lag Attributes", type=Attribute) time_lag_attributes: CollectionProperty(name="Time Lag Attributes", type=Attribute)
visualisation_start: StringProperty(name="Visualisation Start", update=updateVisualisationStart)
visualisation_finish: StringProperty(name="Visualisation Finish", update=updateVisualisationFinish)
class BIMTaskTreeProperties(PropertyGroup): class BIMTaskTreeProperties(PropertyGroup):
@@ -109,6 +109,7 @@ class BIM_PT_work_schedules(Panel):
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK") row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
elif self.props.editing_type == "TASKS": elif self.props.editing_type == "TASKS":
row.prop(self.props, "should_show_times", text="", icon="TIME") row.prop(self.props, "should_show_times", text="", icon="TIME")
row.prop(self.props, "should_show_visualisation_ui", text="", icon="CAMERA_STEREO")
row.operator("bim.generate_gantt_chart", text="", icon="NLA").work_schedule = work_schedule_id row.operator("bim.generate_gantt_chart", text="", icon="NLA").work_schedule = work_schedule_id
row.operator("bim.add_summary_task", text="", icon="ADD").work_schedule = work_schedule_id row.operator("bim.add_summary_task", text="", icon="ADD").work_schedule = work_schedule_id
row.operator("bim.disable_editing_work_schedule", text="", icon="CANCEL") row.operator("bim.disable_editing_work_schedule", text="", icon="CANCEL")
@@ -122,11 +123,21 @@ class BIM_PT_work_schedules(Panel):
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id
if self.props.active_work_schedule_id == work_schedule_id: if self.props.active_work_schedule_id == work_schedule_id:
if self.props.should_show_visualisation_ui:
self.draw_visualisation_ui()
if self.props.editing_type == "WORK_SCHEDULE": if self.props.editing_type == "WORK_SCHEDULE":
self.draw_editable_work_schedule_ui() self.draw_editable_work_schedule_ui()
elif self.props.editing_type == "TASKS": elif self.props.editing_type == "TASKS":
self.draw_editable_task_ui(work_schedule_id) self.draw_editable_task_ui(work_schedule_id)
def draw_visualisation_ui(self):
row = self.layout.row(align=True)
row.prop(self.props, "visualisation_start", text="", icon="REW")
row.prop(self.props, "visualisation_finish", text="", icon="FF")
op = row.operator("bim.visualise_work_schedule_date", text="", icon="RESTRICT_RENDER_OFF")
op.work_schedule = self.props.active_work_schedule_id
op = row.operator("bim.visualise_work_schedule_date_range", text="", icon="OUTLINER_OB_CAMERA")
def draw_editable_work_schedule_ui(self): def draw_editable_work_schedule_ui(self):
for attribute in self.props.work_schedule_attributes: for attribute in self.props.work_schedule_attributes:
row = self.layout.row(align=True) row = self.layout.row(align=True)