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.LoadTaskProperties,
operator.SelectTaskRelatedProducts,
operator.VisualiseWorkScheduleDate,
operator.VisualiseWorkScheduleDateRange,
prop.WorkPlan,
prop.BIMWorkPlanProperties,
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 ifcopenshell.api
import ifcopenshell.util.date
import blenderbim.bim.module.sequence.helper as helper
from datetime import datetime
from datetime import timedelta
from dateutil import parser
@@ -311,8 +312,8 @@ class LoadTaskProperties(bpy.types.Operator):
isodate.duration_isoformat(task_time["ScheduleDuration"]) if task_time["ScheduleDuration"] else "-"
)
else:
derived_start = self.derive_start(item.ifc_definition_id)
derived_finish = self.derive_finish(item.ifc_definition_id)
derived_start = helper.derive_date(item.ifc_definition_id, "ScheduleStart", is_earliest=True)
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_finish = self.canonicalise_time(derived_finish) if derived_finish else ""
if derived_start and derived_finish:
@@ -324,30 +325,6 @@ class LoadTaskProperties(bpy.types.Operator):
self.props.is_task_update_enabled = True
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):
if not time:
return "-"
@@ -1421,3 +1398,72 @@ class SelectTaskRelatedProducts(bpy.types.Operator):
if obj.BIMObjectProperties.ifc_definition_id in related_products:
obj.select_set(True)
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))
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 = []
@@ -159,6 +185,7 @@ class BIMWorkScheduleProperties(PropertyGroup):
active_task_index: IntProperty(name="Active Task Index")
active_task_id: IntProperty(name="Active Task Id")
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)
active_task_time_id: IntProperty(name="Active Task Id")
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")
sequence_attributes: CollectionProperty(name="Sequence 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):
@@ -109,6 +109,7 @@ class BIM_PT_work_schedules(Panel):
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
elif self.props.editing_type == "TASKS":
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.add_summary_task", text="", icon="ADD").work_schedule = work_schedule_id
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
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":
self.draw_editable_work_schedule_ui()
elif self.props.editing_type == "TASKS":
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):
for attribute in self.props.work_schedule_attributes:
row = self.layout.row(align=True)