应用程序名称:RoutingPreferenceTools

Revit平台:MEP

Revit版本:2013.0

首次发布于:2013.0

编程语言:C#

技能水平:中等

类别:MEP

类型:ExternalCommand

主题:关于管道/电缆走向偏好的工具

概要:此示例包含三个命令。

[注意] - 所有示例只有在从MEP文档模板(例如“Systems-Default.rte”)创建时才能正常工作。

管路/电缆走向偏好分析

主题:分析给定管道类型的走向偏好,以检查常见问题。

概要:此示例演示如何使用管道/电缆走向偏好API来查看给定PipeType的所有规则和标准,并检查几个常见问题,包括:

1)一组规则(例如“弯头”)中的第一条规则的范围为“无”。

2)一组中的所有规则的范围均为“无”。

3)设置了TeeTap的首选连接器类型,但未分配该类型的配件到规则中。

4)一段管道的尺寸范围未被弯头、连接器或十字管道所覆盖。

相关类:

Autodesk.Revit.DB.Plumbing

Autodesk.Revit.DB

项目文件:

Command.cs

Analyzer.cs

执行所有管道/电缆走向偏好分析并导出一个带有XML数据报告任何警告的XDocument

PartIdInfo.cs

包含管道/电缆走向偏好规则组和与通过管道/电缆走向偏好管理器指定的标准找到的段和配件相对应的ElementIds列表。

说明:

1. Revit MEP中,打开或创建一个新文档,加载几个管道配件,为几个管道类型定义几个管道段。

2. 在管道类型的走向偏好管理器中,添加这些配件和段,并设置最小和最大尺寸范围。

3. 运行管道/电缆走向偏好分析命令,并选择一个管道类型。

4. 单击“查找问题”按钮并阅读包含上述警告的XML输出。

要查看你的管道/电缆走向偏好将使用哪些段和配件来给定尺寸,请从组合框中选择一个管道尺寸,单击“检查特定尺寸”按钮,然后阅读输出。

源代码:

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

Analyzer.cs

//
// (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.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;

namespace Revit.SDK.Samples.RoutingPreferenceTools.CS
{

/// <summary>
/// Queries all routing preferences and reports potential problems in the form of an XDocument.
/// </summary>
internal class Analyzer
{
#region Data
private Autodesk.Revit.DB.Document m_document;
private Autodesk.Revit.DB.RoutingPreferenceManager m_routingPreferenceManager;
private double m_mepSize;
#endregion

#region Public interface
/// <summary>
/// Constructor
/// </summary>
/// <param name="routingPreferenceManager"></param>
/// <param name="document"></param>
public Analyzer(RoutingPreferenceManager routingPreferenceManager, Document document)
{
m_routingPreferenceManager = routingPreferenceManager;
m_document = document;
m_mepSize = 0;
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="routingPreferenceManager"></param>
/// <param name="mepSize"></param>
/// <param name="document"></param>
public Analyzer(RoutingPreferenceManager routingPreferenceManager, double mepSize, Document document)
{
m_routingPreferenceManager = routingPreferenceManager;
m_document = document;

m_mepSize = Convert.ConvertValueToFeet(mepSize, m_document);
}

/// <summary>
/// Get specific size query
/// </summary>
/// <returns></returns>
public XDocument GetSpecificSizeQuery()
{

XDocument xReportDoc = new XDocument();
XElement xroot = new XElement(XName.Get("RoutingPreferenceAnalysisSizeQuery"));
xroot.Add(GetHeaderInformation());

foreach (PartIdInfo partId in GetPreferredFittingsAndSegments())
{
xroot.Add(partId.GetXml(m_document));
}

xReportDoc.Add(xroot);
return xReportDoc;

}

/// <summary>
/// Get all segments from a the currently selected pipe type, get each size from each segment,
/// collect, sort, and return.
/// </summary>
public static List<double> GetAvailableSegmentSizes(RoutingPreferenceManager routingPreferenceManager, Autodesk.Revit.DB.Document document)
{

System.Collections.Generic.HashSet<double> sizes = new HashSet<double>();
int segmentCount = routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Segments);
for (int index = 0; index != segmentCount; ++index)
{
RoutingPreferenceRule segmentRule = routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Segments, index);

Segment segment = document.GetElement(segmentRule.MEPPartId) as Segment;
foreach (MEPSize size in segment.GetSizes())
{
sizes.Add(size.NominalDiameter);
}
}
List<double> sizesSorted = sizes.ToList();
sizesSorted.Sort();
return sizesSorted;
}

/// <summary>
/// Returns XML data for a variety of potential routing-preference problems.
/// </summary>
/// <returns></returns>
public XDocument GetWarnings()
{
XDocument xReportDoc = new XDocument();
XElement xroot = new XElement(XName.Get("RoutingPreferenceAnalysis"));
xroot.Add(GetHeaderInformation());
XElement xWarnings = new XElement(XName.Get("Warnings"));

//None-range-warnings
foreach (RoutingPreferenceRuleGroupType groupType in Enum.GetValues(typeof(RoutingPreferenceRuleGroupType)).Cast<RoutingPreferenceRuleGroupType>())
{
if (IsRuleSetToRangeNone(m_routingPreferenceManager, groupType, 0))
{

XElement xNoRangeSet = new XElement(XName.Get("NoRangeSet"));
xNoRangeSet.Add(new XAttribute(XName.Get("groupType"), groupType.ToString()));

if (IsGroupSetToRangeNone(m_routingPreferenceManager, groupType))
{
xNoRangeSet.Add(new XAttribute(XName.Get("rule"), "allRules"));
}
else
{
xNoRangeSet.Add(new XAttribute(XName.Get("rule"), "firstRule"));
}
xWarnings.Add(xNoRangeSet);
}
}

//tee/tap warnings

if (!IsPreferredJunctionTypeValid(m_routingPreferenceManager))
{
XElement xJunctionFittingsNotDefined = new XElement(XName.Get("FittingsNotDefinedForPreferredJunction"));
xWarnings.Add(xJunctionFittingsNotDefined);
}

//size range warnings for elbow, Junction, and Cross

XElement xSegmentElbowWarning = GetSegmentRangeNotCoveredWarning(m_routingPreferenceManager, RoutingPreferenceRuleGroupType.Elbows);
if (xSegmentElbowWarning != null)
xWarnings.Add(xSegmentElbowWarning);
XElement xSegmentTeeWarning = GetSegmentRangeNotCoveredWarning(m_routingPreferenceManager, RoutingPreferenceRuleGroupType.Junctions);
if (xSegmentTeeWarning != null)
xWarnings.Add(xSegmentTeeWarning);
XElement xSegmentCrossWarning = GetSegmentRangeNotCoveredWarning(m_routingPreferenceManager, RoutingPreferenceRuleGroupType.Crosses);
if (xSegmentCrossWarning != null)
xWarnings.Add(xSegmentCrossWarning);

xroot.Add(xWarnings);
xReportDoc.Add(xroot);
return xReportDoc;
}
#endregion

#region Analyis helper methods

/// <summary>
/// Get basic information about the PipeType
/// </summary>
private XElement GetHeaderInformation()
{
XElement xHeader = new XElement(XName.Get("PipeType"));
string pipeTypeName = m_document.GetElement(m_routingPreferenceManager.OwnerId).Name;

xHeader.Add(new XAttribute(XName.Get("name"), pipeTypeName));
xHeader.Add(new XAttribute(XName.Get("elementId"), m_routingPreferenceManager.OwnerId.ToString()));
return xHeader;
}

/// <summary>
/// Checks to see if any segments in the routing preference manager have sizes that cannot be fitted with fittings defined in a rule group type, such as "Elbow."
/// For example, if a segment rule defines a segment be used from sizes 2" to 12", and there are three elbows rules defined to be used from ranges
/// 2"-4", 4"-7", and 9"-14", this method will return warning information specifying the sizes (8", 8.5", etc...) not covered by elbow fittings.
/// </summary>
private XElement GetSegmentRangeNotCoveredWarning(RoutingPreferenceManager routingPreferenceManager, RoutingPreferenceRuleGroupType groupType)
{
for (int index = 0; index != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Segments); ++index)
{
RoutingPreferenceRule rule = routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Segments, index);
if (rule.MEPPartId == ElementId.InvalidElementId)
continue;

if (rule.NumberOfCriteria == 0) //double check all/none
continue;

PrimarySizeCriterion psc = rule.GetCriterion(0) as PrimarySizeCriterion;

PipeSegment segment = m_document.GetElement(rule.MEPPartId) as PipeSegment;
List<double> sizesNotCovered = new List<double>();
bool isCovered = CheckSegmentForValidCoverage(routingPreferenceManager, psc.MinimumSize, psc.MaximumSize, rule.MEPPartId, groupType, sizesNotCovered);
if (!isCovered)
{
XElement xSegmentNotCovered = new XElement(XName.Get("SegmentRangeNotCovered"));
xSegmentNotCovered.Add(new XAttribute(XName.Get("name"), segment.Name));
StringBuilder sBuilder = new StringBuilder();

foreach (double size in sizesNotCovered)
{
double roundedSize = Convert.ConvertValueDocumentUnits(size, m_document);

sBuilder.Append(roundedSize.ToString() + " ");
}
sBuilder.Remove(sBuilder.Length - 1, 1);
xSegmentNotCovered.Add(new XAttribute(XName.Get("sizes"), sBuilder.ToString()));
xSegmentNotCovered.Add(new XAttribute(XName.Get("unit"), m_document.GetUnits().GetFormatOptions(UnitType.UT_PipeSize).DisplayUnits.ToString()));
xSegmentNotCovered.Add(new XAttribute(XName.Get("groupType"), groupType.ToString()));

return xSegmentNotCovered;
}
}
return null;
}

private bool CheckSegmentForValidCoverage(RoutingPreferenceManager routingPreferenceManager, double lowerBound, double upperBound, ElementId segmentId, RoutingPreferenceRuleGroupType groupType, List<double> sizesNotCovered)
{

bool retval = true;
if (segmentId == ElementId.InvalidElementId)
throw new Exception("Invalid segment ElementId");

PipeSegment segment = this.m_document.GetElement(segmentId) as PipeSegment;
foreach (MEPSize size in segment.GetSizes())
{
//skip sizes outside of rp bounds
if (size.NominalDiameter < lowerBound)
continue;
if (size.NominalDiameter > upperBound)
break;

RoutingConditions conditions = new RoutingConditions(RoutingPreferenceErrorLevel.None);
conditions.AppendCondition(new RoutingCondition(size.NominalDiameter));
ElementId foundFitting = routingPreferenceManager.GetMEPPartId(groupType, conditions);
if (foundFitting == ElementId.InvalidElementId)
{
sizesNotCovered.Add(size.NominalDiameter);
retval = false;
}
}
return retval;
}

private bool IsRuleSetToRangeNone(RoutingPreferenceManager routingPreferenceManager, RoutingPreferenceRuleGroupType groupType, int index)
{

if (routingPreferenceManager.GetNumberOfRules(groupType) == 0)
{
return false;
}

RoutingPreferenceRule rule = routingPreferenceManager.GetRule(groupType, index);
if (rule.NumberOfCriteria == 0)
{
return false;
}

PrimarySizeCriterion psc = rule.GetCriterion(0) as PrimarySizeCriterion;
if (psc.IsEqual(PrimarySizeCriterion.None()))
{
return true;
}
else
return false;
}

private bool IsGroupSetToRangeNone(RoutingPreferenceManager routingPreferenceManager, RoutingPreferenceRuleGroupType groupType)
{
bool retval = true;

if (routingPreferenceManager.GetNumberOfRules(groupType) == 0)
{
return false;
}
for (int index = 0; index != routingPreferenceManager.GetNumberOfRules(groupType); ++index)
{
if (!(IsRuleSetToRangeNone(routingPreferenceManager, groupType, index)))
retval = false;
}

return retval;

}
/// <summary>
/// Check to see if the routing preferences specify a preferred junction type but do not have any
/// rules with valid fittings for that type (e.g, "Tee" is the preferred junction type, but only "Tap" fittings
/// are specified in junction rules.)
/// </summary>
/// <param name="routingPreferenceManager"></param>
/// <returns></returns>
private bool IsPreferredJunctionTypeValid(RoutingPreferenceManager routingPreferenceManager)
{
PreferredJunctionType preferredJunctionType = routingPreferenceManager.PreferredJunctionType;

if (routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Junctions) == 0)
return false;

bool teeDefined = false;
bool tapDefined = false;
for (int index = 0; index != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Junctions); ++index)
{
RoutingPreferenceRule rule = routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Junctions, index);
if (rule.MEPPartId == ElementId.InvalidElementId)
continue;

FamilySymbol familySymbol = this.m_document.GetElement(rule.MEPPartId) as FamilySymbol;

Parameter paramPartType = familySymbol.Family.get_Parameter(BuiltInParameter.FAMILY_CONTENT_PART_TYPE);
if (paramPartType == null)
throw new Exception("Null partType parameter.");

PartType partType = (PartType)paramPartType.AsInteger();

if ((partType == PartType.Tee))
teeDefined = true;
else if (
(partType == PartType.TapAdjustable) ||
(partType == PartType.TapPerpendicular) ||
(partType == PartType.SpudPerpendicular) ||
(partType == PartType.SpudAdjustable)
)
tapDefined = true;
}
if ((preferredJunctionType == PreferredJunctionType.Tap) && !tapDefined)
return false;
if ((preferredJunctionType == PreferredJunctionType.Tee) && !teeDefined)
return false;

return true;
}
private string GetFittingName(ElementId id)
{
if (id == ElementId.InvalidElementId)
throw new Exception("Invalid ElementId");
FamilySymbol symbol = m_document.GetElement(id) as FamilySymbol;
return symbol.Family.Name + " " + symbol.Name;
}
private string GetSegmentName(ElementId id)
{
if (id == ElementId.InvalidElementId)
throw new Exception("Invalid ElementId");
PipeSegment segment = m_document.GetElement(id) as PipeSegment;
return segment.Name;
}

/// <summary>
///Using routing preferences, display found segment and fitting info from the size and pipe type specified in the dialog.
/// </summary>
private List<PartIdInfo> GetPreferredFittingsAndSegments()
{
List<PartIdInfo> partIdInfoList = new List<PartIdInfo>();

RoutingConditions conditions = new RoutingConditions(RoutingPreferenceErrorLevel.None);

conditions.AppendCondition(new RoutingCondition(m_mepSize));
foreach (RoutingPreferenceRuleGroupType groupType in Enum.GetValues(typeof(RoutingPreferenceRuleGroupType)))
{
if (groupType == RoutingPreferenceRuleGroupType.Undefined)
continue;

IList<ElementId> preferredTypes = new List<ElementId>();
ElementId preferredType = m_routingPreferenceManager.GetMEPPartId(groupType, conditions);
//GetMEPPartId is the main "query" method of the
//routing preferences API that evaluates conditions and criteria and returns segment and fitting elementIds that meet
//those criteria.

if (groupType != RoutingPreferenceRuleGroupType.Segments)
{
preferredTypes.Add(preferredType);
}
else //Get all segments that support a given size, not just the first segment.
{
preferredTypes = m_routingPreferenceManager.GetSharedSizes(m_mepSize, ConnectorProfileType.Round);
}
partIdInfoList.Add(new PartIdInfo(groupType, preferredTypes)); //Collect a PartIdInfo object for each group type.
}

return partIdInfoList;
}
#endregion
}
}

PartIdInfo.cs

//
// (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.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using System.Xml.Linq;
namespace Revit.SDK.Samples.RoutingPreferenceTools.CS
{
    /// <summary>
    /// This class contains a routing preference rule group and list of elementIds that correspond
    /// to found segments and fittings that meet criteria specified through a routing preference manager.
    /// </summary>
    public class PartIdInfo
    {
        // List of part Ids
        private List<ElementId> m_ids;
        // group type
        private RoutingPreferenceRuleGroupType m_groupType;
        /// <summary>
        /// Id
        /// </summary>
        public List<ElementId> Id
        {
            get { return m_ids; }
        }
        /// <summary>
        ///  Group type
        /// </summary>
        public RoutingPreferenceRuleGroupType GroupType
        {
            get { return m_groupType; }
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="groupType"></param>
        /// <param name="ids"></param>
        public PartIdInfo(RoutingPreferenceRuleGroupType groupType, IList<ElementId> ids)
        {
            m_ids = new List<ElementId>();
            m_groupType = groupType;
            m_ids.AddRange(ids);
        }
        /// <summary>
        /// Build XML information of document
        /// </summary>
        /// <param name="document"></param>
        /// <returns></returns>
        public XElement GetXml(Autodesk.Revit.DB.Document document)
        {
            XElement xPartInfo = new XElement(XName.Get("PartInfo"));
            xPartInfo.Add(new XAttribute(XName.Get("groupType"), m_groupType.ToString()));
            xPartInfo.Add(new XAttribute(XName.Get("partNames"), GetFittingNames(document)));
            return xPartInfo;
        }
        private string GetFittingName(Document document, ElementId id)
        {
            string fittingName = " None ";
            if (id != ElementId.InvalidElementId)
            {
                Element element = document.GetElement(id);
                if (element is Segment)
                {
                    fittingName = element.Name;
                }
                else
                {
                    FamilySymbol familySymbol = element as FamilySymbol;
                    fittingName = familySymbol.Family.Name + " " + familySymbol.Name;
                }
            }
            return fittingName;
        }
        /// <summary>
        /// Fitting name
        /// </summary>
        /// <param name="document"></param>
        /// <returns></returns>
        private string GetFittingNames(Document document)
        {
            string fittingNames = "";
            if (m_ids.Count == 0)
            {
                fittingNames += "None -1";
            }
            foreach (ElementId id in m_ids)
            {
                fittingNames += GetFittingName(document, id) + " " + id.ToString() + ", ";
            }
            return fittingNames.Remove(fittingNames.Length - 2, 2);
        }
    }
}

管道/电缆走向偏好构建器CommandReadPreferencesCommandWritePreferences命令)

主题:

一种工具,用于从XML文件中设置管道类型、管道配件和走向偏好,并将这些偏好导出到XML以供归档、文档和协作使用

概要:

这些命令允许用户在适合于在各种BIM管理环境中重复使用的可共享的XML格式中处理管道/电缆走向偏好数据。

相关类:

Autodesk.Revit.DB.RoutingPreferenceManager - 管理不同MEP曲线类型的管道/电缆走向偏好规则和条件。

Autodesk.Revit.DB.RoutingPreferenceRule

Autodesk.Revit.DB.Plumbing.PipeType

Autodesk.Revit.DB.Plumbing.PipeSegment

Autodesk.Revit.DB.Plumbing.PipeScheduleType

Autodesk.Revit.DB.PrimarySizeCriterion

Autodesk.Revit.DB.FamilySymbol

Autodesk.Revit.DB.Material

项目文件:

CommandReadPreferences.cs

“从XML读取偏好”命令的主命令文件。

CommandWritePreferences.cs

“将偏好写入XML”命令的主命令文件。

RoutingPreferenceBuilder.cs

用于读取和写入XML和管道/电缆走向偏好数据的类。

RoutingPreferenceBuilderUtility.cs

RoutingPreferenceBuilder使用的辅助类。

RoutingPreferenceSample.xml

包含族、时间表、管道类型、管道段、尺寸、规则和标准的样本数据文件。

RoutingPreferenceBuilderData.xsd

用于验证RoutingPreferenceBuilder XML文档的XML模式。

说明:

1. Revit MEP中打开包含至少一个管道类型的项目。

2. 检查RoutingPreferenceSample.xml中的管道、时间表、段和走向偏好规则数据。

3. 根据需要更新任何族(.rfa)文件的路径。

4. 运行“CommandReadPreferences”命令,选择样本RoutingPreferenceSample.xml文件,并注意添加到项目中的所有管道类型、时间表、段和走向偏好规则。运行“CommandWritePreferences”命令,选择要创建的新文件,并在新的XML文件中注意导出的管道/电缆走向偏好数据。

注意:

将可选的familypaths.xml”文件放置在RoutingPreferenceTools.dll文件夹中,并提供除Revit指定的默认内容路径之外的其他族文件路径。这将使Revit在读取和写入管道/电缆走向偏好时,在其他位置搜索族文件。

源代码:

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

CommandReadPreferences.cs

//
// (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.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using System.Xml.Linq;
using System.IO;
using System.Xml;
using System.Diagnostics;
namespace Revit.SDK.Samples.RoutingPreferenceTools.CS
{
    /// <summary>
    /// A command to read a routing preference builder xml file and add pipe types, schedules, segments, and sizes, and routing preferences rules to the
    /// document from the xml data.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    public class CommandReadPreferences : Autodesk.Revit.UI.IExternalCommand
    {
        /// <summary>
        /// Implement this method as an external command for Revit.
        /// </summary>
        /// <param name="commandData">An object that is passed to the external application 
        /// which contains data related to the command, 
        /// such as the application object and active view.</param>
        /// <param name="message">A message that can be set by the external application 
        /// which will be displayed if a failure or cancellation is returned by 
        /// the external command.</param>
        /// <param name="elements">A set of elements to which the external application 
        /// can add elements that are to be highlighted in case of failure or cancellation.</param>
        /// <returns>Return the status of the external command. 
        /// A result of Succeeded means that the API external method functioned as expected. 
        /// Cancelled can be used to signify that the user cancelled the external operation 
        /// at some point. Failure should be returned if the application is unable to proceed with 
        /// the operation.</returns>
        public Autodesk.Revit.UI.Result Execute(Autodesk.Revit.UI.ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
        {
            if (!Validation.ValidateMep(commandData.Application.Application))
            {
                Validation.MepWarning();
                return Autodesk.Revit.UI.Result.Succeeded;
            }
            if (!Validation.ValidatePipesDefined(commandData.Application.ActiveUIDocument.Document))
            {
                Validation.PipesDefinedWarning();
                return Autodesk.Revit.UI.Result.Succeeded;
            }
            Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.DefaultExt = ".xml";
            ofd.Filter = "RoutingPreference Builder Xml files (*.xml)|*.xml";
            if (ofd.ShowDialog() == true)
            {
                StreamReader reader = new StreamReader(ofd.FileName);
                XDocument routingPreferenceBuilderDoc = XDocument.Load(new XmlTextReader(reader));
                reader.Close();
                //Distribute the .xsd file to routing preference builder xml authors as necessary.
                string xmlValidationMessage;
                if (!SchemaValidationHelper.ValidateRoutingPreferenceBuilderXml(routingPreferenceBuilderDoc, out xmlValidationMessage))
                {
                    Autodesk.Revit.UI.TaskDialog.Show("RoutingPreferenceBuilder", "Xml file is not a valid RoutingPreferenceBuilder xml document.  Please check RoutingPreferenceBuilderData.xsd.  " + xmlValidationMessage);
                    return Autodesk.Revit.UI.Result.Succeeded;
                }
                try
                {
                    RoutingPreferenceBuilder builder = new RoutingPreferenceBuilder(commandData.Application.ActiveUIDocument.Document);
                    builder.ParseAllPipingPoliciesFromXml(routingPreferenceBuilderDoc);
                    Autodesk.Revit.UI.TaskDialog.Show("RoutingPreferenceBuilder", "Routing Preferences imported successfully.");
                }
                catch (RoutingPreferenceDataException ex)
                {
                    Autodesk.Revit.UI.TaskDialog.Show("RoutingPreferenceBuilder error: ", ex.ToString());
                }
            }
            return Autodesk.Revit.UI.Result.Succeeded;
        }
    }
}

CommandWritePreferences.cs

//
// (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.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using System.Xml.Linq;
using System.IO;
using System.Xml;
using System.Diagnostics;
namespace Revit.SDK.Samples.RoutingPreferenceTools.CS
{
    /// <summary>
    /// A command to read routing preference data from a document and write an XML file summarizing it that can later be read by the
    /// CommandReadPreferences command.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.ReadOnly)]
    public class CommandWritePreferences : Autodesk.Revit.UI.IExternalCommand
    {
        /// <summary>
        /// Implement this method as an external command for Revit.
        /// </summary>
        /// <param name="commandData">An object that is passed to the external application 
        /// which contains data related to the command, 
        /// such as the application object and active view.</param>
        /// <param name="message">A message that can be set by the external application 
        /// which will be displayed if a failure or cancellation is returned by 
        /// the external command.</param>
        /// <param name="elements">A set of elements to which the external application 
        /// can add elements that are to be highlighted in case of failure or cancellation.</param>
        /// <returns>Return the status of the external command. 
        /// A result of Succeeded means that the API external method functioned as expected. 
        /// Cancelled can be used to signify that the user cancelled the external operation 
        /// at some point. Failure should be returned if the application is unable to proceed with 
        /// the operation.</returns>
        public Autodesk.Revit.UI.Result Execute(Autodesk.Revit.UI.ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
        {
            if (!Validation.ValidateMep(commandData.Application.Application))
            {
                Validation.MepWarning();
                return Autodesk.Revit.UI.Result.Succeeded;
            }
            if (!Validation.ValidatePipesDefined(commandData.Application.ActiveUIDocument.Document))
            {
                Validation.PipesDefinedWarning();
                return Autodesk.Revit.UI.Result.Succeeded;
            }
            Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
            sfd.DefaultExt = ".xml";
            sfd.Filter = "RoutingPreference Builder Xml files (*.xml)|*.xml";
            sfd.FileName = (System.IO.Path.GetFileNameWithoutExtension(commandData.Application.ActiveUIDocument.Document.PathName)) + ".routingPreferences.xml";
            if (sfd.ShowDialog() == true)
            {
                RoutingPreferenceBuilder builder = new RoutingPreferenceBuilder(commandData.Application.ActiveUIDocument.Document);
        bool pathsNotFound = false;
            XDocument routingPreferenceBuilderDoc = builder.CreateXmlFromAllPipingPolicies(ref pathsNotFound);
            XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
            xmlWriterSettings.Indent = true;
            xmlWriterSettings.NewLineOnAttributes = false;
            XmlWriter writer = XmlWriter.Create(sfd.FileName, xmlWriterSettings);
            routingPreferenceBuilderDoc.WriteTo(writer);
            writer.Flush();
            writer.Close();
            string pathmessage = "";
            if (pathsNotFound)
               pathmessage = "One or more paths to .rfa files were not found.  You may need to add these paths in manually to the generated xml file.";
            Autodesk.Revit.UI.TaskDialog.Show("RoutingPreferenceBuilder", "Routing Preferences exported successfully.   " + pathmessage);
            }
            return Autodesk.Revit.UI.Result.Succeeded;
        }
    }
}

RoutingPreferenceBuilder.cs

//
// (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.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using System.Xml.Linq;
using System.IO;
using System.Xml;
using System.Diagnostics;

namespace Revit.SDK.Samples.RoutingPreferenceTools.CS
{
/// <summary>
/// Class to read and write XML and routing preference data
/// </summary>
public class RoutingPreferenceBuilder
{
#region Data
private IEnumerable<Segment> m_segments;
private IEnumerable<FamilySymbol> m_fittings;
private IEnumerable<Material> m_materials;
private IEnumerable<PipeScheduleType> m_pipeSchedules;
private IEnumerable<PipeType> m_pipeTypes;
private Autodesk.Revit.DB.Document m_document;
#endregion

#region Public interface
/// <summary>
/// Create an instance of the class and initialize lists of all segments, fittings, materials, schedules, and pipe types in the document.
/// </summary>
public RoutingPreferenceBuilder(Document document)
{
m_document = document;
m_segments = GetAllPipeSegments(m_document);
m_fittings = GetAllFittings(m_document);
m_materials = GetAllMaterials(m_document);
m_pipeSchedules = GetAllPipeScheduleTypes(m_document);
m_pipeTypes = GetAllPipeTypes(m_document);

}
/// <summary>
/// Reads data from an Xml source and loads pipe fitting families, creates segments, sizes, schedules, and routing preference rules from the xml data.
/// </summary>
/// <param name="xDoc">The Xml data source to read from</param>
public void ParseAllPipingPoliciesFromXml(XDocument xDoc)
{
if (m_pipeTypes.Count() == 0)
throw new RoutingPreferenceDataException("No pipe pipes defined in this project. At least one must be defined.");

FormatOptions formatOptionPipeSize = m_document.GetUnits().GetFormatOptions(UnitType.UT_PipeSize);

string docPipeSizeUnit = formatOptionPipeSize.DisplayUnits.ToString();
string xmlPipeSizeUnit = xDoc.Root.Attribute("pipeSizeUnits").Value;
if (docPipeSizeUnit != xmlPipeSizeUnit)
throw new RoutingPreferenceDataException("Units from XML do not match current pipe size units.");

FormatOptions formatOptionRoughness = m_document.GetUnits().GetFormatOptions(UnitType.UT_Piping_Roughness);

string docRoughnessUnit = formatOptionRoughness.DisplayUnits.ToString();
string xmlRoughnessUnit = xDoc.Root.Attribute("pipeRoughnessUnits").Value;
if (docRoughnessUnit != xmlRoughnessUnit)
throw new RoutingPreferenceDataException("Units from XML do not match current pipe roughness units.");

Transaction loadFamilies = new Transaction(m_document, "Load Families");
loadFamilies.Start();
IEnumerable<XElement> families = xDoc.Root.Elements("Family");
FindFolderUtility findFolderUtility = new FindFolderUtility(m_document.Application);

foreach (XElement xfamily in families)
try
{
ParseFamilyFromXml(xfamily, findFolderUtility); //Load families.
}
catch (Exception ex)
{
loadFamilies.RollBack();
throw ex;
}
loadFamilies.Commit();

Transaction addPipeTypes = new Transaction(m_document, "Add PipeTypes");
addPipeTypes.Start();
IEnumerable<XElement> pipeTypes = xDoc.Root.Elements("PipeType");
foreach (XElement xpipeType in pipeTypes)
try
{
ParsePipeTypeFromXml(xpipeType); //Define new pipe types.
}
catch (Exception ex)
{
addPipeTypes.RollBack();
throw ex;
}
addPipeTypes.Commit();

Transaction addPipeSchedules = new Transaction(m_document, "Add Pipe Schedule Types");
addPipeSchedules.Start();
IEnumerable<XElement> pipeScheduleTypes = xDoc.Root.Elements("PipeScheduleType");
foreach (XElement xpipeScheduleType in pipeScheduleTypes)
try
{
ParsePipeScheduleTypeFromXml(xpipeScheduleType); //Define new pipe schedule types.
}
catch (Exception ex)
{
addPipeSchedules.RollBack();
throw ex;
}
addPipeSchedules.Commit();

//The code above have added some new pipe types, schedules, or fittings, so update the lists of all of these.
UpdatePipeTypesList();
UpdatePipeTypeSchedulesList();
UpdateFittingsList();

Transaction addPipeSegments = new Transaction(m_document, "Add Pipe Segments");
addPipeSchedules.Start();
IEnumerable<XElement> pipeSegments = xDoc.Root.Elements("PipeSegment"); //Define new segments.
foreach (XElement xpipeSegment in pipeSegments)
try
{
ParsePipeSegmentFromXML(xpipeSegment);
}
catch (Exception ex)
{
addPipeSchedules.RollBack();
throw ex;
}
addPipeSchedules.Commit();

UpdateSegmentsList(); //More segments may have been created, so update the segment list.

//Now that all of the various types that routing preferences use have been created or loaded, add all the routing preferences.
Transaction addRoutingPreferences = new Transaction(m_document, "Add Routing Preferences");
addRoutingPreferences.Start();
IEnumerable<XElement> routingPreferenceManagers = xDoc.Root.Elements("RoutingPreferenceManager");
foreach (XElement xroutingPreferenceManager in routingPreferenceManagers)
try
{
ParseRoutingPreferenceManagerFromXML(xroutingPreferenceManager);
}
catch (Exception ex)
{
addRoutingPreferences.RollBack();
throw ex;
}
addRoutingPreferences.Commit();

}

/// <summary>
/// Reads pipe fitting family, segment, size, schedule, and routing preference data from a document and summarizes it in Xml.
/// </summary>
/// <returns>An XDocument containing an Xml summary of routing preference information</returns>
public XDocument CreateXmlFromAllPipingPolicies(ref bool pathsNotFound)
{
//To export the full path name of all .rfa family files, use the FindFolderUtility class.
FindFolderUtility findFolderUtility = new FindFolderUtility(m_document.Application);

XDocument routingPreferenceBuilderDoc = new XDocument();
XElement xroot = new XElement(XName.Get("RoutingPreferenceBuilder"));

FormatOptions formatOptionPipeSize = m_document.GetUnits().GetFormatOptions(UnitType.UT_PipeSize);
string unitStringPipeSize = formatOptionPipeSize.DisplayUnits.ToString();
xroot.Add(new XAttribute(XName.Get("pipeSizeUnits"), unitStringPipeSize));

FormatOptions formatOptionRoughness = m_document.GetUnits().GetFormatOptions(UnitType.UT_Piping_Roughness);
string unitStringRoughness = formatOptionRoughness.DisplayUnits.ToString();
xroot.Add(new XAttribute(XName.Get("pipeRoughnessUnits"), unitStringRoughness));

foreach (FamilySymbol familySymbol in this.m_fittings)
{
xroot.Add(CreateXmlFromFamily(familySymbol, findFolderUtility, ref pathsNotFound));
}

foreach (PipeType pipeType in m_pipeTypes)
{
xroot.Add(CreateXmlFromPipeType(pipeType));
}

foreach (PipeScheduleType pipeScheduleType in m_pipeSchedules)
{
xroot.Add(CreateXmlFromPipeScheduleType(pipeScheduleType));
}

foreach (PipeSegment pipeSegment in m_segments)
{
xroot.Add(CreateXmlFromPipeSegment(pipeSegment));
}

foreach (PipeType pipeType in m_pipeTypes)
{
xroot.Add(CreateXmlFromRoutingPreferenceManager(pipeType.RoutingPreferenceManager));
}

routingPreferenceBuilderDoc.Add(xroot);
return routingPreferenceBuilderDoc;
}
#endregion

#region XML parsing and generation
/// <summary>
/// Load a family from xml
/// </summary>
/// <param name="familyXElement"></param>
/// <param name="findFolderUtility"></param>
private void ParseFamilyFromXml(XElement familyXElement, FindFolderUtility findFolderUtility)
{

XAttribute xafilename = familyXElement.Attribute(XName.Get("filename"));
string familyPath = xafilename.Value;
if (!System.IO.File.Exists(familyPath))
{
string filename = System.IO.Path.GetFileName(familyPath);
familyPath = findFolderUtility.FindFileFolder(filename);
if (!System.IO.File.Exists(familyPath))
throw new RoutingPreferenceDataException("Cannot find family file: " + xafilename.Value);
}

if (string.Compare(System.IO.Path.GetExtension(familyPath), ".rfa", true) != 0)
throw new RoutingPreferenceDataException(familyPath + " is not a family file.");

try
{
if (!m_document.LoadFamily(familyPath))
return; //returns false if already loaded.
}
catch (System.Exception ex)
{
throw new RoutingPreferenceDataException("Cannot load family: " + xafilename.Value + ": " + ex.ToString());
}

}
/// <summary>
/// Create xml from a family
/// </summary>
/// <param name="pipeFitting"></param>
/// <param name="findFolderUtility"></param>
/// <param name="pathNotFound"></param>
/// <returns></returns>
private static XElement CreateXmlFromFamily(FamilySymbol pipeFitting, FindFolderUtility findFolderUtility, ref bool pathNotFound)
{
//Try to find the path of the .rfa file.
string path = findFolderUtility.FindFileFolder(pipeFitting.Family.Name + ".rfa");
string pathToWrite;
if (path == "")
{
pathNotFound = true;
pathToWrite = pipeFitting.Family.Name + ".rfa";
}
else
pathToWrite = path;

XElement xFamilySymbol = new XElement(XName.Get("Family"));
xFamilySymbol.Add(new XAttribute(XName.Get("filename"), pathToWrite));
return xFamilySymbol;
}

/// <summary>
/// Greate a PipeType from xml
/// </summary>
/// <param name="pipetypeXElement"></param>
private void ParsePipeTypeFromXml(XElement pipetypeXElement)
{
XAttribute xaName = pipetypeXElement.Attribute(XName.Get("name"));

ElementId pipeTypeId = GetPipeTypeByName(xaName.Value);

if (pipeTypeId == ElementId.InvalidElementId) //If the pipe type does not exist, create it.
{
PipeType newPipeType = m_pipeTypes.First().Duplicate(xaName.Value) as PipeType;
ClearRoutingPreferenceRules(newPipeType);
}

}

/// <summary>
/// Clear all routing preferences in a PipeType
/// </summary>
/// <param name="pipeType"></param>
private static void ClearRoutingPreferenceRules(PipeType pipeType)
{
foreach ( RoutingPreferenceRuleGroupType group in System.Enum.GetValues(typeof(RoutingPreferenceRuleGroupType)))
{
int ruleCount = pipeType.RoutingPreferenceManager.GetNumberOfRules(group);
for (int index = 0; index != ruleCount; ++index)
{
pipeType.RoutingPreferenceManager.RemoveRule(group, 0);
}
}
}

/// <summary>
/// Create Xml from a PipeType
/// </summary>
/// <param name="pipeType"></param>
/// <returns></returns>
private static XElement CreateXmlFromPipeType(PipeType pipeType)
{
XElement xPipeType = new XElement(XName.Get("PipeType"));
xPipeType.Add(new XAttribute(XName.Get("name"), pipeType.Name));
return xPipeType;
}

private void ParsePipeScheduleTypeFromXml(XElement pipeScheduleTypeXElement)
{
XAttribute xaName = pipeScheduleTypeXElement.Attribute(XName.Get("name"));
ElementId pipeScheduleTypeId = GetPipeScheduleTypeByName(xaName.Value);
if (pipeScheduleTypeId == ElementId.InvalidElementId) //If the pipe schedule type does not exist, create it.
m_pipeSchedules.First().Duplicate(xaName.Value);
}

/// <summary>
/// Create Xml from a PipeScheduleType
/// </summary>
/// <param name="pipeScheduleType"></param>
/// <returns></returns>
private static XElement CreateXmlFromPipeScheduleType(PipeScheduleType pipeScheduleType)
{
XElement xPipeSchedule = new XElement(XName.Get("PipeScheduleType"));
xPipeSchedule.Add(new XAttribute(XName.Get("name"), pipeScheduleType.Name));
return xPipeSchedule;
}

/// <summary>
/// Create a PipeSegment from XML
/// </summary>
/// <param name="segmentXElement"></param>
private void ParsePipeSegmentFromXML(XElement segmentXElement)
{
XAttribute xaMaterial = segmentXElement.Attribute(XName.Get("materialName"));
XAttribute xaSchedule = segmentXElement.Attribute(XName.Get("pipeScheduleTypeName"));
XAttribute xaRoughness = segmentXElement.Attribute(XName.Get("roughness"));

ElementId materialId = GetMaterialByName(xaMaterial.Value); //There is nothing in the xml schema for creating new materials -- any material specified must already exist in the document.
if (materialId == ElementId.InvalidElementId)
{
throw new RoutingPreferenceDataException("Cannot find Material: " + xaMaterial.Value + " in: " + segmentXElement.ToString());
}
ElementId scheduleId = GetPipeScheduleTypeByName(xaSchedule.Value);

double roughness;
bool r1 = double.TryParse(xaRoughness.Value, out roughness);

if (!r1)
throw new RoutingPreferenceDataException("Invalid roughness value: " + xaRoughness.Value + " in: " + segmentXElement.ToString());

if (roughness <= 0)
throw new RoutingPreferenceDataException("Invalid roughness value: " + xaRoughness.Value + " in: " + segmentXElement.ToString());

if (scheduleId == ElementId.InvalidElementId)
{
throw new RoutingPreferenceDataException("Cannot find Schedule: " + xaSchedule.Value + " in: " + segmentXElement.ToString()); //we will not create new schedules.
}

ElementId existingPipeSegmentId = GetSegmentByIds(materialId, scheduleId);
if (existingPipeSegmentId != ElementId.InvalidElementId)
return; //Segment found, no need to create.

ICollection<MEPSize> sizes = new List<MEPSize>();
foreach (XNode sizeNode in segmentXElement.Nodes())
{
if (sizeNode is XElement)
{
MEPSize newSize = ParseMEPSizeFromXml(sizeNode as XElement, m_document);
sizes.Add(newSize);
}
}
PipeSegment pipeSegment = PipeSegment.Create(m_document, materialId, scheduleId, sizes);
pipeSegment.Roughness = Convert.ConvertValueToFeet(roughness, m_document);

return;
}

/// <summary>
/// Create Xml from a PipeSegment
/// </summary>
/// <param name="pipeSegment"></param>
/// <returns></returns>
private XElement CreateXmlFromPipeSegment(PipeSegment pipeSegment)
{
XElement xPipeSegment = new XElement(XName.Get("PipeSegment"));
string segmentName = pipeSegment.Name;

xPipeSegment.Add(new XAttribute(XName.Get("pipeScheduleTypeName"), GetPipeScheduleTypeNamebyId(pipeSegment.ScheduleTypeId)));
xPipeSegment.Add(new XAttribute(XName.Get("materialName"), GetMaterialNameById(pipeSegment.MaterialId)));

double roughnessInDocumentUnits = Convert.ConvertValueDocumentUnits(pipeSegment.Roughness, m_document);
xPipeSegment.Add(new XAttribute(XName.Get("roughness"), roughnessInDocumentUnits.ToString("r") ));

foreach (MEPSize size in pipeSegment.GetSizes())
xPipeSegment.Add(CreateXmlFromMEPSize(size, m_document));

return xPipeSegment;

}

/// <summary>
/// Create an MEPSize from Xml
/// </summary>
/// <param name="sizeXElement"></param>
/// <param name="document"></param>
/// <returns></returns>
private static MEPSize ParseMEPSizeFromXml(XElement sizeXElement, Autodesk.Revit.DB.Document document)
{
XAttribute xaNominal = sizeXElement.Attribute(XName.Get("nominalDiameter"));
XAttribute xaInner = sizeXElement.Attribute(XName.Get("innerDiameter"));
XAttribute xaOuter = sizeXElement.Attribute(XName.Get("outerDiameter"));
XAttribute xaUsedInSizeLists = sizeXElement.Attribute(XName.Get("usedInSizeLists"));
XAttribute xaUsedInSizing = sizeXElement.Attribute(XName.Get("usedInSizing"));

double nominal, inner, outer;
bool usedInSizeLists, usedInSizing;
bool r1 = double.TryParse(xaNominal.Value, out nominal);
bool r2 = double.TryParse(xaInner.Value, out inner);
bool r3 = double.TryParse(xaOuter.Value, out outer);
bool r4 = bool.TryParse(xaUsedInSizeLists.Value, out usedInSizeLists);
bool r5 = bool.TryParse(xaUsedInSizing.Value, out usedInSizing);

if (!r1 || !r2 || !r3 || !r4 || !r5)
throw new RoutingPreferenceDataException("Cannot parse MEPSize attributes:" + xaNominal.Value + ", " + xaInner.Value + ", " + xaOuter.Value + ", " + xaUsedInSizeLists.Value + ", " + xaUsedInSizing.Value);

MEPSize newSize = null;

try
{

newSize = new MEPSize(Convert.ConvertValueToFeet(nominal, document), Convert.ConvertValueToFeet(inner, document), Convert.ConvertValueToFeet(outer, document), usedInSizeLists, usedInSizing);
}

catch (Exception)
{
throw new RoutingPreferenceDataException("Invalid MEPSize values: " + nominal.ToString() + ", " + inner.ToString() + ", " + outer.ToString());
}
return newSize;

}

/// <summary>
/// Create Xml from an MEPSize
/// </summary>
/// <param name="size"></param>
/// <param name="document"></param>
/// <returns></returns>
private static XElement CreateXmlFromMEPSize(MEPSize size, Autodesk.Revit.DB.Document document)
{
XElement xMEPSize = new XElement(XName.Get("MEPSize"));

xMEPSize.Add(new XAttribute(XName.Get("innerDiameter"), (Convert.ConvertValueDocumentUnits(size.InnerDiameter, document) ).ToString()));
xMEPSize.Add(new XAttribute(XName.Get("nominalDiameter"), (Convert.ConvertValueDocumentUnits(size.NominalDiameter, document) ).ToString()));
xMEPSize.Add(new XAttribute(XName.Get("outerDiameter"), (Convert.ConvertValueDocumentUnits(size.OuterDiameter, document) ).ToString()));
xMEPSize.Add(new XAttribute(XName.Get("usedInSizeLists"), size.UsedInSizeLists));
xMEPSize.Add(new XAttribute(XName.Get("usedInSizing"), size.UsedInSizing));
return xMEPSize;
}
/// <summary>
/// Populate a routing preference manager from Xml
/// </summary>
/// <param name="routingPreferenceManagerXElement"></param>
private void ParseRoutingPreferenceManagerFromXML(XElement routingPreferenceManagerXElement)
{

XAttribute xaPipeTypeName = routingPreferenceManagerXElement.Attribute(XName.Get("pipeTypeName"));
XAttribute xaPreferredJunctionType = routingPreferenceManagerXElement.Attribute(XName.Get("preferredJunctionType"));

PreferredJunctionType preferredJunctionType;
bool r1 = Enum.TryParse<PreferredJunctionType>(xaPreferredJunctionType.Value, out preferredJunctionType);

if (!r1)
throw new RoutingPreferenceDataException("Invalid Preferred Junction Type in: " + routingPreferenceManagerXElement.ToString());

ElementId pipeTypeId = GetPipeTypeByName(xaPipeTypeName.Value);
if (pipeTypeId == ElementId.InvalidElementId)
throw new RoutingPreferenceDataException("Could not find pipe type element in: " + routingPreferenceManagerXElement.ToString());

PipeType pipeType = m_document.GetElement(pipeTypeId) as PipeType;

RoutingPreferenceManager routingPreferenceManager = pipeType.RoutingPreferenceManager;
routingPreferenceManager.PreferredJunctionType = preferredJunctionType;

foreach (XNode xRule in routingPreferenceManagerXElement.Nodes())
{
if (xRule is XElement)
{
RoutingPreferenceRuleGroupType groupType;
RoutingPreferenceRule rule = ParseRoutingPreferenceRuleFromXML(xRule as XElement, out groupType);
routingPreferenceManager.AddRule(groupType, rule);
}
}

}
/// <summary>
/// Create Xml from a RoutingPreferenceManager
/// </summary>
/// <param name="routingPreferenceManager"></param>
/// <returns></returns>
private XElement CreateXmlFromRoutingPreferenceManager(RoutingPreferenceManager routingPreferenceManager)
{
XElement xRoutingPreferenceManager = new XElement(XName.Get("RoutingPreferenceManager"));

xRoutingPreferenceManager.Add(new XAttribute(XName.Get("pipeTypeName"), GetPipeTypeNameById(routingPreferenceManager.OwnerId)));

xRoutingPreferenceManager.Add(new XAttribute(XName.Get("preferredJunctionType"), routingPreferenceManager.PreferredJunctionType.ToString()));

for (int indexCrosses = 0; indexCrosses != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Crosses); indexCrosses++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Crosses, indexCrosses), RoutingPreferenceRuleGroupType.Crosses));
}

for (int indexElbows = 0; indexElbows != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Elbows); indexElbows++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Elbows, indexElbows), RoutingPreferenceRuleGroupType.Elbows));
}

for (int indexSegments = 0; indexSegments != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Segments); indexSegments++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Segments, indexSegments), RoutingPreferenceRuleGroupType.Segments));
}

for (int indexJunctions = 0; indexJunctions != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Junctions); indexJunctions++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Junctions, indexJunctions), RoutingPreferenceRuleGroupType.Junctions));
}

for (int indexTransitions = 0; indexTransitions != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Transitions); indexTransitions++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Transitions, indexTransitions), RoutingPreferenceRuleGroupType.Transitions));
}

for (int indexUnions = 0; indexUnions != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.Unions); indexUnions++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.Unions, indexUnions), RoutingPreferenceRuleGroupType.Unions));
}

for (int indexMechanicalJoints = 0; indexMechanicalJoints != routingPreferenceManager.GetNumberOfRules(RoutingPreferenceRuleGroupType.MechanicalJoints); indexMechanicalJoints++)
{
xRoutingPreferenceManager.Add(createXmlFromRoutingPreferenceRule(routingPreferenceManager.GetRule(RoutingPreferenceRuleGroupType.MechanicalJoints, indexMechanicalJoints), RoutingPreferenceRuleGroupType.MechanicalJoints));
}

return xRoutingPreferenceManager;
}

/// <summary>
/// Create a RoutingPreferenceRule from Xml
/// </summary>
/// <param name="ruleXElement"></param>
/// <param name="groupType"></param>
/// <returns></returns>
private RoutingPreferenceRule ParseRoutingPreferenceRuleFromXML(XElement ruleXElement, out RoutingPreferenceRuleGroupType groupType)
{

XAttribute xaDescription = null;
XAttribute xaPartName = null;
XAttribute xaMinSize = null;
XAttribute xaMaxSize = null;
XAttribute xaGroup = null;

xaDescription = ruleXElement.Attribute(XName.Get("description"));
xaPartName = ruleXElement.Attribute(XName.Get("partName"));
xaGroup = ruleXElement.Attribute(XName.Get("ruleGroup"));
xaMinSize = ruleXElement.Attribute(XName.Get("minimumSize"));

ElementId partId;

bool r3 = Enum.TryParse<RoutingPreferenceRuleGroupType>(xaGroup.Value, out groupType);
if (!r3)
throw new RoutingPreferenceDataException("Could not parse rule group type: " + xaGroup.Value);

string description = xaDescription.Value;

if (groupType == RoutingPreferenceRuleGroupType.Segments)
partId = GetSegmentByName(xaPartName.Value);
else
partId = GetFittingByName(xaPartName.Value);

if (partId == ElementId.InvalidElementId)
throw new RoutingPreferenceDataException("Could not find MEP Part: " + xaPartName.Value + ". Is this the correct family name, and is the correct family loaded?");

RoutingPreferenceRule rule = new RoutingPreferenceRule(partId, description);

PrimarySizeCriterion sizeCriterion;
if (string.Compare(xaMinSize.Value, "All", true) == 0) //If "All" or "None" are specified, set min and max values to documented "Max" values.
{
sizeCriterion = PrimarySizeCriterion.All();
}
else if (string.Compare(xaMinSize.Value, "None", true) == 0)
{
sizeCriterion = PrimarySizeCriterion.None();
}
else // "maximumSize" attribute is only needed if not specifying "All" or "None."
{
try
{
xaMaxSize = ruleXElement.Attribute(XName.Get("maximumSize"));
}
catch (System.Exception)
{
throw new RoutingPreferenceDataException("Cannot get maximumSize attribute in: " + ruleXElement.ToString());
}
double min, max;
bool r1 = double.TryParse(xaMinSize.Value, out min);
bool r2 = double.TryParse(xaMaxSize.Value, out max);
if (!r1 || !r2)
throw new RoutingPreferenceDataException("Could not parse size values: " + xaMinSize.Value + ", " + xaMaxSize.Value);
if (min > max)
throw new RoutingPreferenceDataException("Invalid size range.");

min = Convert.ConvertValueToFeet(min, m_document);
max = Convert.ConvertValueToFeet(max, m_document);
sizeCriterion = new PrimarySizeCriterion(min, max);
}

rule.AddCriterion(sizeCriterion);

return rule;

}
/// <summary>
/// Create Xml from a RoutingPreferenceRule
/// </summary>
/// <param name="rule"></param>
/// <param name="groupType"></param>
/// <returns></returns>
private XElement createXmlFromRoutingPreferenceRule(RoutingPreferenceRule rule, RoutingPreferenceRuleGroupType groupType)
{
XElement xRoutingPreferenceRule = new XElement(XName.Get("RoutingPreferenceRule"));
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("description"), rule.Description));
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("ruleGroup"), groupType.ToString()));
if (rule.NumberOfCriteria >= 1)
{
PrimarySizeCriterion psc = rule.GetCriterion(0) as PrimarySizeCriterion;

if (psc.IsEqual(PrimarySizeCriterion.All()))
{
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("minimumSize"), "All"));
}
else
if (psc.IsEqual(PrimarySizeCriterion.None()))
{
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("minimumSize"), "None"));
}
else //Only specify "maximumSize" if not specifying "All" or "None" for minimum size, just like in the UI.
{

xRoutingPreferenceRule.Add(new XAttribute(XName.Get("minimumSize"), (Convert.ConvertValueDocumentUnits(psc.MinimumSize, m_document)).ToString()));
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("maximumSize"), (Convert.ConvertValueDocumentUnits(psc.MaximumSize, m_document)).ToString()));
}
}
else
{
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("minimumSize"), "All"));
}

if (groupType == RoutingPreferenceRuleGroupType.Segments)
{
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("partName"), GetSegmentNameById(rule.MEPPartId)));
}
else
xRoutingPreferenceRule.Add(new XAttribute(XName.Get("partName"), GetFittingNameById(rule.MEPPartId)));

return xRoutingPreferenceRule;
}
#endregion

#region Accessors and finders
/// <summary>
/// Get PipeScheduleTypeName by Id
/// </summary>
/// <param name="pipescheduleTypeId"></param>
/// <returns></returns>
private string GetPipeScheduleTypeNamebyId(ElementId pipescheduleTypeId)
{
return m_document.GetElement(pipescheduleTypeId).Name;
}

/// <summary>
/// Get material name by Id
/// </summary>
/// <param name="materialId"></param>
/// <returns></returns>
private string GetMaterialNameById(ElementId materialId)
{
return m_document.GetElement(materialId).Name;
}

/// <summary>
/// Get segment name by Id
/// </summary>
/// <param name="segmentId"></param>
/// <returns></returns>
private string GetSegmentNameById(ElementId segmentId)
{
return m_document.GetElement(segmentId).Name;
}

/// <summary>
/// Get fitting name by Id
/// </summary>
/// <param name="fittingId"></param>
/// <returns></returns>
private string GetFittingNameById(ElementId fittingId)
{
FamilySymbol fs = m_document.GetElement(fittingId) as FamilySymbol;
return fs.Family.Name + " " + fs.Name;
}

/// <summary>
/// Get segment by Ids
/// </summary>
/// <param name="materialId"></param>
/// <param name="pipeScheduleTypeId"></param>
/// <returns></returns>
private ElementId GetSegmentByIds(ElementId materialId, ElementId pipeScheduleTypeId)
{
if ((materialId == ElementId.InvalidElementId) || (pipeScheduleTypeId == ElementId.InvalidElementId))
return ElementId.InvalidElementId;

Element material = m_document.GetElement(materialId);
Element pipeScheduleType = m_document.GetElement(pipeScheduleTypeId);
string segmentName = material.Name + " - " + pipeScheduleType.Name;
return GetSegmentByName(segmentName);

}

/// <summary>
/// Get pipe type name by Id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private string GetPipeTypeNameById(ElementId id)
{
return m_document.GetElement(id).Name;
}

/// <summary>
/// Get segment by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private ElementId GetSegmentByName(string name)
{
foreach (Segment segment in m_segments)
if (segment.Name == name)
return segment.Id;
return ElementId.InvalidElementId;
}

/// <summary>
/// Get fitting by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private ElementId GetFittingByName(string name)
{
foreach (FamilySymbol fitting in m_fittings)
if ((fitting.Family.Name + " " + fitting.Name) == name)

return fitting.Id;
return ElementId.InvalidElementId;
}

/// <summary>
/// Get material by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private ElementId GetMaterialByName(string name)
{
foreach (Material material in m_materials)
if (material.Name == name)
return material.Id;
return ElementId.InvalidElementId;
}

/// <summary>
/// Get pipe schedule type by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private ElementId GetPipeScheduleTypeByName(string name)
{
foreach (PipeScheduleType pipeScheduleType in m_pipeSchedules)
if (pipeScheduleType.Name == name)
return pipeScheduleType.Id;
return ElementId.InvalidElementId;

}

/// <summary>
/// Get pipe type by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private ElementId GetPipeTypeByName(string name)
{
foreach (PipeType pipeType in m_pipeTypes)
if (pipeType.Name == name)
return pipeType.Id;
return ElementId.InvalidElementId;

}

/// <summary>
/// Update fittings list
/// </summary>
private void UpdateFittingsList()
{
m_fittings = GetAllFittings(m_document);
}

/// <summary>
/// Update segments list
/// </summary>
private void UpdateSegmentsList()
{
m_segments = GetAllPipeSegments(m_document);
}

/// <summary>
/// Update pipe types list
/// </summary>
private void UpdatePipeTypesList()
{
m_pipeTypes = GetAllPipeTypes(m_document);
}

/// <summary>
/// Update pipe type schedules list
/// </summary>
private void UpdatePipeTypeSchedulesList()
{
m_pipeSchedules = GetAllPipeScheduleTypes(m_document);
}

/// <summary>
/// Update materials list
/// </summary>
private void UpdateMaterialsList()
{
m_materials = GetAllMaterials(m_document);
}

/// <summary>
/// Get all pipe segments
/// </summary>
/// <param name="document"></param>
/// <returns></returns>
public IEnumerable<PipeSegment> GetAllPipeSegments(Document document)
{
FilteredElementCollector fec = new FilteredElementCollector(document);
fec.OfClass(typeof(PipeSegment));
IEnumerable<PipeSegment> segments = fec.ToElements().Cast<PipeSegment>();
return segments;
}

/// <summary>
/// Get all fittings
/// </summary>
/// <param name="document"></param>
/// <returns></returns>
public IEnumerable<FamilySymbol> GetAllFittings(Document document)
{
FilteredElementCollector fec = new FilteredElementCollector(document);
fec.OfClass(typeof(FamilySymbol));
fec.OfCategory(BuiltInCategory.OST_PipeFitting);
IEnumerable<FamilySymbol> fittings = fec.ToElements().Cast<FamilySymbol>();
return fittings;
}

/// <summary>
/// Get all materials
/// </summary>
/// <param name="document"></param>
/// <returns></returns>
private IEnumerable<Material> GetAllMaterials(Document document)
{
FilteredElementCollector fec = new FilteredElementCollector(document);
fec.OfClass(typeof(Material));
IEnumerable<Material> materials = fec.ToElements().Cast<Material>();
return materials;
}

/// <summary>
/// Get all pipe schedule types
/// </summary>
/// <param name="document"></param>
/// <returns></returns>
public IEnumerable<PipeScheduleType> GetAllPipeScheduleTypes(Document document)
{
FilteredElementCollector fec = new FilteredElementCollector(document);
fec.OfClass(typeof(Autodesk.Revit.DB.Plumbing.PipeScheduleType));
IEnumerable<Autodesk.Revit.DB.Plumbing.PipeScheduleType> pipeScheduleTypes = fec.ToElements().Cast<Autodesk.Revit.DB.Plumbing.PipeScheduleType>();
return pipeScheduleTypes;
}

/// <summary>
/// Get all pipe types
/// </summary>
/// <param name="document"></param>
/// <returns></returns>
public IEnumerable<PipeType> GetAllPipeTypes(Document document)
{
ElementClassFilter ecf = new ElementClassFilter(typeof(PipeType));

FilteredElementCollector fec = new FilteredElementCollector(document);
fec.WherePasses(ecf);
IEnumerable<PipeType> pipeTypes = fec.ToElements().Cast<PipeType>();
return pipeTypes;
}

#endregion

}
}

RoutingPreferenceBuilderUtility.cs

//
// (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.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using System.Xml.Linq;
using System.IO;
using System.Xml;
using System.Diagnostics;
using System.Xml.Schema;
namespace Revit.SDK.Samples.RoutingPreferenceTools.CS
{
    /// <summary>
    /// A helper class to find paths of .rfa family files in a document.
    /// </summary>
    internal class FindFolderUtility
    {
        /// <summary>
        /// Create an instance of the helper class
        /// </summary>
      public FindFolderUtility(Autodesk.Revit.ApplicationServices.Application application)
      {
         m_application = application;
         m_basePath = GetFamilyBasePath();
         List<string> extraFamilyPaths = FindFolderUtility.GetAdditionalFamilyPaths();
         m_Familyfiles = new Dictionary<string, string>();
         GetAllFiles(m_basePath, m_Familyfiles);
         //Get additional .rfa files in user-defined paths specified in familypaths.xml
         foreach (string extraPath in extraFamilyPaths)
         {
            GetAllFiles(extraPath, m_Familyfiles);
         }
      }
      /// <summary>
      /// Gets a list of paths defined in the familypaths.xml file in the RoutingPreferenceTools.dll folder.
      /// </summary>
      /// <returns>A list of paths containing .rfa files</returns>
      public static List<string> GetAdditionalFamilyPaths()
      {
         List<string> pathsList = new List<string>();
          StreamReader reader = null;
         try
         {
            reader = new StreamReader(Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName + "\\familypaths.xml");
         }
         catch (System.Exception)
         {
            return pathsList;
         }
         XDocument pathsDoc = XDocument.Load(new XmlTextReader(reader));
         IEnumerable<XElement> paths = pathsDoc.Root.Elements("FamilyPath");
         foreach (XElement xpath in paths)
            try
            {
               XAttribute xaPath = xpath.Attribute(XName.Get("pathname"));
               string familyPath = xaPath.Value;
               pathsList.Add(familyPath);
            }
            catch (Exception)
            {
            }
         reader.Close();
         return pathsList;
      }
        /// <summary>
        /// Returns the full path of an .rfa file in the "Data" directory of a Revit installation given an .rfa filename.
        /// </summary>
        /// <param name="filename">an .rfa filename without any path, e.g. "Generic elbow.rfa"</param>
        /// <returns>The full path to the .rfa file</returns>
        public string FindFileFolder(string filename)
        {
            string retval = "";
            try
            {
                retval = m_Familyfiles[filename];
            }
            catch (KeyNotFoundException)
            {
            }
            return retval;
        }
        private static void GetAllFiles(string basePath, Dictionary<string, string> allPaths)
        {
            if (!System.IO.Directory.Exists(basePath))
                return;
            string[] paths = (Directory.GetFiles(basePath, "*.rfa"));
            foreach (string path in paths)
            {
                string filename = System.IO.Path.GetFileName(path);
                allPaths[filename] = path;
            }
            string[] dirs = Directory.GetDirectories(basePath);
            foreach (string dir in dirs)
            {
                GetAllFiles(dir, allPaths);
            }
        }
        private string GetFamilyBasePath()
        {
            string basePath = "";
            try
            {
                basePath = m_application.GetLibraryPaths()["Imperial Library"];
                Debug.WriteLine(basePath);
                if (basePath.EndsWith(@"\"))
                {
                    basePath = basePath.Substring(0, basePath.Length - 1);
                }
                basePath = (System.IO.Directory.GetParent(basePath)).ToString();
                if (!(string.IsNullOrEmpty(basePath)))
                    return basePath;
            }
            catch (Exception)
            {
            }
            try
            {
                basePath = m_application.GetLibraryPaths().First().Value;
                if (basePath.EndsWith(@"\"))
                {
                    basePath = basePath.Substring(0, basePath.Length - 1);
                }
                basePath = System.IO.Directory.GetParent((System.IO.Directory.GetParent(basePath)).ToString()).ToString();
                if (!(string.IsNullOrEmpty(basePath)))
                    return basePath;
            }
            catch (Exception)
            {
            }
            try
            {
                string exe = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
                basePath = System.IO.Directory.GetParent(System.IO.Path.GetDirectoryName(exe)).ToString();
            }
            catch (Exception)
            {
            }
            return basePath;
        }
        private string m_basePath;
        private Dictionary<string, string> m_Familyfiles;
        private Autodesk.Revit.ApplicationServices.Application m_application;
    }
    /// <summary>
    /// A simple exception class to help identify errors related to this application.
    /// </summary>
    internal class RoutingPreferenceDataException : System.Exception
    {
        public RoutingPreferenceDataException(string message)
        {
            m_message = message;
        }
        public override string ToString()
        {
            return m_message;
        }
        private string m_message;
    }
    /// <summary>
    /// A helper class to validate RoutingPreferenceBuilder xml documents against
    /// the embedded resource "RoutingPreferenceBuilderData.xsd"
    /// </summary>
    internal class SchemaValidationHelper
    {
        /// <summary>
        /// Returns true if a document is a valid RoutingPreferenceBuilder xml document, false otherwise.
        /// </summary>
        public static bool ValidateRoutingPreferenceBuilderXml(XDocument doc, out string message)
        {
            message = "";
            XmlSchemaSet schemas = new XmlSchemaSet();
            schemas.Add("", XmlReader.Create(new StringReader(GetXmlSchema())));
            try
            {
                doc.Validate(schemas, null);
                return true;
            }
            catch (System.Xml.Schema.XmlSchemaValidationException ex)
            {
                message = ex.Message + ", " + ex.LineNumber + ", " + ex.LinePosition;
                return false;
            }
        }
        private static string GetXmlSchema()
        {
           //string[] names = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
           Stream schemaStream = null;
           try
           {
              schemaStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Revit.SDK.Samples.RoutingPreferenceTools.CS.RoutingPreferenceBuilder.RoutingPreferenceBuilderData.xsd");
           }
           catch (Exception)
           {
              throw new Exception("Could not find embedded xml RotingPreferenceBuilder schema resource.");
           }
           if (schemaStream == null)
           {
              throw new Exception("Could not find embedded xml RotingPreferenceBuilder schema resource.");
           }
           System.IO.StreamReader stReader = new StreamReader(schemaStream);
           return stReader.ReadToEnd();
        }
    }
}