Beispiel #1
0
        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!");
            }
        }
Beispiel #2
0
        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());
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }