// // (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 { /// /// Queries all routing preferences and reports potential problems in the form of an XDocument. /// 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 /// /// Constructor /// /// /// public Analyzer(RoutingPreferenceManager routingPreferenceManager, Document document) { m_routingPreferenceManager = routingPreferenceManager; m_document = document; m_mepSize = 0; } /// /// Constructor /// /// /// /// public Analyzer(RoutingPreferenceManager routingPreferenceManager, double mepSize, Document document) { m_routingPreferenceManager = routingPreferenceManager; m_document = document; m_mepSize = Convert.ConvertValueToFeet(mepSize, m_document); } /// /// Get specific size query /// /// 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; } /// /// Get all segments from a the currently selected pipe type, get each size from each segment, /// collect, sort, and return. /// public static List GetAvailableSegmentSizes(RoutingPreferenceManager routingPreferenceManager, Autodesk.Revit.DB.Document document) { System.Collections.Generic.HashSet sizes = new HashSet(); 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 sizesSorted = sizes.ToList(); sizesSorted.Sort(); return sizesSorted; } /// /// Returns XML data for a variety of potential routing-preference problems. /// /// 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()) { 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 /// /// Get basic information about the PipeType /// 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; } /// /// 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. /// 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 sizesNotCovered = new List(); 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(SpecTypeId.PipeSize).GetUnitTypeId().TypeId)); 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 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; } /// /// 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.) /// /// /// 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; } /// ///Using routing preferences, display found segment and fitting info from the size and pipe type specified in the dialog. /// private List GetPreferredFittingsAndSegments() { List partIdInfoList = new List(); 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 preferredTypes = new List(); 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 } }