public Cons(Element element, bool pipeConnectorsOrdered = false) { var connectors = MepUtils.GetALLConnectorsFromElements(element); switch (element) { case Pipe pipe: List <Connector> filteredCons = new List <Connector>(); if (pipeConnectorsOrdered) { filteredCons = connectors .Where(c => c.ConnectorType.ToString() == "End") .OrderBy(c => c.Origin.X.Round(1)) .ThenBy(c => c.Origin.Y.Round(1)) .ThenBy(c => c.Origin.Z.Round(1)) .ToList(); } else { filteredCons = connectors.Where(c => c.ConnectorType.ToString() == "End").ToList(); } Primary = filteredCons.First(); Secondary = filteredCons.Last(); break; case FamilyInstance fi: foreach (Connector connector in connectors) { MEPConnectorInfo mci = connector.GetMEPConnectorInfo(); Count++; if (mci.IsPrimary) { Primary = connector; } else if (mci.IsSecondary) { Secondary = connector; } else { Tertiary = connector; } } if (Count > 1 && Secondary == null) { throw new Exception($"Element {element.Id.ToString()} has {Count} connectors and no secondary!"); } if (element is FamilyInstance) { if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeFitting) { var mf = ((FamilyInstance)element).MEPModel as MechanicalFitting; if (mf.PartType.ToString() == "Transition") { double primDia = (Primary.Radius * 2).Round(3); double secDia = (Secondary.Radius * 2).Round(3); Largest = primDia > secDia ? Primary : Secondary; Smallest = primDia < secDia ? Primary : Secondary; } } } break; default: throw new Exception("Cons: Element id nr.: " + element.Id.ToString() + " is not a Pipe or FamilyInstance!"); } }
public Result ExportNtr(ExternalCommandData cData) { // UIApplication uiApp = commandData.Application; Document doc = cData.Application.ActiveUIDocument.Document; try { #region Declaration of variables // Instance a collector FilteredElementCollector collector = new FilteredElementCollector(doc); // Define a Filter instance to filter by System Abbreviation ElementParameterFilter sysAbbr = Shared.Filter.ParameterValueGenericFilter(doc, InputVars.SysAbbr, InputVars.SysAbbrParam); // Declare pipeline grouping object IEnumerable <IGrouping <string, Element> > pipelineGroups; //Declare an object to hold collected elements from collector HashSet <Element> colElements = new HashSet <Element>(); #endregion #region Element collectors //If user chooses to export a single pipeline get only elements in that pipeline and create grouping. //Grouping is necessary even tho theres only one group to be able to process by the same code as the all pipelines case //If user chooses to export all pipelines get all elements and create grouping if (iv.ExportAllOneFile) { //Define a collector (Pipe OR FamInst) AND (Fitting OR Accessory OR Pipe). //This is to eliminate FamilySymbols from collector which would throw an exception later on. collector.WherePasses(new LogicalAndFilter(new List <ElementFilter> { new LogicalOrFilter(new List <ElementFilter> { new ElementCategoryFilter(BuiltInCategory.OST_PipeFitting), new ElementCategoryFilter(BuiltInCategory.OST_PipeAccessory), new ElementClassFilter(typeof(Pipe)) }), new LogicalOrFilter(new List <ElementFilter> { new ElementClassFilter(typeof(Pipe)), new ElementClassFilter(typeof(FamilyInstance)) }) })); colElements = collector.ToElements().ToHashSet(); } else if (iv.ExportAllSepFiles || iv.ExportSpecificPipeLine) { //Define a collector with multiple filters to collect PipeFittings OR PipeAccessories OR Pipes + filter by System Abbreviation //System Abbreviation filter also filters FamilySymbols out. collector.WherePasses( new LogicalOrFilter( new List <ElementFilter> { new ElementCategoryFilter(BuiltInCategory.OST_PipeFitting), new ElementCategoryFilter(BuiltInCategory.OST_PipeAccessory), new ElementClassFilter(typeof(Pipe)) })).WherePasses(sysAbbr); colElements = collector.ToElements().ToHashSet(); } else if (iv.ExportSelection) { //TODO: If selection is exported -- validate selected elements: //Only fittings, accessories and pipes allowed -> everything else must be filtered out ICollection <ElementId> selection = cData.Application.ActiveUIDocument.Selection.GetElementIds(); colElements = selection.Select(s => doc.GetElement(s)).ToHashSet(); } //DiameterLimit filter applied to ALL elements. //Filter out EXCLuded elements - 0 means no checkmark, GUID is for PCF_ELEM_EXCL //Filter by system PCF_PIPL_EXCL: c1c2c9fe-2634-42ba-89d0-5af699f54d4c //PROBLEM: If user exports selection which includes element in a PipingSystem which is not allowed //PROBLEM: no elements will be exported //SOLUTION: Turn off PipingSystemAllowed filter off for ExportSelection case. HashSet <Element> elements = (from element in colElements where NTR_Filter.FilterDiameterLimit(element) && element.get_Parameter(new Guid("CC8EC292-226C-4677-A32D-10B9736BFC1A")).AsInteger() == 0 && element.PipingSystemAllowed(doc) select element).ToHashSet(); //Add the elements from ARGD (Rigids) piping system back to working set //Which were filtered out by DiameterLimit, but still respecting PCF_ELEM_EXCL HashSet <Element> argdElemsOutsideDiaLimit = colElements.Where(x => !NTR_Filter.FilterDiameterLimit(x) && x.get_Parameter(new Guid("CC8EC292-226C-4677-A32D-10B9736BFC1A")).AsInteger() == 0 && x.MEPSystemAbbreviation() == "ARGD").ToHashSet(); //Combine the newly found ARGD elements back to main collection elements.UnionWith(argdElemsOutsideDiaLimit); //Create a grouping of elements based on the Pipeline identifier (System Abbreviation) pipelineGroups = from e in elements group e by e.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM).AsString(); #endregion #region Configuration validation //Validate if configuration has all pipelines defined //Else no LAST value will be written! //PROBLEM: If user has pipelines which have been marked as not allowed and do not wish to define them //PROBLEM: in the configuration file, this validation will throw an error //SOLUTION: Exclude the names of not allowed PipingSystems from the list. List <string> pipeSysAbbrs = Shared.MepUtils.GetDistinctPhysicalPipingSystemTypeNames(doc).ToList(); foreach (string sa in pipeSysAbbrs) { string returnValue = DataWriter.ReadPropertyFromDataTable(sa, conf.Pipelines, "LAST"); if (returnValue.IsNullOrEmpty()) { throw new Exception($"Pipeline {sa} is not defined in the configuration!"); } } #endregion outputBuilder.AppendLine("C Element definitions"); #region Pipeline management //TransactionGroup to rollback the changes in creating the NonBreakInElements using (TransactionGroup txGp = new TransactionGroup(doc)) { txGp.Start("Olets non breakin elems."); //List to store ALL created NonBreakInElements //List<NonBreakInElement> nbifAllList = new List<NonBreakInElement>(); foreach (IGrouping <string, Element> gp in pipelineGroups) { HashSet <Element> pipeList = (from element in gp where element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves select element).ToHashSet(); HashSet <Element> fittingList = (from element in gp where element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeFitting select element).ToHashSet(); HashSet <Element> accessoryList = (from element in gp where element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeAccessory select element).ToHashSet(); #region Olets and other non-break in items //Here be code to handle non-breaking in elements as olets by the following principle: //Find the non-breaking elements (fx. olets) and affected pipes. //Remove affected pipes from pipeList and create in a transaction new, broken up pieces of pipe coinciding //with olet and ends. Then add those pieces to the pipeList, !!!copy parameter values also!!! <- not needed for NTR?. //Process pipeList as usual and then delete those new dummy pipes from model. //TODO: Implement multiple types of non-breaking items per pipe -> only if needed -> cannot think of others than olets //SpudAdjustable -> Olets //Find fittings of this type: IEnumerable <IGrouping <int, Element> > spudAdjQry = fittingList.Where(x => x.OfPartType(PartType.SpudAdjustable)).GroupBy(x => x.OletRefOwnerIdAsInt()); IList <NonBreakInElement> nbifList = new List <NonBreakInElement>(); foreach (IGrouping <int, Element> group in spudAdjQry) { nbifList.Add(new NonBreakInElement(doc, group)); } //nbifAllList.AddRange(nbifList); //Remove the HeadPipes from the PipeList List <int> pipesToRemoveIds = nbifList.Select(x => x.HeadPipe.Id.IntegerValue).ToList(); pipeList = pipeList.ExceptWhere(x => pipesToRemoveIds.Contains(x.Id.IntegerValue)).ToHashSet(); //Transaction to create all part pipes using (Transaction tx1 = new Transaction(doc)) { tx1.Start("Create broken pipes."); foreach (var g in nbifList) { for (int i = 0; i < g.AllCreationPoints.Count - 1; i++) { int j = i + 1; g.CreatedElements.Add(Pipe.Create(doc, g.HeadPipe.MEPSystem.GetTypeId(), g.HeadPipe.GetTypeId(), g.HeadPipe.ReferenceLevel.Id, g.AllCreationPoints[i], g.AllCreationPoints[j])); } foreach (Element el in g.CreatedElements) { el.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(g.HeadPipe.Diameter); } //Add created pipes to pipeList pipeList.UnionWith(g.CreatedElements); } //Additional nodes need to be created for internal supports //Internal supports need a separate master node for each support if (iv.IncludeSteelStructure) { Guid tag4guid = new Guid("f96a5688-8dbe-427d-aa62-f8744a6bc3ee"); var SteelSupports = accessoryList.Where( x => x.get_Parameter(tag4guid).AsString() == "FRAME"); //Also modify accessoryList to remove the same said supports accessoryList = accessoryList.ExceptWhere( x => x.get_Parameter(tag4guid).AsString() == "FRAME").ToHashSet(); foreach (FamilyInstance fi in SteelSupports) { string familyName = fi.get_Parameter(BuiltInParameter.ELEM_FAMILY_PARAM).AsValueString(); //Currently only the following family is implemented if (familyName == "VEKS bærering modplade") { Element elType = doc.GetElement(fi.GetTypeId()); bool TopBool = elType.LookupParameter("Modpl_Top_Vis").AsInteger() == 1; bool BottomBool = elType.LookupParameter("Modpl_Bottom_Vis").AsInteger() == 1; bool LeftBool = elType.LookupParameter("Modpl_Left_Vis").AsInteger() == 1; bool RightBool = elType.LookupParameter("Modpl_Right_Vis").AsInteger() == 1; //Count how many extra nodes are needed int extraNodeCount = 0; if (TopBool) { extraNodeCount++; } if (BottomBool) { extraNodeCount++; } if (LeftBool) { extraNodeCount++; } if (RightBool) { extraNodeCount++; } if (extraNodeCount == 0) { continue; } else if (extraNodeCount == 1) { continue; } Cons supportCons = new Cons((Element)fi); //Getting the corresponding connectors with AllRefs method cannot be used //Because if two supports reside on same pipe //Subsequent iterations will get the original pipe, which will make overlapping segments //So connectors must be obtained by matching geometry //And it must be done separately at each iteration! var allConnectors = MepUtils.GetALLConnectorsFromElements(pipeList) .Where(c => c.ConnectorType == ConnectorType.End).ToHashSet(); var matchedPipeConnectors = allConnectors .Where(x => supportCons.Primary.Equalz(x, 1.0.MmToFt())) .ExceptWhere(x => x.Owner.Id.IntegerValue == fi.Id.IntegerValue); //Should be a null check here -> to tired to figure it out Connector FirstSideConStart = matchedPipeConnectors.First(); //Assume that supports will always be placed on pipes Connector FirstSideConEnd = (from Connector c in FirstSideConStart.ConnectorManager.Connectors where c.Id != FirstSideConStart.Id && (int)c.ConnectorType == 1 select c).FirstOrDefault(); Connector SecondSideConStart = matchedPipeConnectors.Last(); //Assume that supports will always be placed on pipes Connector SecondSideConEnd = (from Connector c in SecondSideConStart.ConnectorManager.Connectors where c.Id != SecondSideConStart.Id && (int)c.ConnectorType == 1 select c).FirstOrDefault(); //Create help lines to help with the geometry analysis //The point is to get a point along the line at 5 mm distance from start Line FirstSideLine = Line.CreateBound(FirstSideConStart.Origin, FirstSideConEnd.Origin); Line SecondSideLine = Line.CreateBound(SecondSideConStart.Origin, SecondSideConEnd.Origin); List <XYZ> creationPoints = new List <XYZ>(extraNodeCount + 2); if (extraNodeCount == 2) { creationPoints.Add(FirstSideConEnd.Origin); creationPoints.Add(FirstSideLine.Evaluate(2.5.MmToFt(), false)); creationPoints.Add(SecondSideLine.Evaluate(2.5.MmToFt(), false)); creationPoints.Add(SecondSideConEnd.Origin); } else if (extraNodeCount == 3) { creationPoints.Add(FirstSideConEnd.Origin); creationPoints.Add(FirstSideLine.Evaluate(5.0.MmToFt(), false)); creationPoints.Add(FirstSideConStart.Origin); creationPoints.Add(SecondSideLine.Evaluate(5.0.MmToFt(), false)); creationPoints.Add(SecondSideConEnd.Origin); //Dbg.PlaceAdaptiveFamilyInstance(doc, "Marker Line: Red", FirstSideConStart.Origin, FirstSidePoint); //Dbg.PlaceAdaptiveFamilyInstance(doc, "Marker Line: Red", SecondSideConStart.Origin, SecondSidePoint); } else if (extraNodeCount == 4) { creationPoints.Add(FirstSideConEnd.Origin); creationPoints.Add(FirstSideLine.Evaluate(7.5.MmToFt(), false)); creationPoints.Add(FirstSideLine.Evaluate(2.5.MmToFt(), false)); creationPoints.Add(SecondSideLine.Evaluate(2.5.MmToFt(), false)); creationPoints.Add(SecondSideLine.Evaluate(7.5.MmToFt(), false)); creationPoints.Add(SecondSideConEnd.Origin); } //Remove the original pipes from pipeList Pipe fPipe = (Pipe)FirstSideConStart.Owner; Pipe sPipe = (Pipe)SecondSideConStart.Owner; pipeList = pipeList.ExceptWhere(x => x.Id.IntegerValue == fPipe.Id.IntegerValue) .ExceptWhere(x => x.Id.IntegerValue == sPipe.Id.IntegerValue) .ToHashSet(); //Create extra pipes HashSet <Element> createdPipes = new HashSet <Element>(); for (int i = 0; i < creationPoints.Count - 1; i++) { int j = i + 1; createdPipes.Add(Pipe.Create(doc, fPipe.MEPSystem.GetTypeId(), fPipe.GetTypeId(), fPipe.ReferenceLevel.Id, creationPoints[i], creationPoints[j])); } foreach (Element el in createdPipes) { el.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(fPipe.Diameter); } //Add created pipes to pipeList pipeList.UnionWith(createdPipes); } else { } //Implement other possibilities later //This, I think, is needed to be able to interact with temporary pipes doc.Regenerate(); } } tx1.Commit(); } #endregion //TODO: Change the splitting of elements to follow the worksheets //There's now a mismatch of how many worksheets define the elements (ELEMENTS and SUPPORTS) and //the division into fittings and accessories. Would be more concise to follow the excel configuration //by worksheet StringBuilder sbPipes = NTR_Pipes.Export(gp.Key, pipeList, conf, doc); StringBuilder sbFittings = NTR_Fittings.Export(gp.Key, fittingList, conf, doc); StringBuilder sbAccessories = NTR_Accessories.Export(gp.Key, accessoryList, conf, doc); outputBuilder.Append(sbPipes); outputBuilder.Append(sbFittings); outputBuilder.Append(sbAccessories); } //Include steel structure here //Requires that the Support Pipe Accessories which the structure support are filtered in the above section if (iv.IncludeSteelStructure) { StringBuilder sbSteel = new NTR_Steel(doc).ExportSteel(); outputBuilder.Append(sbSteel); StringBuilder sbBoundaryConds = new NTR_Steel(doc).ExportBoundaryConditions(); outputBuilder.Append(sbBoundaryConds); } #region Debug //string ids = string.Empty; //foreach (var g in nbifAllList) foreach (var e in g.CreatedElements) ids += e.Id.ToString() + "\n"; //BuildingCoderUtilities.InfoMsg(ids); #endregion txGp.RollBack(); //Rollback the extra elements created //txGp.Commit(); //For debug purposes can be uncommented } #endregion #region Hangers //Temporary section to handle GenericModel hangers //Works only if all line in one file selected //outputBuilder.Append(NTR_GenericModels.ExportHangers(conf, doc)); #endregion #region Output // Output the processed data NTR_Output.Output output = new NTR_Output.Output(); output.OutputWriter(doc, outputBuilder, iv.OutputDirectoryFilePath); #endregion } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { return(Result.Cancelled); } catch (Exception ex) { throw new Exception(ex.Message); } return(Result.Succeeded); }
public AnalyticModel(HashSet <Element> elements) { AllElements = elements.ToList(); AllConnectors = MepUtils.GetALLConnectorsFromElements(elements).ToList(); }
public void AnalyzeSystem() { //Deviously hard to follow (and debug) code be here //Start analysis var openEnds = detectOpenEnds(Model); Connector To = null; Connector From = null; Node FromNode = null; Node ToNode = null; Element curElem = null; AnalyticElement curAElem = null; AnalyticSequence curSequence = null; bool continueSequence = false; IList <Connector> branchEnds = new List <Connector>(); for (int i = 0; i < Model.AllElements.Count; i++) { if (!continueSequence) { if (branchEnds.Count > 0) { From = branchEnds.FirstOrDefault(); branchEnds = branchEnds.ExceptWhere(c => c.Equalz(From, _1mmTol)).ToList(); FromNode = (from Node n in Model.AllNodes where n.PreviousCon != null && n.PreviousCon.Equalz(From, _1mmTol) select n).FirstOrDefault(); FromNode.NextCon = From; } else { From = openEnds.FirstOrDefault(); openEnds = openEnds.ExceptWhere(c => c.Equalz(From, _1mmTol)).ToList(); FromNode = new Node(); FromNode.NextCon = From; Model.AllNodes.Add(FromNode); curSequence = new AnalyticSequence(); } ToNode = new Node(); curElem = From.Owner; curAElem = new AnalyticElement(curElem); continueSequence = true; } switch (curElem) { case Pipe pipe: To = (from Connector c in pipe.ConnectorManager.Connectors //End of the host/dummy pipe where c.Id != From.Id && (int)c.ConnectorType == 1 select c).FirstOrDefault(); //Test if the ToNode already exists //TODO: This test must be copied to all other elements Node existingToNode = (from Node n in Model.AllNodes where (n.PreviousCon != null && n.PreviousCon.Equalz(To, _1mmTol)) || (n.NextCon != null && n.NextCon.Equalz(To, _1mmTol)) select n).FirstOrDefault(); if (existingToNode != null) { ToNode = existingToNode; if (ToNode.PreviousCon == null) { ToNode.PreviousCon = To; } else if (ToNode.NextCon == null) { ToNode.NextCon = To; } } else { ToNode.PreviousCon = To; Model.AllNodes.Add(ToNode); } curAElem.From = FromNode; curAElem.To = ToNode; //Assign correct element type to analytic element curAElem.Type = ElemType.Pipe; curSequence.Sequence.Add(curAElem); break; case FamilyInstance fi: Cons cons = MepUtils.GetConnectors(fi); int cat = fi.Category.Id.IntegerValue; switch (cat) { case (int)BuiltInCategory.OST_PipeFitting: var mf = fi.MEPModel as MechanicalFitting; var partType = mf.PartType; switch (partType) { case PartType.Elbow: //First Analytic Element XYZ elbowLoc = ((LocationPoint)fi.Location).Point; ToNode.PreviousLoc = elbowLoc; //The node has only element Location point defining it - ToNode.NextLoc = elbowLoc; //and not two adjacent Connectors as element connection nodes ToNode.Type = ElemType.Elbow; Model.AllNodes.Add(ToNode); curAElem.From = FromNode; curAElem.To = ToNode; curAElem.Type = ElemType.Elbow; curAElem.AnalyzeBend(); curSequence.Sequence.Add(curAElem); //Second Analytic Element //First determine the To connector To = (from Connector c in MepUtils.GetALLConnectorsFromElements(curElem) where c.Id != From.Id select c).FirstOrDefault(); FromNode = ToNode; //Switch to next element ToNode = new Node(); Model.AllNodes.Add(ToNode); ToNode.PreviousCon = To; curAElem = new AnalyticElement(curElem); curAElem.From = FromNode; curAElem.To = ToNode; curAElem.Type = ElemType.Pipe; curSequence.Sequence.Add(curAElem); break; case PartType.Tee: //Junction logic Node primNode = null; Node secNode = null; Node tertNode = null; //First analytic element XYZ teeLoc = ((LocationPoint)fi.Location).Point; ToNode.PreviousLoc = teeLoc; //The node has only element Location point defining it - ToNode.NextLoc = teeLoc; //and not two adjacent Connectors as element connection nodes ToNode.Type = ElemType.Tee; Model.AllNodes.Add(ToNode); curAElem.From = FromNode; curAElem.To = ToNode; curAElem.Type = ElemType.Tee; curSequence.Sequence.Add(curAElem); //Logic to return correct node to next element if (From.GetMEPConnectorInfo().IsPrimary) { primNode = FromNode; } else if (From.GetMEPConnectorInfo().IsSecondary) { secNode = FromNode; } else { tertNode = FromNode; } //From node is common for next two elements FromNode = ToNode; //Second Analytic Element if (From.GetMEPConnectorInfo().IsPrimary) { To = cons.Secondary; } else if (From.GetMEPConnectorInfo().IsSecondary) { To = cons.Primary; } else { To = cons.Primary; } ToNode = new Node(); Model.AllNodes.Add(ToNode); ToNode.PreviousCon = To; curAElem = new AnalyticElement(curElem); curAElem.From = FromNode; curAElem.To = ToNode; curAElem.Type = ElemType.Pipe; curSequence.Sequence.Add(curAElem); //Logic to return correct node to next element if (From.GetMEPConnectorInfo().IsPrimary) { secNode = ToNode; } else if (From.GetMEPConnectorInfo().IsSecondary) { primNode = ToNode; } else { primNode = ToNode; } //Third Analytic Element if (From.GetMEPConnectorInfo().IsPrimary) { To = cons.Tertiary; } else if (From.GetMEPConnectorInfo().IsSecondary) { To = cons.Tertiary; } else { To = cons.Secondary; } ToNode = new Node(); Model.AllNodes.Add(ToNode); ToNode.PreviousCon = To; curAElem = new AnalyticElement(curElem); curAElem.From = FromNode; curAElem.To = ToNode; curAElem.Type = ElemType.Pipe; curSequence.Sequence.Add(curAElem); //Logic to return correct node to next element if (From.GetMEPConnectorInfo().IsPrimary) { tertNode = ToNode; } else if (From.GetMEPConnectorInfo().IsSecondary) { tertNode = ToNode; } else { secNode = ToNode; } //Continuation logic Connector candidate1; Connector candidate2; if (From.GetMEPConnectorInfo().IsPrimary) { candidate1 = (from Connector c in Model.AllConnectors where c.Equalz(cons.Secondary, _1mmTol) select c).FirstOrDefault(); candidate2 = (from Connector c in Model.AllConnectors where c.Equalz(cons.Tertiary, _1mmTol) select c).FirstOrDefault(); if (candidate1 != null) { To = cons.Secondary; ToNode = secNode; if (candidate2 != null) { branchEnds.Add(candidate2); } } else if (candidate2 != null) { To = cons.Tertiary; ToNode = tertNode; } } else if (From.GetMEPConnectorInfo().IsSecondary) { candidate1 = (from Connector c in Model.AllConnectors where c.Equalz(cons.Primary, _1mmTol) select c).FirstOrDefault(); candidate2 = (from Connector c in Model.AllConnectors where c.Equalz(cons.Tertiary, _1mmTol) select c).FirstOrDefault(); if (candidate1 != null) { To = cons.Primary; ToNode = primNode; if (candidate2 != null) { branchEnds.Add(candidate2); } } else if (candidate2 != null) { To = cons.Tertiary; ToNode = tertNode; } } else { candidate1 = (from Connector c in Model.AllConnectors where c.Equalz(cons.Primary, _1mmTol) select c).FirstOrDefault(); candidate2 = (from Connector c in Model.AllConnectors where c.Equalz(cons.Secondary, _1mmTol) select c).FirstOrDefault(); if (candidate1 != null) { To = cons.Primary; ToNode = primNode; if (candidate2 != null) { branchEnds.Add(candidate2); } } else if (candidate2 != null) { To = cons.Secondary; ToNode = secNode; } } break; case PartType.Transition: To = (from Connector c in MepUtils.GetALLConnectorsFromElements(curElem) where c.Id != From.Id select c).FirstOrDefault(); ToNode.PreviousCon = To; Model.AllNodes.Add(ToNode); curAElem.From = FromNode; curAElem.To = ToNode; //Assign correct element type to analytic element curAElem.Type = ElemType.Transition; //Determine start dia and second dia curAElem.AnalyzeReducer(); curSequence.Sequence.Add(curAElem); break; case PartType.Cap: //Handles flanges because of the workaround PartType.Cap for flanges //Real Caps are ignored for now To = (from Connector c in MepUtils.GetALLConnectorsFromElements(curElem) where c.Id != From.Id select c).FirstOrDefault(); ToNode.PreviousCon = To; Model.AllNodes.Add(ToNode); curAElem.From = FromNode; curAElem.To = ToNode; //Assign correct element type to analytic element curAElem.Type = ElemType.Rigid; curSequence.Sequence.Add(curAElem); break; case PartType.Union: throw new NotImplementedException(); case PartType.SpudAdjustable: throw new NotImplementedException(); default: continue; } break; case (int)BuiltInCategory.OST_PipeAccessory: if (From.GetMEPConnectorInfo().IsPrimary) { To = cons.Secondary; } else if (From.GetMEPConnectorInfo().IsSecondary) { To = cons.Primary; } else { throw new Exception("Something went wrong with connectors of element " + curElem.Id.ToString()); } ToNode.PreviousCon = To; Model.AllNodes.Add(ToNode); curAElem.From = FromNode; curAElem.To = ToNode; //Assign correct element type to analytic element curAElem.Type = ElemType.Rigid; curSequence.Sequence.Add(curAElem); break; default: continue; } break; default: continue; } //Prepare to restart iteration Model.AllConnectors = Model.AllConnectors.ExceptWhere(c => c.Owner.Id.IntegerValue == curElem.Id.IntegerValue).ToList(); From = (from Connector c in Model.AllConnectors where c.Equalz(To, _1mmTol) select c).FirstOrDefault(); if (From != null) { curElem = From.Owner; FromNode = ToNode; FromNode.NextCon = From; ToNode = new Node(); curAElem = new AnalyticElement(curElem); } else { continueSequence = false; openEnds = openEnds.ExceptWhere(c => c.Equalz(To, _1mmTol)).ToList(); if (branchEnds.Count > 0 && To != null) { branchEnds = branchEnds.ExceptWhere(c => c.Equalz(To, _1mmTol)).ToList(); } if (branchEnds.Count < 1) { Model.Sequences.Add(curSequence); } } } //Reference ALL analytic elements in the collecting pool foreach (var sequence in Model.Sequences) { foreach (var item in sequence.Sequence) { Model.AllAnalyticElements.Add(item); } } //Loop over ALL nodes and populate coordinate information foreach (Node n in Model.AllNodes) { n.PopulateCoordinates(); } BuildingCoderUtilities.InfoMsg(Model.AllNodes.Count.ToString()); }
public static StringBuilder DetectAndWriteEndsAndConnections( string key, HashSet <Element> pipes, HashSet <Element> fittings, HashSet <Element> accessories, Document doc) { StringBuilder sb = new StringBuilder(); HashSet <Element> all = new HashSet <Element>(pipes); all.UnionWith(fittings); all.UnionWith(accessories); //Iterate over all elements and check their connected counterparts //If they satisfy certain conditions -> write end continuation property //Cases for connected connectors: //1) In different PipingSystem -> Pipeline continuation //1.1) If Selection and connector belongs to an element not in selection -> Pipeline continuation //2) Belongs to MechanicalEquipment -> Equipment continuation -> write tags //3) Free end -> Null connection foreach (Element elem in all) { HashSet <Connector> cons = new HashSet <Connector>(); switch (elem) { case Pipe pipe: var consPipe = new Cons(elem); cons.Add(consPipe.Primary); cons.Add(consPipe.Secondary); break; case FamilyInstance fi: cons = MepUtils.GetALLConnectorsFromElements(elem); break; default: continue; } foreach (Connector con in cons) { //This if should also filter out free ends... if (con.IsConnected) { var allRefsNotFiltered = MepUtils.GetAllConnectorsFromConnectorSet(con.AllRefs); var correspondingCon = allRefsNotFiltered .Where(x => x.Domain == Domain.DomainPiping) .Where(x => x.Owner.Id.IntegerValue != elem.Id.IntegerValue).FirstOrDefault(); //CASE: Free end -> Do nothing yet, for simplicity //This also catches empty cons on multicons accessories //Example: pressure take outs on filters. if (correspondingCon == null) { continue; } //CASE: If selection is exported, continuation for elements not in selection //Even if same pipeline if (iv.ExportSelection) { bool inElementsList = !all.Any(x => x.Id.IntegerValue == correspondingCon.Owner.Id.IntegerValue); //bool inDiscardedPipes = !discardedPipes.Any(x => x.Id.IntegerValue == correspondingCon.Owner.Id.IntegerValue); if (inElementsList)// && inDiscardedPipes) { //CASE: Con belongs to MechanicalEquipment if (correspondingCon.Owner.Category.Id.IntegerValue == (int)BuiltInCategory.OST_MechanicalEquipment) { sb.AppendLine("END-CONNECTION-EQUIPMENT"); sb.Append(PCF_Functions.EndWriter.WriteCO(correspondingCon.Origin)); sb.Append(PCF_Functions.ParameterDataWriter .ParameterValue("CONNECTION-REFERENCE", new[] { "TAG 1", "TAG 2", "TAG 3", "TAG 4" }, correspondingCon.Owner)); continue; } //CASE: Any other component else { sb.AppendLine("END-CONNECTION-PIPELINE"); sb.Append(PCF_Functions.EndWriter.WriteCO(correspondingCon.Origin)); sb.AppendLine(" PIPELINE-REFERENCE " + correspondingCon.MEPSystemAbbreviation(doc)); continue; } } //CASE: None of the above hit -> continue with loop execution //To prevent from falling through to non selection cases. continue; } //CASE: Con belongs to MechanicalEquipment else if (correspondingCon.Owner.Category.Id.IntegerValue == (int)BuiltInCategory.OST_MechanicalEquipment) { sb.AppendLine("END-CONNECTION-EQUIPMENT"); sb.Append(PCF_Functions.EndWriter.WriteCO(correspondingCon.Origin)); sb.Append(PCF_Functions.ParameterDataWriter .ParameterValue("CONNECTION-REFERENCE", new[] { "TAG 1", "TAG 2", "TAG 3", "TAG 4" }, correspondingCon.Owner)); continue; } //CASE: If corrCon belongs to different Pipeline -> unconditional end //MechanicalEquipment cons should belong to the same Piping System, else... else if (correspondingCon.MEPSystemAbbreviation(doc) != key) { sb.AppendLine("END-CONNECTION-PIPELINE"); sb.Append(PCF_Functions.EndWriter.WriteCO(correspondingCon.Origin)); sb.AppendLine(" PIPELINE-REFERENCE " + correspondingCon.MEPSystemAbbreviation(doc)); continue; } //CASE: If corrCon belongs to EXISTING component //Write PIPELINE-REFERENCE -> EXISTING Element hostElement = correspondingCon.Owner; //GUID is for PCF_ELEM_SPEC Parameter existingParameter = hostElement.get_Parameter(new Guid("90be8246-25f7-487d-b352-554f810fcaa7")); if (existingParameter.AsString() == "EXISTING") { sb.AppendLine("END-CONNECTION-PIPELINE"); sb.Append(PCF_Functions.EndWriter.WriteCO(correspondingCon.Origin)); sb.AppendLine(" PIPELINE-REFERENCE EKSISTERENDE"); } } } } return(sb); }
internal Result ExecuteMyCommand(UIApplication uiApp, ref string msg) { // UIApplication uiApp = commandData.Application; //Test comment Document doc = uiApp.ActiveUIDocument.Document; UIDocument uidoc = uiApp.ActiveUIDocument; try { #region Declaration of variables // Instance a collector FilteredElementCollector collector = new FilteredElementCollector(doc); //FilteredElementCollector pipeTypeCollector = new FilteredElementCollector(doc); //Obsolete??? // Define a Filter instance to filter by System Abbreviation ElementParameterFilter sysAbbr = Shared.Filter.ParameterValueGenericFilter(doc, InputVars.SysAbbr, InputVars.SysAbbrParam); // Declare pipeline grouping object IEnumerable <IGrouping <string, Element> > pipelineGroups; //Declare an object to hold collected elements from collector HashSet <Element> colElements = new HashSet <Element>(); // Instance a collecting stringbuilder StringBuilder sbCollect = new StringBuilder(); #endregion #region Compose preamble //Compose preamble Composer composer = new Composer(); StringBuilder sbPreamble = composer.PreambleComposer(); //Append preamble sbCollect.Append(sbPreamble); #endregion #region Element collectors //If user chooses to export a single pipeline get only elements in that pipeline and create grouping. //Grouping is necessary even tho theres only one group to be able to process by the same code as the all pipelines case //If user chooses to export all pipelines get all elements and create grouping if (InputVars.ExportAllOneFile) { //Define a collector (Pipe OR FamInst) AND (Fitting OR Accessory OR Pipe). //This is to eliminate FamilySymbols from collector which would throw an exception later on. collector.WherePasses(new LogicalAndFilter(new List <ElementFilter> { new LogicalOrFilter(new List <ElementFilter> { new ElementCategoryFilter(BuiltInCategory.OST_PipeFitting), new ElementCategoryFilter(BuiltInCategory.OST_PipeAccessory), new ElementClassFilter(typeof(Pipe)) }), new LogicalOrFilter(new List <ElementFilter> { new ElementClassFilter(typeof(Pipe)), new ElementClassFilter(typeof(FamilyInstance)) }) })); colElements = collector.ToElements().ToHashSet(); } else if (InputVars.ExportAllSepFiles || InputVars.ExportSpecificPipeLine) { //Define a collector with multiple filters to collect PipeFittings OR PipeAccessories OR Pipes + filter by System Abbreviation //System Abbreviation filter also filters FamilySymbols out. collector.WherePasses( new LogicalOrFilter( new List <ElementFilter> { new ElementCategoryFilter(BuiltInCategory.OST_PipeFitting), new ElementCategoryFilter(BuiltInCategory.OST_PipeAccessory), new ElementClassFilter(typeof(Pipe)) })).WherePasses(sysAbbr); colElements = collector.ToElements().ToHashSet(); } else if (InputVars.ExportSelection) { ICollection <ElementId> selection = uiApp.ActiveUIDocument.Selection.GetElementIds(); colElements = selection.Select(s => doc.GetElement(s)).ToHashSet(); } #region Sub: Filtering HashSet <Element> elements; try { //DiameterLimit filter applied to ALL elements. var filtering = from element in colElements where Filters.FilterDL(element) select element; //Filter out EXCLUDED elements -> 0 means no checkmark filtering = from element in filtering where element.get_Parameter(new plst().PCF_ELEM_EXCL.Guid).AsInteger() == 0 select element; //Filter out EXCLUDED pipelines -> 0 means no checkmark filtering = filtering.Where(x => x.PipingSystemAllowed(doc) == true); //Remove instrument pipes filtering = filtering.ExceptWhere(x => x.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM) .AsString() == "INSTR"); //Filter out elements with specified PCF_ELEM_SPEC string if (InputVars.PCF_ELEM_SPEC_FILTER.IsNullOrEmpty() == false) { filtering = filtering.ExceptWhere(x => x.get_Parameter(new plst().PCF_ELEM_SPEC.Guid).AsString() == InputVars.PCF_ELEM_SPEC_FILTER); } //If exporting to ISO, remove some not needed elements if (InputVars.ExportToIsogen) { //When exporting to Plant3D ISO creation, remove the group with the Piping System: Analysis Rigids (ARGD) filtering = filtering .Where(x => !(x.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM).AsString() == "ARGD")); ////Also remove anchor symbols -> not needed for ISO ////Currently not removed -> used for floor symbols //filtering = filtering.ExceptWhere(x => x // .get_Parameter(BuiltInParameter.ELEM_FAMILY_AND_TYPE_PARAM) // .AsValueString() == "Support Symbolic: ANC"); } //Create a grouping of elements based on the Pipeline identifier (System Abbreviation) pipelineGroups = from e in filtering group e by e.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM).AsString(); elements = filtering.ToHashSet(); } catch (Exception ex) { throw new Exception("Filtering in Main threw an exception:\n" + ex.Message + "\nTo fix:\n" + "1. See if parameter PCF_ELEM_EXCL exists, if not, rerun parameter import."); } #endregion #endregion #region Initialize Material Data //TEST: Do not write material data to elements with EXISTING-INCLUDE spec //HashSet<Element> existInclElements = elements.Where(x => // x.get_Parameter(new plst().PCF_ELEM_SPEC.Guid).AsString() == "EXISTING-INCLUDE").ToHashSet(); ////Remember the clearing of previous run data in transaction below //elements = elements.ExceptWhere(x => // x.get_Parameter(new plst().PCF_ELEM_SPEC.Guid).AsString() == "EXISTING-INCLUDE").ToHashSet(); //Set the start number to count the COMPID instances and MAT groups. int elementIdentificationNumber = 0; int materialGroupIdentifier = 0; //Make sure that every element has PCF_MAT_DESCR filled out. foreach (Element e in elements) { if (string.IsNullOrEmpty(e.get_Parameter(new plst().PCF_MAT_DESCR.Guid).AsString())) { uidoc.Selection.SetElementIds(new List <ElementId>(1) { e.Id }); BuildingCoderUtilities.ErrorMsg("PCF_MAT_DESCR is empty for element " + e.Id + "! Please, correct this issue before exporting again."); throw new Exception("PCF_MAT_DESCR is empty for element " + e.Id + "! Please, correct this issue before exporting again."); } } //Initialize material group numbers on the elements IEnumerable <IGrouping <string, Element> > materialGroups = from e in elements group e by e.get_Parameter(new plst().PCF_MAT_DESCR.Guid).AsString(); using (Transaction trans = new Transaction(doc, "Set PCF_ELEM_COMPID and PCF_MAT_ID")) { trans.Start(); //Clear MTL data from previous runs for elements with EXISTING-INCLUDE spec //foreach (Element e in existInclElements) //{ // e.get_Parameter(new plst().PCF_ELEM_COMPID.Guid).Set(""); // e.get_Parameter(new plst().PCF_MAT_ID.Guid).Set(""); //} //Access groups foreach (IEnumerable <Element> group in materialGroups) { materialGroupIdentifier++; //Access parameters foreach (Element element in group) { elementIdentificationNumber++; element.get_Parameter(new plst().PCF_ELEM_COMPID.Guid).Set(elementIdentificationNumber.ToString()); element.get_Parameter(new plst().PCF_MAT_ID.Guid).Set(materialGroupIdentifier.ToString()); } } trans.Commit(); } //If turned on, write wall thickness of all components if (InputVars.WriteWallThickness) { //Assign correct wall thickness to elements. using (Transaction trans1 = new Transaction(doc)) { trans1.Start("Set wall thickness for pipes!"); ParameterDataWriter.SetWallThicknessPipes(elements); trans1.Commit(); } } #endregion using (TransactionGroup txGp = new TransactionGroup(doc)) { txGp.Start("Bogus transactionGroup for the break in hangers"); #region Pipeline management foreach (IGrouping <string, Element> gp in pipelineGroups) { HashSet <Element> pipeList = (from element in gp where element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves select element).ToHashSet(); HashSet <Element> fittingList = (from element in gp where element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeFitting select element).ToHashSet(); HashSet <Element> accessoryList = (from element in gp where element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeAccessory select element).ToHashSet(); StringBuilder sbPipeline = new PCF_Pipeline.PCF_Pipeline_Export().Export(gp.Key, doc); StringBuilder sbFilename = PCF_Pipeline.Filename.BuildAndWriteFilename(doc); StringBuilder sbEndsAndConnections = PCF_Pipeline.EndsAndConnections .DetectAndWriteEndsAndConnections(gp.Key, pipeList, fittingList, accessoryList, doc); #region BrokenPipes //Here be code to handle break in accessories that act as supports //Find the supports in current acessoryList and add to supportList //Instantiate a brokenPipesGroup class //Collect all Connectors from brokenPipesList and find the longest distance //Create a temporary pipe from the Connectors with longest distance //Copy PCF_ELEM parameter values to the temporary pipe //Add the temporary pipe to the pipeList //Roll back the TransactionGroup after the elements are sent to Export class' Export methods. List <BrokenPipesGroup> bpgList = new List <BrokenPipesGroup>(); List <Element> supportsList = accessoryList.Where(x => x.ComponentClass1(doc) == "Pipe Support").ToList(); while (supportsList.Count > 0) { //Get an element to start traversing Element seedElement = supportsList.FirstOrDefault(); if (seedElement == null) { throw new Exception("BrokenPipes: Seed element returned null! supportsList.Count is " + supportsList.Count); } //Instantiate the BrokenPipesGroup BrokenPipesGroup bpg = new BrokenPipesGroup(seedElement, gp.Key); //Traverse system bpg.Traverse(doc); //Remove the support Elements from the collection to keep track of the while loop foreach (Element support in bpg.SupportsOnPipe) { supportsList = supportsList.ExceptWhere(x => x.Id.IntegerValue == support.Id.IntegerValue).ToList(); } bpgList.Add(bpg); } using (Transaction tx = new Transaction(doc)) { tx.Start("Create healed pipes"); foreach (BrokenPipesGroup bpg in bpgList) { //Remove the broken pipes from the pipeList //If there's only one broken pipe, then there's no need to do anything //If there's no broken pipes, then there's no need to do anything either if (bpg.BrokenPipes.Count != 0 && bpg.BrokenPipes.Count != 1) { foreach (Element pipe in bpg.BrokenPipes) { pipeList = pipeList.ExceptWhere(x => x.Id.IntegerValue == pipe.Id.IntegerValue).ToHashSet(); } //Using the new IEqualityComparer for Connectors to get distinct connectors in the collection var brokenCons = MepUtils.GetALLConnectorsFromElements(bpg.BrokenPipes.ToHashSet(), new ConnectorXyzComparer(2.0.MmToFt())); //Create distinct pair combinations with distance from all broken connectors //https://stackoverflow.com/a/47003122/6073998 List <(Connector c1, Connector c2, double dist)> pairs = brokenCons .SelectMany ( (fst, i) => brokenCons.Skip(i + 1).Select(snd => (fst, snd, fst.Origin.DistanceTo(snd.Origin))) ) .ToList(); var longest = pairs.MaxBy(x => x.dist).FirstOrDefault(); Pipe dPipe = (Pipe)longest.c1.Owner; bpg.HealedPipe = Pipe.Create(doc, dPipe.MEPSystem.GetTypeId(), dPipe.GetTypeId(), dPipe.ReferenceLevel.Id, longest.c1.Origin, longest.c2.Origin); Pipe donorPipe = (Pipe)bpg.BrokenPipes.FirstOrDefault(); bpg.HealedPipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(donorPipe.Diameter); //Add the healed pipe to the pipeList for processing pipeList.Add(bpg.HealedPipe); } } tx.Commit(); } //Now the healed pipe must be populated by the parameters from a donorPipe using (Transaction tx = new Transaction(doc)) { //Gather all relevant parameter definitions List <pdef> plist = new plst().LPAll.Where(x => x.Domain == "ELEM" && x.Usage == "U").ToList(); plist.Add(new plst().PCF_MAT_ID); tx.Start("Populate the HealedPipe parameters!"); foreach (BrokenPipesGroup bpg in bpgList) { //Skip iteration if there's only 1 or no broken pipes if (bpg.BrokenPipes.Count == 0 || bpg.BrokenPipes.Count == 1) { continue; } Element donorPipe = bpg.BrokenPipes.FirstOrDefault(); foreach (pdef p in plist) { Parameter donorParameter = donorPipe.get_Parameter(p.Guid); if (donorParameter == null) { continue; } switch (donorParameter.StorageType) { case StorageType.None: continue; case StorageType.Integer: int donorInt = donorParameter.AsInteger(); if (donorInt == 0) { continue; } Parameter targetParInt = bpg.HealedPipe.get_Parameter(p.Guid); targetParInt.Set(donorInt); break; case StorageType.Double: continue; case StorageType.String: string donorStr = donorParameter.AsString(); if (donorStr.IsNullOrEmpty()) { continue; } Parameter targetParStr = bpg.HealedPipe.get_Parameter(p.Guid); targetParStr.Set(donorStr); break; case StorageType.ElementId: continue; default: continue; } } } tx.Commit(); } #endregion StringBuilder sbPipes = new PCF_Pipes.PCF_Pipes_Export().Export(gp.Key, pipeList, doc); StringBuilder sbFittings = new PCF_Fittings.PCF_Fittings_Export().Export(gp.Key, fittingList, doc); StringBuilder sbAccessories = new PCF_Accessories.PCF_Accessories_Export().Export(gp.Key, accessoryList, doc); sbCollect.Append(sbPipeline); sbCollect.Append(sbFilename); sbCollect.Append(sbEndsAndConnections); sbCollect.Append(sbPipes); sbCollect.Append(sbFittings); sbCollect.Append(sbAccessories); } #endregion txGp.RollBack(); //RollBack the temporary created elements } #region Materials StringBuilder sbMaterials = composer.MaterialsSection(materialGroups); sbCollect.Append(sbMaterials); #endregion #region Output // Output the processed data PCF_Output.Output output = new PCF_Output.Output(); output.OutputWriter(sbCollect); #endregion } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { return(Result.Cancelled); } catch (Exception ex) { throw new Exception(ex.Message); } return(Result.Succeeded); }