Application: Plant 3D API
Category: Piping & Equipment
Difficulty: Intermediate
Language: C#
You need to extract piping data from Plant 3D models - such as pipe specifications, lengths, fittings, and equipment connections - for material takeoffs, clash detection reports, or integration with other systems.
Use the Plant 3D .NET API to access piping objects, read their properties, and traverse pipe runs. Plant 3D extends AutoCAD with specialized classes for process plant design.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.ProcessPower.PnP3dObjects;
using Autodesk.ProcessPower.DataLinks;
using System.Collections.Generic;
using System.Text;
[CommandMethod("EXTRACTPIPEDATA")]
public void ExtractPipeData()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// Get all pipes in the drawing
TypedValue[] filterList = new TypedValue[]
{
new TypedValue((int)DxfCode.Start, "PIPE")
};
SelectionFilter filter = new SelectionFilter(filterList);
PromptSelectionResult psr = ed.SelectAll(filter);
if (psr.Status != PromptStatus.OK)
{
ed.WriteMessage("\nNo pipes found in drawing.");
return;
}
StringBuilder report = new StringBuilder();
report.AppendLine("PIPE DATA EXTRACTION REPORT");
report.AppendLine("=" + new string('=', 70));
report.AppendLine();
double totalLength = 0;
Dictionary<string, double> specLengths = new Dictionary<string, double>();
Dictionary<string, int> specCounts = new Dictionary<string, int>();
foreach (SelectedObject selObj in psr.Value)
{
if (selObj == null) continue;
Pipe pipe = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Pipe;
if (pipe != null)
{
// Get pipe properties
string pipeTag = pipe.PipeTag;
string spec = pipe.Spec;
double nominalDiameter = pipe.NominalDiameter;
double length = pipe.Length;
string material = pipe.Material;
string schedule = pipe.Schedule;
// Get start and end points
Point3d startPoint = pipe.StartPoint;
Point3d endPoint = pipe.EndPoint;
// Get connected equipment
string startConnection = GetConnectedEquipment(pipe.StartPort, tr);
string endConnection = GetConnectedEquipment(pipe.EndPort, tr);
// Add to report
report.AppendLine($"Pipe Tag: {pipeTag}");
report.AppendLine($" Spec: {spec}");
report.AppendLine($" Size: {nominalDiameter}\"");
report.AppendLine($" Length: {length:F2} mm");
report.AppendLine($" Material: {material}");
report.AppendLine($" Schedule: {schedule}");
report.AppendLine($" Start: {startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}");
report.AppendLine($" End: {endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}");
if (!string.IsNullOrEmpty(startConnection))
report.AppendLine($" Start Connection: {startConnection}");
if (!string.IsNullOrEmpty(endConnection))
report.AppendLine($" End Connection: {endConnection}");
report.AppendLine();
// Accumulate totals
totalLength += length;
if (!specLengths.ContainsKey(spec))
{
specLengths[spec] = 0;
specCounts[spec] = 0;
}
specLengths[spec] += length;
specCounts[spec]++;
}
}
// Add summary
report.AppendLine("\nSUMMARY BY SPECIFICATION");
report.AppendLine("=" + new string('=', 70));
foreach (var spec in specLengths.Keys)
{
report.AppendLine($"{spec}:");
report.AppendLine($" Count: {specCounts[spec]} pipes");
report.AppendLine($" Total Length: {specLengths[spec]:F2} mm ({specLengths[spec] / 1000:F2} m)");
report.AppendLine();
}
report.AppendLine($"GRAND TOTAL LENGTH: {totalLength:F2} mm ({totalLength / 1000:F2} m)");
tr.Commit();
// Save report
string reportPath = @"C:\Temp\PipeDataReport.txt";
System.IO.File.WriteAllText(reportPath, report.ToString());
ed.WriteMessage($"\nReport saved to: {reportPath}");
// Open report
System.Diagnostics.Process.Start("notepad.exe", reportPath);
}
}
private string GetConnectedEquipment(Port port, Transaction tr)
{
if (port == null || port.ConnectedPorts.Count == 0)
return string.Empty;
foreach (ObjectId connectedPortId in port.ConnectedPorts)
{
Port connectedPort = tr.GetObject(connectedPortId, OpenMode.ForRead) as Port;
if (connectedPort != null)
{
ObjectId ownerId = connectedPort.OwnerId;
DBObject owner = tr.GetObject(ownerId, OpenMode.ForRead);
// Check if connected to equipment
if (owner is Equipment equipment)
{
return equipment.Tag;
}
}
}
return string.Empty;
}
[CommandMethod("EXTRACTFITTINGS")]
public void ExtractFittingsData()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// Get all fittings
TypedValue[] filterList = new TypedValue[]
{
new TypedValue((int)DxfCode.Start, "PIPEFITTING,VALVE")
};
SelectionFilter filter = new SelectionFilter(filterList);
PromptSelectionResult psr = ed.SelectAll(filter);
if (psr.Status != PromptStatus.OK)
{
ed.WriteMessage("\nNo fittings found.");
return;
}
Dictionary<string, int> fittingCounts = new Dictionary<string, int>();
foreach (SelectedObject selObj in psr.Value)
{
if (selObj == null) continue;
PipeFitting fitting = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as PipeFitting;
if (fitting != null)
{
string fittingType = fitting.PartSizeProperties.PartFamily;
string size = fitting.PartSizeProperties.NominalDiameter.ToString();
string key = $"{fittingType} - {size}\"";
if (!fittingCounts.ContainsKey(key))
fittingCounts[key] = 0;
fittingCounts[key]++;
}
}
tr.Commit();
// Display summary
StringBuilder summary = new StringBuilder();
summary.AppendLine("FITTINGS SUMMARY");
summary.AppendLine("================\n");
foreach (var kvp in fittingCounts.OrderBy(x => x.Key))
{
summary.AppendLine($"{kvp.Key}: {kvp.Value}");
}
ed.WriteMessage("\n" + summary.ToString());
}
}
using System.IO;
using OfficeOpenXml; // EPPlus library
[CommandMethod("EXPORTMTO")]
public void ExportMaterialTakeoff()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// Collect all piping components
List<PipeData> pipes = new List<PipeData>();
List<FittingData> fittings = new List<FittingData>();
// Get pipes
TypedValue[] pipeFilter = new TypedValue[] { new TypedValue((int)DxfCode.Start, "PIPE") };
PromptSelectionResult pipeSel = ed.SelectAll(new SelectionFilter(pipeFilter));
if (pipeSel.Status == PromptStatus.OK)
{
foreach (SelectedObject selObj in pipeSel.Value)
{
Pipe pipe = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Pipe;
if (pipe != null)
{
pipes.Add(new PipeData
{
Tag = pipe.PipeTag,
Spec = pipe.Spec,
Size = pipe.NominalDiameter,
Length = pipe.Length,
Material = pipe.Material,
Schedule = pipe.Schedule
});
}
}
}
// Get fittings
TypedValue[] fittingFilter = new TypedValue[] { new TypedValue((int)DxfCode.Start, "PIPEFITTING") };
PromptSelectionResult fittingSel = ed.SelectAll(new SelectionFilter(fittingFilter));
if (fittingSel.Status == PromptStatus.OK)
{
foreach (SelectedObject selObj in fittingSel.Value)
{
PipeFitting fitting = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as PipeFitting;
if (fitting != null)
{
fittings.Add(new FittingData
{
Type = fitting.PartSizeProperties.PartFamily,
Size = fitting.PartSizeProperties.NominalDiameter,
Spec = fitting.Spec
});
}
}
}
tr.Commit();
// Export to Excel
string excelPath = @"C:\Temp\MaterialTakeoff.xlsx";
ExportToExcel(pipes, fittings, excelPath);
ed.WriteMessage($"\nMaterial takeoff exported to: {excelPath}");
System.Diagnostics.Process.Start(excelPath);
}
}
private void ExportToExcel(List<PipeData> pipes, List<FittingData> fittings, string filePath)
{
using (ExcelPackage package = new ExcelPackage())
{
// Pipe sheet
ExcelWorksheet pipeSheet = package.Workbook.Worksheets.Add("Pipes");
pipeSheet.Cells[1, 1].Value = "Tag";
pipeSheet.Cells[1, 2].Value = "Spec";
pipeSheet.Cells[1, 3].Value = "Size";
pipeSheet.Cells[1, 4].Value = "Length (mm)";
pipeSheet.Cells[1, 5].Value = "Material";
pipeSheet.Cells[1, 6].Value = "Schedule";
int row = 2;
foreach (var pipe in pipes)
{
pipeSheet.Cells[row, 1].Value = pipe.Tag;
pipeSheet.Cells[row, 2].Value = pipe.Spec;
pipeSheet.Cells[row, 3].Value = pipe.Size;
pipeSheet.Cells[row, 4].Value = pipe.Length;
pipeSheet.Cells[row, 5].Value = pipe.Material;
pipeSheet.Cells[row, 6].Value = pipe.Schedule;
row++;
}
// Fittings sheet
ExcelWorksheet fittingSheet = package.Workbook.Worksheets.Add("Fittings");
fittingSheet.Cells[1, 1].Value = "Type";
fittingSheet.Cells[1, 2].Value = "Size";
fittingSheet.Cells[1, 3].Value = "Spec";
fittingSheet.Cells[1, 4].Value = "Quantity";
var fittingGroups = fittings.GroupBy(f => new { f.Type, f.Size, f.Spec })
.Select(g => new { g.Key, Count = g.Count() });
row = 2;
foreach (var group in fittingGroups)
{
fittingSheet.Cells[row, 1].Value = group.Key.Type;
fittingSheet.Cells[row, 2].Value = group.Key.Size;
fittingSheet.Cells[row, 3].Value = group.Key.Spec;
fittingSheet.Cells[row, 4].Value = group.Count;
row++;
}
// Save file
FileInfo file = new FileInfo(filePath);
package.SaveAs(file);
}
}
// Helper classes
public class PipeData
{
public string Tag { get; set; }
public string Spec { get; set; }
public double Size { get; set; }
public double Length { get; set; }
public string Material { get; set; }
public string Schedule { get; set; }
}
public class FittingData
{
public string Type { get; set; }
public double Size { get; set; }
public string Spec { get; set; }
}
Mistake: Assuming length units
// WRONG - mixing units
double lengthInMeters = pipe.Length; // Actually in mm!
// CORRECT - convert to desired units
double lengthInMeters = pipe.Length / 1000; // mm to m
double lengthInFeet = pipe.Length / 304.8; // mm to ft
Mistake: Not checking port connections
// WRONG - may throw null reference
ObjectId connectedId = pipe.StartPort.ConnectedPorts[0];
// CORRECT - check first
if (pipe.StartPort != null && pipe.StartPort.ConnectedPorts.Count > 0)
{
ObjectId connectedId = pipe.StartPort.ConnectedPorts[0];
}
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.ProcessPower.PnP3dObjects;
using Autodesk.ProcessPower.DataLinks;
using System.Collections.Generic;
using System.Text;
[CommandMethod("EXTRACTPIPEDATA")]
public void ExtractPipeData()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// Get all pipes in the drawing
TypedValue[] filterList = new TypedValue[]
{
new TypedValue((int)DxfCode.Start, "PIPE")
};
SelectionFilter filter = new SelectionFilter(filterList);
PromptSelectionResult psr = ed.SelectAll(filter);
if (psr.Status != PromptStatus.OK)
{
ed.WriteMessage("\nNo pipes found in drawing.");
return;
}
StringBuilder report = new StringBuilder();
report.AppendLine("PIPE DATA EXTRACTION REPORT");
report.AppendLine("=" + new string('=', 70));
report.AppendLine();
double totalLength = 0;
Dictionary<string, double> specLengths = new Dictionary<string, double>();
Dictionary<string, int> specCounts = new Dictionary<string, int>();
foreach (SelectedObject selObj in psr.Value)
{
if (selObj == null) continue;
Pipe pipe = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Pipe;
if (pipe != null)
{
// Get pipe properties
string pipeTag = pipe.PipeTag;
string spec = pipe.Spec;
double nominalDiameter = pipe.NominalDiameter;
double length = pipe.Length;
string material = pipe.Material;
string schedule = pipe.Schedule;
// Get start and end points
Point3d startPoint = pipe.StartPoint;
Point3d endPoint = pipe.EndPoint;
// Get connected equipment
string startConnection = GetConnectedEquipment(pipe.StartPort, tr);
string endConnection = GetConnectedEquipment(pipe.EndPort, tr);
// Add to report
report.AppendLine($"Pipe Tag: {pipeTag}");
report.AppendLine($" Spec: {spec}");
report.AppendLine($" Size: {nominalDiameter}\"");
report.AppendLine($" Length: {length:F2} mm");
report.AppendLine($" Material: {material}");
report.AppendLine($" Schedule: {schedule}");
report.AppendLine($" Start: {startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}");
report.AppendLine($" End: {endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}");
if (!string.IsNullOrEmpty(startConnection))
report.AppendLine($" Start Connection: {startConnection}");
if (!string.IsNullOrEmpty(endConnection))
report.AppendLine($" End Connection: {endConnection}");
report.AppendLine();
// Accumulate totals
totalLength += length;
if (!specLengths.ContainsKey(spec))
{
specLengths[spec] = 0;
specCounts[spec] = 0;
}
specLengths[spec] += length;
specCounts[spec]++;
}
}
// Add summary
report.AppendLine("\nSUMMARY BY SPECIFICATION");
report.AppendLine("=" + new string('=', 70));
foreach (var spec in specLengths.Keys)
{
report.AppendLine($"{spec}:");
report.AppendLine($" Count: {specCounts[spec]} pipes");
report.AppendLine($" Total Length: {specLengths[spec]:F2} mm ({specLengths[spec] / 1000:F2} m)");
report.AppendLine();
}
report.AppendLine($"GRAND TOTAL LENGTH: {totalLength:F2} mm ({totalLength / 1000:F2} m)");
tr.Commit();
// Save report
string reportPath = @"C:\Temp\PipeDataReport.txt";
System.IO.File.WriteAllText(reportPath, report.ToString());
ed.WriteMessage($"\nReport saved to: {reportPath}");
// Open report
System.Diagnostics.Process.Start("notepad.exe", reportPath);
}
}
private string GetConnectedEquipment(Port port, Transaction tr)
{
if (port == null || port.ConnectedPorts.Count == 0)
return string.Empty;
foreach (ObjectId connectedPortId in port.ConnectedPorts)
{
Port connectedPort = tr.GetObject(connecte