Build untested IFC to P6 XML export feature.

This commit is contained in:
Dion Moult
2022-01-25 16:44:37 +11:00
parent 3222c9ce99
commit 01f25528e8
4 changed files with 282 additions and 73 deletions
@@ -20,86 +20,87 @@ import bpy
from . import ui, prop, operator
classes = (
operator.AddWorkPlan,
operator.EditWorkPlan,
operator.RemoveWorkPlan,
operator.EnableEditingWorkPlan,
operator.DisableEditingWorkPlan,
operator.EnableEditingWorkPlanSchedules,
operator.AssignWorkSchedule,
operator.UnassignWorkSchedule,
operator.AddWorkSchedule,
operator.EditWorkSchedule,
operator.RemoveWorkSchedule,
operator.EnableEditingWorkSchedule,
operator.EnableEditingTasks,
operator.DisableEditingWorkSchedule,
operator.DisableEditingSequence,
operator.EditSequenceAttributes,
operator.EditSequenceTimeLag,
operator.EnableEditingSequenceAttributes,
operator.EnableEditingSequenceTimeLag,
operator.AddSummaryTask,
operator.AddTask,
operator.AddTaskColumn,
operator.AddTimePeriod,
operator.AddWorkCalendar,
operator.EditWorkCalendar,
operator.EditWorkTime,
operator.RemoveWorkCalendar,
operator.RemoveWorkTime,
operator.UnassignRecurrencePattern,
operator.UnassignLagTime,
operator.RemoveTimePeriod,
operator.EnableEditingWorkCalendar,
operator.EnableEditingWorkTime,
operator.EnableEditingWorkCalendarTimes,
operator.DisableEditingWorkCalendar,
operator.DisableEditingWorkTime,
operator.AddWorkPlan,
operator.AddWorkSchedule,
operator.AddWorkTime,
operator.AssignLagTime,
operator.AssignRecurrencePattern,
operator.AddTimePeriod,
operator.AddTask,
operator.AddSummaryTask,
operator.ExpandTask,
operator.ContractTask,
operator.RemoveTask,
operator.EnableEditingTask,
operator.DisableEditingTask,
operator.DisableEditingTaskTime,
operator.EditTask,
operator.CopyTaskAttribute,
operator.AssignPredecessor,
operator.AssignSuccessor,
operator.UnassignPredecessor,
operator.UnassignSuccessor,
operator.EnableEditingTaskTime,
operator.EnableEditingTaskCalendar,
operator.EnableEditingTaskSequence,
operator.EditTaskTime,
operator.EditTaskCalendar,
operator.RemoveTaskCalendar,
operator.AssignProduct,
operator.UnassignProduct,
operator.AssignProcess,
operator.UnassignProcess,
operator.GenerateGanttChart,
operator.ImportP6,
operator.ImportP6XER,
operator.ImportPP,
operator.ImportMSP,
operator.LoadTaskProperties,
operator.SelectTaskRelatedProducts,
operator.VisualiseWorkScheduleDate,
operator.VisualiseWorkScheduleDateRange,
operator.RecalculateSchedule,
operator.AssignProduct,
operator.AssignRecurrencePattern,
operator.AssignSuccessor,
operator.AssignWorkSchedule,
operator.BlenderBIM_DatePicker,
operator.BlenderBIM_DatePickerSetDate,
operator.BlenderBIM_RedrawDatePicker,
operator.AddTaskColumn,
operator.RemoveTaskColumn,
operator.SetTaskSortColumn,
operator.LoadTaskInputs,
operator.LoadTaskResources,
operator.LoadTaskOutputs,
operator.CalculateTaskDuration,
operator.ContractTask,
operator.CopyTaskAttribute,
operator.DisableEditingSequence,
operator.DisableEditingTask,
operator.DisableEditingTaskTime,
operator.DisableEditingWorkCalendar,
operator.DisableEditingWorkPlan,
operator.DisableEditingWorkSchedule,
operator.DisableEditingWorkTime,
operator.EditSequenceAttributes,
operator.EditSequenceTimeLag,
operator.EditTask,
operator.EditTaskCalendar,
operator.EditTaskTime,
operator.EditWorkCalendar,
operator.EditWorkPlan,
operator.EditWorkSchedule,
operator.EditWorkTime,
operator.EnableEditingSequenceAttributes,
operator.EnableEditingSequenceTimeLag,
operator.EnableEditingTask,
operator.EnableEditingTaskCalendar,
operator.EnableEditingTaskSequence,
operator.EnableEditingTaskTime,
operator.EnableEditingTasks,
operator.EnableEditingWorkCalendar,
operator.EnableEditingWorkCalendarTimes,
operator.EnableEditingWorkPlan,
operator.EnableEditingWorkPlanSchedules,
operator.EnableEditingWorkSchedule,
operator.EnableEditingWorkTime,
operator.ExpandTask,
operator.ExportP6,
operator.GenerateGanttChart,
operator.ImportMSP,
operator.ImportP6,
operator.ImportP6XER,
operator.ImportPP,
operator.LoadTaskInputs,
operator.LoadTaskOutputs,
operator.LoadTaskProperties,
operator.LoadTaskResources,
operator.RecalculateSchedule,
operator.RemoveTask,
operator.RemoveTaskCalendar,
operator.RemoveTaskColumn,
operator.RemoveTimePeriod,
operator.RemoveWorkCalendar,
operator.RemoveWorkPlan,
operator.RemoveWorkSchedule,
operator.RemoveWorkTime,
operator.SelectTaskRelatedProducts,
operator.SetTaskSortColumn,
operator.UnassignLagTime,
operator.UnassignPredecessor,
operator.UnassignProcess,
operator.UnassignProduct,
operator.UnassignRecurrencePattern,
operator.UnassignSuccessor,
operator.UnassignWorkSchedule,
operator.VisualiseWorkScheduleDate,
operator.VisualiseWorkScheduleDateRange,
prop.WorkPlan,
prop.BIMWorkPlanProperties,
prop.Task,
@@ -124,6 +125,10 @@ classes = (
)
def menu_func_export(self, context):
self.layout.operator(operator.ExportP6.bl_idname, text="P6 (.xml)")
def menu_func_import(self, context):
self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)")
self.layout.operator(operator.ImportP6XER.bl_idname, text="P6 (.xer)")
@@ -138,6 +143,7 @@ def register():
bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties)
bpy.types.Scene.DatePickerProperties = bpy.props.PointerProperty(type=prop.DatePickerProperties)
bpy.types.TextCurve.BIMDateTextProperties = bpy.props.PointerProperty(type=prop.BIMDateTextProperties)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
@@ -148,4 +154,5 @@ def unregister():
del bpy.types.Scene.BIMWorkCalendarProperties
del bpy.types.Scene.DatePickerProperties
del bpy.types.TextCurve.BIMDateTextProperties
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
@@ -1196,6 +1196,31 @@ class ImportMSP(bpy.types.Operator, ImportHelper):
return {"FINISHED"}
class ExportP6(bpy.types.Operator, ImportHelper):
bl_idname = "export_p6.bim"
bl_label = "Export P6"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".xml"
filter_glob: bpy.props.StringProperty(default="*.xml", options={"HIDDEN"})
@classmethod
def poll(cls, context):
ifc_file = IfcStore.get_file()
return ifc_file is not None
def execute(self, context):
from ifc4d.ifc2p6 import Ifc2P6
self.file = IfcStore.get_file()
start = time.time()
ifc2p6 = Ifc2P6()
ifc2p6.xml = self.filepath
ifc2p6.file = self.file
ifc2p6.execute()
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
class EnableEditingWorkCalendarTimes(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_calendar_times"
bl_label = "Enable Editing Work Calendar Times"
+3 -2
View File
@@ -6,17 +6,18 @@ Ifc4D contains a series of utilities for converting to and from various 4D softw
- Oracle Primavera 6 (P6) XML to IFC
- Oracle Primavera 6 (P6) XER to IFC
- Asta Powerproject to IFC
- IFC to Oracle Primavera 6 (P6) XML
Planned (would you like to contribute? Please reach out!):
- IFC to Microsoft Project
- IFC to Oracle Primavera 6 (P6)
- IFC to Oracle Primavera 6 (P6) XER
- IFC to Oracle Primavera 6 (P6) XLS
- IFC to Asta Powerproject
- LibreProject to IFC
- IFC to LibreProject
- IFC to Gantt
## Useful links
- [P6 EPPM XER Import/Export Data Map Guide](https://docs.oracle.com/cd/F12057_01/English/Mapping_and_Schema/xer_import_export_data_map_project/helpmain.htm?toc.htm?97881.htm)
+176
View File
@@ -0,0 +1,176 @@
# Ifc4D - IFC scheduling utility
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Ifc4D.
#
# Ifc4D is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ifc4D is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Ifc4D. If not, see <http://www.gnu.org/licenses/>.
import math
import datetime
import ifcopenshell
import ifcopenshell.util.date
import xml.etree.ElementTree as ET
from .common import ScheduleIfcGenerator
class Ifc2P6:
def __init__(self):
self.xml = None
self.file = None
self.id = 0
self.id_map = {}
self.element_map = {}
self.hours_per_day = 8
self.project_tasks = []
def execute(self):
self.root = ET.Element("APIBusinessObjects")
self.schedule = self.file.by_type("IfcWorkSchedule")[0]
self.create_obs()
self.create_calendars()
self.create_projects()
tree = ET.ElementTree(self.root)
tree.write(self.xml)
def create_obs(self):
element = self.file.by_type("IfcProject")[0]
obs = ET.SubElement(self.root, "OBS")
self.link_element(element, obs)
ET.SubElement(obs, "Name").text = element.Name or "Unnamed"
def create_calendars(self):
for calendar in self.file.by_type("IfcWorkCalendar"):
self.create_calendar(calendar)
def create_calendar(self, calendar):
el = ET.SubElement(self.root, "Calendar")
ET.SubElement(el, "Name").text = calendar.Name or "Unnamed"
self.link_element(calendar, el)
# Hardcode for now
work_week = ET.SubElement(el, "StandardWorkWeek")
work_hours = ET.SubElement(work_week, "StandardWorkHours")
day = ET.SubElement(work_hours, "DayOfWeek").text = "Sunday"
for name in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]:
work_hours = ET.SubElement(work_week, "StandardWorkHours")
day = ET.SubElement(work_hours, "DayOfWeek").text = name
work_time = ET.SubElement(work_hours, "WorkTime")
ET.SubElement(work_time, "Start").text = "08:00:00"
ET.SubElement(work_time, "Finish").text = "17:00:00"
holidays = ET.SubElement(el, "HolidayOrExceptions")
holiday = ET.SubElement(holidays, "HolidayOrException")
date = ET.SubElement(holidays, "Date").text = "2022-01-01T00:00:00"
holiday = ET.SubElement(holidays, "HolidayOrException")
date = ET.SubElement(holidays, "Date").text = "2022-01-02T00:00:00"
holiday = ET.SubElement(holidays, "HolidayOrException")
date = ET.SubElement(holidays, "Date").text = "2022-01-03T00:00:00"
def create_projects(self):
for work_schedule in self.file.by_type("IfcWorkSchedule"):
self.create_project(work_schedule)
def create_project(self, work_schedule):
project = ET.SubElement(self.root, "Project")
self.link_element(work_schedule, project)
ET.SubElement(project, "ActivityDefaultCalendarObjectId").text = self.id_map[self.file.by_type("IfcWorkCalendar")[0]]
ET.SubElement(project, "Name").text = work_schedule.Name or "Unnamed"
ET.SubElement(project, "OBSObjectId").text = self.id_map[self.file.by_type("IfcProject")[0]]
self.project_tasks = []
self.create_wbs(self.get_subtasks(work_schedule), work_schedule)
self.create_relationships(self.project_tasks, work_schedule)
def create_wbs(self, tasks, work_schedule, parent=None):
self.project_tasks.extend(tasks)
for task in tasks:
subtasks = self.get_subtasks(task)
if not subtasks:
self.create_activity(task, work_schedule, parent=parent)
continue
wbs = ET.SubElement(self.element_map[work_schedule], "WBS")
self.link_element(task, wbs)
ET.SubElement(wbs, "Code").text = task.Identification or "X"
ET.SubElement(wbs, "Name").text = task.Name or "Unnamed"
if parent:
ET.SubElement(wbs, "ParentObjectId").text = self.id_map[parent]
ET.SubElement(wbs, "ProjectObjectId").text = self.id_map[work_schedule]
self.create_wbs(subtasks, work_schedule, parent=task)
def create_activity(self, task, work_schedule, parent=None):
activity = ET.SubElement(self.element_map[work_schedule], "Activity")
self.link_element(task, activity)
ET.SubElement(activity, "Id").text = task.Identification or "X"
ET.SubElement(activity, "Name").text = task.Name or "Unnamed"
if parent:
ET.SubElement(activity, "WBSObjectId").text = self.id_map[parent]
if task.TaskTime:
if task.TaskTime.ScheduleDuration:
duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration)
ET.SubElement(activity, "PlannedDuration").text = str(duration.days * self.hours_per_day)
if task.TaskTime.ScheduleStart:
ET.SubElement(activity, "PlannedStartDate").text = task.TaskTime.ScheduleStart
ET.SubElement(activity, "StartDate").text = task.TaskTime.ScheduleStart
if task.TaskTime.ScheduleFinish:
ET.SubElement(activity, "PlannedFinishDate").text = task.TaskTime.ScheduleFinish
ET.SubElement(activity, "FinishDate").text = task.TaskTime.ScheduleFinish
ET.SubElement(activity, "ProjectObjectId").text = self.id_map[work_schedule]
def create_relationships(self, tasks, work_schedule):
for task in tasks:
for rel in task.IsSuccessorFrom or []:
predecessor = rel.RelatingProcess
relationship = ET.SubElement(self.element_map[work_schedule], "Relationship")
self.link_element(rel, relationship)
if rel.TimeLag and rel.TimeLag.LagValue:
duration = ifcopenshell.util.date.ifc2datetime(rel.TimeLag.LagValue.wrappedValue)
ET.SubElement(relationship, "Lag").text = str(duration.days * self.hours_per_day)
else:
ET.SubElement(relationship, "Lag").text = "0"
ET.SubElement(relationship, "PredecessorProjectObjectId").text = self.id_map[work_schedule]
ET.SubElement(relationship, "SuccessorProjectObjectId").text = self.id_map[work_schedule]
ET.SubElement(relationship, "PredecessorActivityObjectId").text = self.id_map[predecessor]
ET.SubElement(relationship, "SuccessorActivityObjectId").text = self.id_map[task]
ET.SubElement(relationship, "Type").text = {
"START_START": "Start to Start",
"START_FINISH": "Start to Finish",
"FINISH_START": "Finish to Start",
"FINISH_FINISH": "Finish to Finish",
None: "Finish to Start",
}[rel.SequenceType]
def get_subtasks(self, element):
tasks = []
if element.is_a("IfcWorkSchedule"):
for rel in element.Controls or []:
tasks.extend([e for e in rel.RelatedObjects if e.is_a("IfcTask")])
elif element.is_a("IfcTask"):
for rel in element.IsNestedBy or []:
tasks.extend([e for e in rel.RelatedObjects if e.is_a("IfcTask")])
return tasks
def link_element(self, element, el):
self.id += 1
ET.SubElement(el, "ObjectId").text = str(self.id)
self.id_map[element] = str(self.id)
self.element_map[element] = el
return self.id