diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 56b035c8f5..96b96c9f9b 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -38,6 +38,7 @@ classes = ( operator.RemoveTask, operator.EnableEditingTask, operator.DisableEditingTask, + operator.DisableEditingTaskTime, operator.EditTask, operator.AssignPredecessor, operator.AssignSuccessor, @@ -45,6 +46,7 @@ classes = ( operator.UnassignSuccessor, operator.EnableEditingTaskTime, operator.EnableEditingTaskCalendar, + operator.EnableEditingTaskSequence, operator.EditTaskTime, operator.EditTaskCalendar, operator.RemoveTaskCalendar, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index d0a8d32bbd..d40519edeb 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -291,8 +291,12 @@ class LoadTaskProperties(bpy.types.Operator): 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"] + item.is_predecessor = self.props.active_task_id in [ + Data.sequences[r]["RelatedProcess"] for r in task["IsPredecessorTo"] + ] + item.is_successor = self.props.active_task_id in [ + Data.sequences[r]["RelatingProcess"] for r in task["IsSuccessorFrom"] + ] if task["TaskTime"]: task_time = Data.task_times[task["TaskTime"]] item.start = self.canonicalise_time(task_time["ScheduleStart"]) @@ -440,7 +444,6 @@ class EnableEditingTaskTime(bpy.types.Operator): props.active_task_time_id = task_time_id props.active_task_id = self.task props.editing_task_type = "TASKTIME" - bpy.ops.bim.load_task_properties() return {"FINISHED"} def add_task_time(self): @@ -530,7 +533,6 @@ class EnableEditingTask(bpy.types.Operator): new.enum_value = data[attribute.name()] props.active_task_id = self.task props.editing_task_type = "ATTRIBUTES" - bpy.ops.bim.load_task_properties() return {"FINISHED"} @@ -1159,3 +1161,26 @@ class RemoveTaskCalendar(bpy.types.Operator): ) Data.load(IfcStore.get_file()) return {"FINISHED"} + + +class EnableEditingTaskSequence(bpy.types.Operator): + bl_idname = "bim.enable_editing_task_sequence" + bl_label = "Enable Editing Task Sequence" + task: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkScheduleProperties + props.active_task_id = self.task + props.editing_task_type = "SEQUENCE" + bpy.ops.bim.load_task_properties() + return {"FINISHED"} + + +class DisableEditingTaskTime(bpy.types.Operator): + bl_idname = "bim.disable_editing_task_time" + bl_label = "Disable Editing Task Time" + + def execute(self, context): + context.scene.BIMWorkScheduleProperties.active_task_time_id = 0 + bpy.ops.bim.disable_editing_task() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index f5c2f8f383..2f83dedb8d 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -156,7 +156,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_times: BoolProperty(name="Should Show Times", default=True) + 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) contracted_tasks: StringProperty(name="Contracted Task Items", default="[]") diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 8b37eb61a8..36875cdfe6 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -149,9 +149,30 @@ class BIM_PT_work_schedules(Panel): self.draw_editable_task_attributes_ui() elif self.props.active_task_id and self.props.editing_task_type == "CALENDAR": self.draw_editable_task_calendar_ui() + elif self.props.active_task_id and self.props.editing_task_type == "SEQUENCE": + self.draw_editable_task_sequence_ui() elif self.props.active_task_time_id and self.props.editing_task_type == "TASKTIME": self.draw_editable_task_time_attributes_ui() + def draw_editable_task_sequence_ui(self): + task = Data.tasks[self.props.active_task_id] + row = self.layout.row() + row.label(text="{} Predecessors".format(len(task["IsSuccessorFrom"])), icon="BACK") + for sequence_id in task["IsSuccessorFrom"]: + self.draw_editable_sequence_ui(Data.sequences[sequence_id], "RelatingProcess") + + row = self.layout.row() + row.label(text="{} Successors".format(len(task["IsPredecessorTo"])), icon="FORWARD") + for sequence_id in task["IsPredecessorTo"]: + self.draw_editable_sequence_ui(Data.sequences[sequence_id], "RelatedProcess") + + def draw_editable_sequence_ui(self, sequence, process_type): + task = Data.tasks[sequence[process_type]] + row = self.layout.row(align=True) + row.label(text=task["Identification"] or "XXX") + row.label(text=task["Name"] or "Unnamed") + row.label(text=sequence["SequenceType"] or "N/A") + def draw_editable_task_calendar_ui(self): task = Data.tasks[self.props.active_task_id] if task["HasAssignmentsWorkCalendar"]: @@ -240,31 +261,35 @@ class BIM_UL_tasks(UIList): row.operator("bim.edit_task_time", text="", icon="CHECKMARK") elif props.editing_task_type == "CALENDAR": row.operator("bim.disable_editing_task", text="", icon="CHECKMARK") + elif props.editing_task_type == "SEQUENCE": + row.operator("bim.disable_editing_task", text="", icon="CHECKMARK") elif props.editing_task_type == "ATTRIBUTES": row.operator("bim.edit_task", text="", icon="CHECKMARK") row.operator("bim.disable_editing_task", text="", icon="CANCEL") elif props.active_task_id: - if item.is_predecessor: - row.operator( - "bim.unassign_predecessor", text="", icon="BACK", emboss=False - ).task = item.ifc_definition_id - else: - row.operator( - "bim.assign_predecessor", text="", icon="TRACKING_BACKWARDS", emboss=False - ).task = item.ifc_definition_id + if props.editing_task_type == "SEQUENCE": + if item.is_predecessor: + row.operator( + "bim.unassign_predecessor", text="", icon="BACK", emboss=False + ).task = item.ifc_definition_id + else: + row.operator( + "bim.assign_predecessor", text="", icon="TRACKING_BACKWARDS", emboss=False + ).task = item.ifc_definition_id - if item.is_successor: - row.operator( - "bim.unassign_successor", text="", icon="FORWARD", emboss=False - ).task = item.ifc_definition_id - else: - row.operator( - "bim.assign_successor", text="", icon="TRACKING_FORWARDS", emboss=False - ).task = item.ifc_definition_id + if item.is_successor: + row.operator( + "bim.unassign_successor", text="", icon="FORWARD", emboss=False + ).task = item.ifc_definition_id + else: + row.operator( + "bim.assign_successor", text="", icon="TRACKING_FORWARDS", emboss=False + ).task = item.ifc_definition_id row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id else: + row.operator("bim.enable_editing_task_sequence", text="", icon="TRACKING").task = item.ifc_definition_id row.operator("bim.enable_editing_task_time", text="", icon="TIME").task = item.ifc_definition_id row.operator("bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO").task = item.ifc_definition_id row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py index b1714dc27a..2df5feed7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py @@ -11,6 +11,7 @@ class Data: time_periods = {} tasks = {} task_times = {} + sequences = {} @classmethod def purge(cls): @@ -23,6 +24,7 @@ class Data: cls.time_periods = {} cls.tasks = {} cls.task_times = {} + cls.sequences = {} @classmethod def load(cls, file): @@ -37,6 +39,7 @@ class Data: cls.load_time_periods() cls.load_tasks() cls.load_task_times() + cls.load_sequences() cls.is_loaded = True @classmethod @@ -132,8 +135,8 @@ class Data: for r in task.HasAssignments if r.is_a("IfcRelAssignsToProduct") ] - [data["IsPredecessorTo"].append(rel.RelatedProcess.id()) for rel in task.IsPredecessorTo or []] - [data["IsSuccessorFrom"].append(rel.RelatingProcess.id()) for rel in task.IsSuccessorFrom or []] + [data["IsPredecessorTo"].append(rel.id()) for rel in task.IsPredecessorTo or []] + [data["IsSuccessorFrom"].append(rel.id()) for rel in task.IsSuccessorFrom or []] [ data["HasAssignmentsWorkCalendar"].append(rel.RelatingControl.id()) for rel in task.HasAssignments or [] @@ -153,3 +156,13 @@ class Data: data[key] = ifcopenshell.util.date.ifc2datetime(value) # TODO parse duration cls.task_times[task_time.id()] = data + + @classmethod + def load_sequences(cls): + cls.sequences = {} + for sequence in cls._file.by_type("IfcRelSequence"): + data = sequence.get_info() + data["RelatingProcess"] = sequence.RelatingProcess.id() + data["RelatedProcess"] = sequence.RelatedProcess.id() + data["TimeLag"] = sequence.TimeLag.id() if sequence.TimeLag else None + cls.sequences[sequence.id()] = data diff --git a/src/ifcp6/ifcp6/p62ifc.py b/src/ifcp6/ifcp6/p62ifc.py index 68e5d9919e..23a29ca062 100644 --- a/src/ifcp6/ifcp6/p62ifc.py +++ b/src/ifcp6/ifcp6/p62ifc.py @@ -130,6 +130,7 @@ class P62Ifc: self.relationships[relationship.find("pr:ObjectId", self.ns).text] = { "PredecessorActivity": relationship.find("pr:PredecessorActivityObjectId", self.ns).text, "SuccessorActivity": relationship.find("pr:SuccessorActivityObjectId", self.ns).text, + "Type": relationship.find("pr:Type", self.ns).text, } def get_wbs(self, wbs): @@ -338,13 +339,25 @@ class P62Ifc: ) def create_rel_sequences(self): + self.sequence_type_map = { + "Start to Start": "START_START", + "Start to Finish": "START_FINISH", + "Finish to Start": "FINISH_START", + "Finish to Finish": "FINISH_FINISH", + } for relationship in self.relationships.values(): - ifcopenshell.api.run( + rel_sequence = ifcopenshell.api.run( "sequence.assign_sequence", self.file, relating_process=self.activities[relationship["PredecessorActivity"]]["ifc"], related_process=self.activities[relationship["SuccessorActivity"]]["ifc"], ) + ifcopenshell.api.run( + "sequence.edit_sequence", + self.file, + rel_sequence=rel_sequence, + attributes={"SequenceType": self.sequence_type_map[relationship["Type"]]}, + ) def create_boilerplate_ifc(self): self.file = ifcopenshell.file(schema="IFC4")