mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
Build untested IFC to P6 XML export feature.
This commit is contained in:
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user