Files
2022-04-07 09:12:58 +02:00

649 lines
22 KiB
C#

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
namespace Revit.SDK.Samples.TraverseSystem.CS
{
/// <summary>
/// A TreeNode object represents an element in the system
/// </summary>
public class TreeNode
{
#region Member variables
/// <summary>
/// Id of the element
/// </summary>
private Autodesk.Revit.DB.ElementId m_Id;
/// <summary>
/// Flow direction of the node
/// For the starting element of the traversal, the direction will be the same as the connector
/// connected to its following element; Otherwise it will be the direction of the connector connected to
/// its previous element
/// </summary>
private FlowDirectionType m_direction;
/// <summary>
/// The parent node of the current node.
/// </summary>
private TreeNode m_parent;
/// <summary>
/// The connector of the previous element to which current element is connected
/// </summary>
private Connector m_inputConnector;
/// <summary>
/// The first-level child nodes of the current node
/// </summary>
private List<TreeNode> m_childNodes;
/// <summary>
/// Active document of Revit
/// </summary>
private Document m_document;
#endregion
#region Properties
/// <summary>
/// Id of the element
/// </summary>
public Autodesk.Revit.DB.ElementId Id
{
get
{
return m_Id;
}
}
/// <summary>
/// Flow direction of the node
/// </summary>
public FlowDirectionType Direction
{
get
{
return m_direction;
}
set
{
m_direction = value;
}
}
/// <summary>
/// Gets and sets the parent node of the current node.
/// </summary>
public TreeNode Parent
{
get
{
return m_parent;
}
set
{
m_parent = value;
}
}
/// <summary>
/// Gets and sets the first-level child nodes of the current node
/// </summary>
public List<TreeNode> ChildNodes
{
get
{
return m_childNodes;
}
set
{
m_childNodes = value;
}
}
/// <summary>
/// The connector of the previous element to which current element is connected
/// </summary>
public Connector InputConnector
{
get
{
return m_inputConnector;
}
set
{
m_inputConnector = value;
}
}
#endregion
#region Methods
/// <summary>
/// Constructor
/// </summary>
/// <param name="doc">Revit document</param>
/// <param name="id">Element's Id</param>
public TreeNode(Document doc, Autodesk.Revit.DB.ElementId id)
{
m_document = doc;
m_Id = id;
m_childNodes = new List<TreeNode>();
}
/// <summary>
/// Get Element by its Id
/// </summary>
/// <param name="eid">Element's Id</param>
/// <returns>Element</returns>
private Element GetElementById(Autodesk.Revit.DB.ElementId eid)
{
return m_document.GetElement(eid);
}
/// <summary>
/// Dump the node into XML file
/// </summary>
/// <param name="writer">XmlWriter object</param>
public void DumpIntoXML(XmlWriter writer)
{
// Write node information
Element element = GetElementById(m_Id);
FamilyInstance fi = element as FamilyInstance;
if (fi != null)
{
MEPModel mepModel = fi.MEPModel;
String type = String.Empty;
if (mepModel is MechanicalEquipment)
{
type = "MechanicalEquipment";
writer.WriteStartElement(type);
}
else if (mepModel is MechanicalFitting)
{
MechanicalFitting mf = mepModel as MechanicalFitting;
type = "MechanicalFitting";
writer.WriteStartElement(type);
writer.WriteAttributeString("Category", element.Category.Name);
writer.WriteAttributeString("PartType", mf.PartType.ToString());
}
else
{
type = "FamilyInstance";
writer.WriteStartElement(type);
writer.WriteAttributeString("Category", element.Category.Name);
}
writer.WriteAttributeString("Name", element.Name);
writer.WriteAttributeString("Id", element.Id.ToString());
writer.WriteAttributeString("Direction", m_direction.ToString());
writer.WriteEndElement();
}
else
{
String type = element.GetType().Name;
writer.WriteStartElement(type);
writer.WriteAttributeString("Name", element.Name);
writer.WriteAttributeString("Id", element.Id.ToString());
writer.WriteAttributeString("Direction", m_direction.ToString());
writer.WriteEndElement();
}
foreach (TreeNode node in m_childNodes)
{
if (m_childNodes.Count > 1)
{
writer.WriteStartElement("Path");
}
node.DumpIntoXML(writer);
if (m_childNodes.Count > 1)
{
writer.WriteEndElement();
}
}
}
#endregion
}
/// <summary>
/// Data structure of the traversal
/// </summary>
public class TraversalTree
{
#region Member variables
// Active document of Revit
private Document m_document;
// The MEP system of the traversal
private MEPSystem m_system;
// The flag whether the MEP system of the traversal is a mechanical system or piping system
private Boolean m_isMechanicalSystem;
// The starting element node
private TreeNode m_startingElementNode;
#endregion
#region Methods
/// <summary>
/// Constructor
/// </summary>
/// <param name="activeDocument">Revit document</param>
/// <param name="system">The MEP system to traverse</param>
public TraversalTree(Document activeDocument, MEPSystem system)
{
m_document = activeDocument;
m_system = system;
m_isMechanicalSystem = (system is MechanicalSystem);
}
/// <summary>
/// Traverse the system
/// </summary>
public void Traverse()
{
// Get the starting element node
m_startingElementNode = GetStartingElementNode();
// Traverse the system recursively
Traverse(m_startingElementNode);
}
/// <summary>
/// Get the starting element node.
/// If the system has base equipment then get it;
/// Otherwise get the owner of the open connector in the system
/// </summary>
/// <returns>The starting element node</returns>
private TreeNode GetStartingElementNode()
{
TreeNode startingElementNode = null;
FamilyInstance equipment = m_system.BaseEquipment;
//
// If the system has base equipment then get it;
// Otherwise get the owner of the open connector in the system
if (equipment != null)
{
startingElementNode = new TreeNode(m_document, equipment.Id);
}
else
{
startingElementNode = new TreeNode(m_document, GetOwnerOfOpenConnector().Id);
}
startingElementNode.Parent = null;
startingElementNode.InputConnector = null;
return startingElementNode;
}
/// <summary>
/// Get the owner of the open connector as the starting element
/// </summary>
/// <returns>The owner</returns>
private Element GetOwnerOfOpenConnector()
{
Element element = null;
//
// Get an element from the system's terminals
ElementSet elements = m_system.Elements;
foreach (Element ele in elements)
{
element = ele;
break;
}
// Get the open connector recursively
Connector openConnector = GetOpenConnector(element, null);
return openConnector.Owner;
}
/// <summary>
/// Get the open connector of the system if the system has no base equipment
/// </summary>
/// <param name="element">An element in the system</param>
/// <param name="inputConnector">The connector of the previous element
/// to which the element is connected </param>
/// <returns>The found open connector</returns>
private Connector GetOpenConnector(Element element, Connector inputConnector)
{
Connector openConnector = null;
ConnectorManager cm = null;
//
// Get the connector manager of the element
if (element is FamilyInstance)
{
FamilyInstance fi = element as FamilyInstance;
cm = fi.MEPModel.ConnectorManager;
}
else
{
MEPCurve mepCurve = element as MEPCurve;
cm = mepCurve.ConnectorManager;
}
foreach (Connector conn in cm.Connectors)
{
// Ignore the connector does not belong to any MEP System or belongs to another different MEP system
if (conn.MEPSystem == null || conn.MEPSystem.Id != m_system.Id)
{
continue;
}
// If the connector is connected to the input connector, they will have opposite flow directions.
if (inputConnector != null && conn.IsConnectedTo(inputConnector))
{
continue;
}
// If the connector is not connected, it is the open connector
if (!conn.IsConnected)
{
openConnector = conn;
break;
}
//
// If open connector not found, then look for it from elements connected to the element
foreach (Connector refConnector in conn.AllRefs)
{
// Ignore non-EndConn connectors and connectors of the current element
if (refConnector.ConnectorType != ConnectorType.End ||
refConnector.Owner.Id == conn.Owner.Id)
{
continue;
}
// Ignore connectors of the previous element
if (inputConnector != null && refConnector.Owner.Id == inputConnector.Owner.Id)
{
continue;
}
openConnector = GetOpenConnector(refConnector.Owner, conn);
if (openConnector != null)
{
return openConnector;
}
}
}
return openConnector;
}
/// <summary>
/// Traverse the system recursively by analyzing each element
/// </summary>
/// <param name="elementNode">The element to be analyzed</param>
private void Traverse(TreeNode elementNode)
{
//
// Find all child nodes and analyze them recursively
AppendChildren(elementNode);
foreach (TreeNode node in elementNode.ChildNodes)
{
Traverse(node);
}
}
/// <summary>
/// Find all child nodes of the specified element node
/// </summary>
/// <param name="elementNode">The specified element node to be analyzed</param>
private void AppendChildren(TreeNode elementNode)
{
List<TreeNode> nodes = elementNode.ChildNodes;
ConnectorSet connectors;
//
// Get connector manager
Element element = GetElementById(elementNode.Id);
FamilyInstance fi = element as FamilyInstance;
if (fi != null)
{
connectors = fi.MEPModel.ConnectorManager.Connectors;
}
else
{
MEPCurve mepCurve = element as MEPCurve;
connectors = mepCurve.ConnectorManager.Connectors;
}
// Find connected connector for each connector
foreach (Connector connector in connectors)
{
MEPSystem mepSystem = connector.MEPSystem;
// Ignore the connector does not belong to any MEP System or belongs to another different MEP system
if (mepSystem == null || mepSystem.Id != m_system.Id)
{
continue;
}
//
// Get the direction of the TreeNode object
if (elementNode.Parent == null)
{
if (connector.IsConnected)
{
elementNode.Direction = connector.Direction;
}
}
else
{
// If the connector is connected to the input connector, they will have opposite flow directions.
// Then skip it.
if (connector.IsConnectedTo(elementNode.InputConnector))
{
elementNode.Direction = connector.Direction;
continue;
}
}
// Get the connector connected to current connector
Connector connectedConnector = GetConnectedConnector(connector);
if (connectedConnector != null)
{
TreeNode node = new TreeNode(m_document, connectedConnector.Owner.Id);
node.InputConnector = connector;
node.Parent = elementNode;
nodes.Add(node);
}
}
nodes.Sort(delegate(TreeNode t1, TreeNode t2)
{
return t1.Id > t2.Id ? 1 : (t1.Id < t2.Id ? -1 : 0);
}
);
}
/// <summary>
/// Get the connected connector of one connector
/// </summary>
/// <param name="connector">The connector to be analyzed</param>
/// <returns>The connected connector</returns>
static private Connector GetConnectedConnector(Connector connector)
{
Connector connectedConnector = null;
ConnectorSet allRefs = connector.AllRefs;
foreach (Connector conn in allRefs)
{
// Ignore non-EndConn connectors and connectors of the current element
if (conn.ConnectorType != ConnectorType.End ||
conn.Owner.Id == connector.Owner.Id)
{
continue;
}
connectedConnector = conn;
break;
}
return connectedConnector;
}
/// <summary>
/// Get element by its id
/// </summary>
private Element GetElementById(Autodesk.Revit.DB.ElementId eid)
{
return m_document.GetElement(eid);
}
/// <summary>
/// Dump the traversal into an XML file
/// </summary>
/// <param name="fileName">Name of the XML file</param>
public void DumpIntoXML(String fileName)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = " ";
XmlWriter writer = XmlWriter.Create(fileName, settings);
// Write the root element
String mepSystemType = String.Empty;
mepSystemType = (m_system is MechanicalSystem ? "MechanicalSystem" : "PipingSystem");
writer.WriteStartElement(mepSystemType);
// Write basic information of the MEP system
WriteBasicInfo(writer);
// Write paths of the traversal
WritePaths(writer);
// Close the root element
writer.WriteEndElement();
writer.Flush();
writer.Close();
}
/// <summary>
/// Write basic information of the MEP system into the XML file
/// </summary>
/// <param name="writer">XMLWriter object</param>
private void WriteBasicInfo(XmlWriter writer)
{
MechanicalSystem ms = null;
PipingSystem ps = null;
if (m_isMechanicalSystem)
{
ms = m_system as MechanicalSystem;
}
else
{
ps = m_system as PipingSystem;
}
// Write basic information of the system
writer.WriteStartElement("BasicInformation");
// Write Name property
writer.WriteStartElement("Name");
writer.WriteString(m_system.Name);
writer.WriteEndElement();
// Write Id property
writer.WriteStartElement("Id");
writer.WriteValue(m_system.Id.ToString());
writer.WriteEndElement();
// Write UniqueId property
writer.WriteStartElement("UniqueId");
writer.WriteString(m_system.UniqueId);
writer.WriteEndElement();
// Write SystemType property
writer.WriteStartElement("SystemType");
if (m_isMechanicalSystem)
{
writer.WriteString(ms.SystemType.ToString());
}
else
{
writer.WriteString(ps.SystemType.ToString());
}
writer.WriteEndElement();
// Write Category property
writer.WriteStartElement("Category");
writer.WriteAttributeString("Id", m_system.Category.Id.ToString());
writer.WriteAttributeString("Name", m_system.Category.Name);
writer.WriteEndElement();
// Write IsWellConnected property
writer.WriteStartElement("IsWellConnected");
if (m_isMechanicalSystem)
{
writer.WriteValue(ms.IsWellConnected);
}
else
{
writer.WriteValue(ps.IsWellConnected);
}
writer.WriteEndElement();
// Write HasBaseEquipment property
writer.WriteStartElement("HasBaseEquipment");
bool hasBaseEquipment = ((m_system.BaseEquipment == null) ? false : true);
writer.WriteValue(hasBaseEquipment);
writer.WriteEndElement();
// Write TerminalElementsCount property
writer.WriteStartElement("TerminalElementsCount");
writer.WriteValue(m_system.Elements.Size);
writer.WriteEndElement();
// Write Flow property
writer.WriteStartElement("Flow");
if (m_isMechanicalSystem)
{
writer.WriteValue(ms.GetFlow());
}
else
{
writer.WriteValue(ps.GetFlow());
}
writer.WriteEndElement();
// Close basic information
writer.WriteEndElement();
}
/// <summary>
/// Write paths of the traversal into the XML file
/// </summary>
/// <param name="writer">XMLWriter object</param>
private void WritePaths(XmlWriter writer)
{
writer.WriteStartElement("Path");
m_startingElementNode.DumpIntoXML(writer);
writer.WriteEndElement();
}
#endregion
}
}