Пример #1
0
        public StringBuilder Export(string pipeLineAbbreviation, HashSet <Element> elements, Document document)
        {
            Document doc   = document;
            string   key   = pipeLineAbbreviation;
            plst     pList = new plst();
            //paramList = new plst();
            //The list of fittings, sorted by TYPE then SKEY
            IList <Element> accessoriesList = elements.
                                              OrderBy(e => e.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString()).
                                              ThenBy(e => e.get_Parameter(pList.PCF_ELEM_SKEY.Guid).AsString()).ToList();

            StringBuilder sbAccessories = new StringBuilder();

            //This is a workaround to try to determine what element caused an exception
            Element element = null;

            try
            {
                foreach (Element Element in accessoriesList)
                {
                    //This is a workaround to try to determine what element caused an exception
                    element = Element;

                    sbAccessories.AppendLine(element.get_Parameter(new plst().PCF_ELEM_TYPE.Guid).AsString());
                    sbAccessories.AppendLine("    COMPONENT-IDENTIFIER " + element.get_Parameter(new plst().PCF_ELEM_COMPID.Guid).AsString());

                    if (element.get_Parameter(new plst().PCF_ELEM_SPEC.Guid).AsString() == "EXISTING-INCLUDE")
                    {
                        sbAccessories.AppendLine("    STATUS DOTTED-UNDIMENSIONED");
                        sbAccessories.AppendLine("    MATERIAL-LIST EXCLUDE");
                    }

                    //Write Plant3DIso entries if turned on
                    if (InputVars.ExportToIsogen)
                    {
                        sbAccessories.Append(Composer.Plant3DIsoWriter(element, doc));
                    }

                    //Cast the elements gathered by the collector to FamilyInstances
                    FamilyInstance familyInstance = (FamilyInstance)element;
                    Options        options        = new Options();

                    //Gather connectors of the element
                    var cons = mp.GetConnectors(element);

                    //Switch to different element type configurations
                    switch (element.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString())
                    {
                    case ("FILTER"):
                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, cons.Primary));
                        sbAccessories.Append(EndWriter.WriteEP2(element, cons.Secondary));
                        sbAccessories.Append(pdw.ParameterValue("TAG", new[] { "TAG 1", "TAG 2", "TAG 3" }, element));
                        break;

                    case ("INSTRUMENT"):
                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, cons.Primary));
                        sbAccessories.Append(EndWriter.WriteEP2(element, cons.Secondary));
                        sbAccessories.Append(EndWriter.WriteCP(familyInstance));
                        sbAccessories.Append(pdw.ParameterValue("TAG", new[] { "TAG 1", "TAG 2", "TAG 3" }, element));

                        break;

                    case ("VALVE"):
                        goto case ("INSTRUMENT");

                    case ("VALVE-ANGLE"):
                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, cons.Primary));
                        sbAccessories.Append(EndWriter.WriteEP2(element, cons.Secondary));

                        //The centre point is obtained by creating an bound line from primary connector and projecting the secondary point on the line.
                        XYZ  reverseConnectorVector = -cons.Primary.CoordinateSystem.BasisZ;
                        Line primaryLine            = Line.CreateBound(cons.Primary.Origin, cons.Primary.Origin + reverseConnectorVector * 10);
                        XYZ  centrePoint            = primaryLine.Project(cons.Secondary.Origin).XYZPoint;

                        sbAccessories.Append(EndWriter.WriteCP(centrePoint));

                        sbAccessories.Append(pdw.ParameterValue("TAG", new[] { "TAG 1", "TAG 2", "TAG 3" }, element));

                        break;

                    case ("INSTRUMENT-DIAL"):

                        //Connector information extraction
                        sbAccessories.Append(EndWriter.WriteEP1(element, cons.Primary));

                        XYZ primConOrigin = cons.Primary.Origin;

                        //Analyses the geometry to obtain a point opposite the main connector.
                        //Extraction of the direction of the connector and reversing it
                        reverseConnectorVector = -cons.Primary.CoordinateSystem.BasisZ;
                        Line detectorLine = Line.CreateBound(primConOrigin, primConOrigin + reverseConnectorVector * 10);
                        //Begin geometry analysis
                        GeometryElement geometryElement = familyInstance.get_Geometry(options);

                        //Prepare resulting point
                        XYZ endPointAnalyzed = null;

                        foreach (GeometryObject geometry in geometryElement)
                        {
                            GeometryInstance instance = geometry as GeometryInstance;
                            if (null == instance)
                            {
                                continue;
                            }
                            foreach (GeometryObject instObj in instance.GetInstanceGeometry())
                            {
                                Solid solid = instObj as Solid;
                                if (null == solid || 0 == solid.Faces.Size || 0 == solid.Edges.Size)
                                {
                                    continue;
                                }
                                foreach (Face face in solid.Faces)
                                {
                                    IntersectionResultArray results = null;
                                    XYZ intersection = null;
                                    try
                                    {
                                        SetComparisonResult result = face.Intersect(detectorLine, out results);
                                        if (result != SetComparisonResult.Overlap)
                                        {
                                            continue;
                                        }
                                        intersection = results.get_Item(0).XYZPoint;
                                        if (intersection.IsAlmostEqualTo(primConOrigin) == false)
                                        {
                                            endPointAnalyzed = intersection;
                                        }
                                    }
                                    catch (Exception)
                                    {
                                        continue;
                                    }
                                }
                            }
                        }

                        //If the point is still null after geometry intersection, it means the analysis failed
                        //Create an artificial point
                        if (endPointAnalyzed == null)
                        {
                            endPointAnalyzed = cons.Primary.Origin + reverseConnectorVector * 2;
                        }

                        sbAccessories.Append(EndWriter.WriteCO(endPointAnalyzed));
                        sbAccessories.Append(pdw.ParameterValue("TAG", new[] { "TAG 1", "TAG 2", "TAG 3" }, element));
                        break;

                    case "SUPPORT":
                        sbAccessories.Append(EndWriter.WriteCO(familyInstance, cons.Primary));
                        sbAccessories.Append(pdw.ParameterValue("TAG", new[] { "TAG 1", "TAG 2", "TAG 3" }, element));
                        break;

                    case "FLOOR-SYMBOL":
                        sbAccessories.Append(EndWriter.WriteCO(familyInstance));
                        break;

                    case "INSTRUMENT-3WAY":
                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, cons.Primary));
                        sbAccessories.Append(EndWriter.WriteEP2(element, cons.Secondary));
                        sbAccessories.Append(EndWriter.WriteEP3(element, cons.Tertiary));
                        sbAccessories.Append(EndWriter.WriteCP(familyInstance));
                        break;
                    }

                    Composer elemParameterComposer = new Composer();
                    sbAccessories.Append(elemParameterComposer.ElemParameterWriter(element));

                    #region CII export
                    if (InputVars.ExportToCII && !string.Equals(element.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString(), "SUPPORT"))
                    {
                        sbAccessories.Append(Composer.CIIWriter(doc, key));
                    }
                    #endregion

                    sbAccessories.Append("    UNIQUE-COMPONENT-IDENTIFIER ");
                    sbAccessories.Append(element.UniqueId);
                    sbAccessories.AppendLine();

                    //Process tap entries of the element if any
                    //Diameter Limit nullifies the tapsWriter output if the tap diameter is less than the limit so it doesn't get exported
                    if (string.IsNullOrEmpty(element.LookupParameter("PCF_ELEM_TAP1").AsString()) == false)
                    {
                        TapsWriter tapsWriter = new TapsWriter(element, "PCF_ELEM_TAP1", doc);
                        sbAccessories.Append(tapsWriter.tapsWriter);
                    }
                    if (string.IsNullOrEmpty(element.LookupParameter("PCF_ELEM_TAP2").AsString()) == false)
                    {
                        TapsWriter tapsWriter = new TapsWriter(element, "PCF_ELEM_TAP2", doc);
                        sbAccessories.Append(tapsWriter.tapsWriter);
                    }
                    if (string.IsNullOrEmpty(element.LookupParameter("PCF_ELEM_TAP3").AsString()) == false)
                    {
                        TapsWriter tapsWriter = new TapsWriter(element, "PCF_ELEM_TAP3", doc);
                        sbAccessories.Append(tapsWriter.tapsWriter);
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("Element " + element.Id.IntegerValue.ToString() + " caused an exception: " + e.Message);
            }

            //// Clear the output file
            //System.IO.File.WriteAllBytes(InputVars.OutputDirectoryFilePath + "Accessories.pcf", new byte[0]);

            //// Write to output file
            //using (StreamWriter w = File.AppendText(InputVars.OutputDirectoryFilePath + "Accessories.pcf"))
            //{
            //    w.Write(sbAccessories);
            //    w.Close();
            //}
            return(sbAccessories);
        }
        public StringBuilder Export(string pipeLineAbbreviation, HashSet <Element> elements, Document document)
        {
            Document doc   = document;
            string   key   = pipeLineAbbreviation;
            plst     pList = new plst();
            //paramList = new plst();
            //The list of fittings, sorted by TYPE then SKEY
            IList <Element> accessoriesList = elements.
                                              OrderBy(e => e.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString()).
                                              ThenBy(e => e.get_Parameter(pList.PCF_ELEM_SKEY.Guid).AsString()).ToList();

            StringBuilder sbAccessories = new StringBuilder();

            //This is a workaround to try to determine what element caused an exception
            Element element = null;

            try
            {
                foreach (Element Element in accessoriesList)
                {
                    //This is a workaround to try to determine what element caused an exception
                    element = Element;
                    //If the Element Type field is empty -> ignore the component
                    if (string.IsNullOrEmpty(element.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString()))
                    {
                        continue;
                    }

                    sbAccessories.AppendLine(element.get_Parameter(new plst().PCF_ELEM_TYPE.Guid).AsString());
                    sbAccessories.AppendLine("    COMPONENT-IDENTIFIER " + element.get_Parameter(new plst().PCF_ELEM_COMPID.Guid).AsInteger());

                    //Write Plant3DIso entries if turned on
                    if (InputVars.ExportToPlant3DIso)
                    {
                        sbAccessories.Append(Composer.Plant3DIsoWriter(element, doc));
                    }

                    //Cast the elements gathered by the collector to FamilyInstances
                    FamilyInstance familyInstance = (FamilyInstance)element;
                    Options        options        = new Options();
                    //MEPModel of the elements is accessed
                    MEPModel mepmodel = familyInstance.MEPModel;
                    //Get connector set for the element
                    ConnectorSet connectorSet = mepmodel.ConnectorManager.Connectors;

                    //Switch to different element type configurations
                    switch (element.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString())
                    {
                    case ("FILTER"):
                        //Process endpoints of the component
                        Connector primaryConnector = null; Connector secondaryConnector = null;

                        foreach (Connector connector in connectorSet)
                        {
                            if (connector.GetMEPConnectorInfo().IsPrimary)
                            {
                                primaryConnector = connector;
                            }
                            if (connector.GetMEPConnectorInfo().IsSecondary)
                            {
                                secondaryConnector = connector;
                            }
                        }

                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, primaryConnector));
                        sbAccessories.Append(EndWriter.WriteEP2(element, secondaryConnector));

                        break;

                    case ("INSTRUMENT"):
                        //Process endpoints of the component
                        primaryConnector = null; secondaryConnector = null;

                        foreach (Connector connector in connectorSet)
                        {
                            if (connector.GetMEPConnectorInfo().IsPrimary)
                            {
                                primaryConnector = connector;
                            }
                            if (connector.GetMEPConnectorInfo().IsSecondary)
                            {
                                secondaryConnector = connector;
                            }
                        }

                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, primaryConnector));
                        sbAccessories.Append(EndWriter.WriteEP2(element, secondaryConnector));
                        sbAccessories.Append(EndWriter.WriteCP(familyInstance));

                        break;

                    case ("VALVE"):
                        goto case ("INSTRUMENT");

                    case ("VALVE-ANGLE"):
                        //Process endpoints of the component
                        primaryConnector = null; secondaryConnector = null;

                        foreach (Connector connector in connectorSet)
                        {
                            if (connector.GetMEPConnectorInfo().IsPrimary)
                            {
                                primaryConnector = connector;
                            }
                            if (connector.GetMEPConnectorInfo().IsSecondary)
                            {
                                secondaryConnector = connector;
                            }
                        }

                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, primaryConnector));
                        sbAccessories.Append(EndWriter.WriteEP2(element, secondaryConnector));

                        //The centre point is obtained by creating an unbound line from primary connector and projecting the secondary point on the line.
                        XYZ  reverseConnectorVector = -primaryConnector.CoordinateSystem.BasisZ;
                        Line primaryLine            = Line.CreateUnbound(primaryConnector.Origin, reverseConnectorVector);
                        XYZ  centrePoint            = primaryLine.Project(secondaryConnector.Origin).XYZPoint;

                        sbAccessories.Append(EndWriter.WriteCP(centrePoint));

                        break;

                    case ("INSTRUMENT-DIAL"):
                        ////Process endpoints of the component
                        //primaryConnector = null;

                        //foreach (Connector connector in connectorSet) primaryConnector = connector;

                        ////Process endpoints of the component
                        //sbAccessories.Append(EndWriter.WriteEP1(element, primaryConnector));

                        ////The co-ords point is obtained by creating an unbound line from primary connector and taking an arbitrary point a long the line.
                        //reverseConnectorVector = -primaryConnector.CoordinateSystem.BasisZ.Multiply(0.656167979);
                        //XYZ coOrdsPoint = primaryConnector.Origin;
                        //Transform pointTranslation;
                        //pointTranslation = Transform.CreateTranslation(reverseConnectorVector);
                        //coOrdsPoint = pointTranslation.OfPoint(coOrdsPoint);

                        primaryConnector = null;
                        foreach (Connector connector in connectorSet)
                        {
                            primaryConnector = connector;
                        }
                        //Connector information extraction

                        sbAccessories.Append(EndWriter.WriteEP1(element, primaryConnector));

                        XYZ primConOrigin = primaryConnector.Origin;

                        //Analyses the geometry to obtain a point opposite the main connector.
                        //Extraction of the direction of the connector and reversing it
                        reverseConnectorVector = -primaryConnector.CoordinateSystem.BasisZ;
                        Line detectorLine = Line.CreateUnbound(primConOrigin, reverseConnectorVector);
                        //Begin geometry analysis
                        GeometryElement geometryElement = familyInstance.get_Geometry(options);

                        //Prepare resulting point
                        XYZ endPointAnalyzed = null;

                        foreach (GeometryObject geometry in geometryElement)
                        {
                            GeometryInstance instance = geometry as GeometryInstance;
                            if (null == instance)
                            {
                                continue;
                            }
                            foreach (GeometryObject instObj in instance.GetInstanceGeometry())
                            {
                                Solid solid = instObj as Solid;
                                if (null == solid || 0 == solid.Faces.Size || 0 == solid.Edges.Size)
                                {
                                    continue;
                                }
                                foreach (Face face in solid.Faces)
                                {
                                    IntersectionResultArray results = null;
                                    XYZ intersection           = null;
                                    SetComparisonResult result = face.Intersect(detectorLine, out results);
                                    if (result != SetComparisonResult.Overlap)
                                    {
                                        continue;
                                    }
                                    intersection = results.get_Item(0).XYZPoint;
                                    if (intersection.IsAlmostEqualTo(primConOrigin) == false)
                                    {
                                        endPointAnalyzed = intersection;
                                    }
                                }
                            }
                        }

                        sbAccessories.Append(EndWriter.WriteCO(endPointAnalyzed));

                        break;

                    case "SUPPORT":
                        primaryConnector = (from Connector c in connectorSet where c.GetMEPConnectorInfo().IsPrimary select c).FirstOrDefault();
                        sbAccessories.Append(EndWriter.WriteCO(familyInstance, primaryConnector));
                        break;

                    case "INSTRUMENT-3WAY":
                        //Sort connectors to primary, secondary and none
                        primaryConnector = null; secondaryConnector = null; Connector tertiaryConnector = null;

                        foreach (Connector connector in connectorSet)
                        {
                            if (connector.GetMEPConnectorInfo().IsPrimary)
                            {
                                primaryConnector = connector;
                            }
                            if (connector.GetMEPConnectorInfo().IsSecondary)
                            {
                                secondaryConnector = connector;
                            }
                            if ((connector.GetMEPConnectorInfo().IsPrimary == false) && (connector.GetMEPConnectorInfo().IsSecondary == false))
                            {
                                tertiaryConnector = connector;
                            }
                        }

                        //Process endpoints of the component
                        sbAccessories.Append(EndWriter.WriteEP1(element, primaryConnector));
                        sbAccessories.Append(EndWriter.WriteEP2(element, secondaryConnector));
                        sbAccessories.Append(EndWriter.WriteEP3(element, tertiaryConnector));
                        sbAccessories.Append(EndWriter.WriteCP(familyInstance));
                        break;
                    }

                    Composer elemParameterComposer = new Composer();
                    sbAccessories.Append(elemParameterComposer.ElemParameterWriter(element));

                    #region CII export
                    if (InputVars.ExportToCII && !string.Equals(element.get_Parameter(pList.PCF_ELEM_TYPE.Guid).AsString(), "SUPPORT"))
                    {
                        sbAccessories.Append(Composer.CIIWriter(doc, key));
                    }
                    #endregion

                    sbAccessories.Append("    UNIQUE-COMPONENT-IDENTIFIER ");
                    sbAccessories.Append(element.UniqueId);
                    sbAccessories.AppendLine();

                    //Process tap entries of the element if any
                    //Diameter Limit nullifies the tapsWriter output if the tap diameter is less than the limit so it doesn't get exported
                    if (string.IsNullOrEmpty(element.LookupParameter("PCF_ELEM_TAP1").AsString()) == false)
                    {
                        TapsWriter tapsWriter = new TapsWriter(element, "PCF_ELEM_TAP1", doc);
                        sbAccessories.Append(tapsWriter.tapsWriter);
                    }
                    if (string.IsNullOrEmpty(element.LookupParameter("PCF_ELEM_TAP2").AsString()) == false)
                    {
                        TapsWriter tapsWriter = new TapsWriter(element, "PCF_ELEM_TAP2", doc);
                        sbAccessories.Append(tapsWriter.tapsWriter);
                    }
                    if (string.IsNullOrEmpty(element.LookupParameter("PCF_ELEM_TAP3").AsString()) == false)
                    {
                        TapsWriter tapsWriter = new TapsWriter(element, "PCF_ELEM_TAP3", doc);
                        sbAccessories.Append(tapsWriter.tapsWriter);
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("Element " + element.Id.IntegerValue.ToString() + " caused an exception: " + e.Message);
            }

            //// Clear the output file
            //System.IO.File.WriteAllBytes(InputVars.OutputDirectoryFilePath + "Accessories.pcf", new byte[0]);

            //// Write to output file
            //using (StreamWriter w = File.AppendText(InputVars.OutputDirectoryFilePath + "Accessories.pcf"))
            //{
            //    w.Write(sbAccessories);
            //    w.Close();
            //}
            return(sbAccessories);
        }
Пример #3
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);
        }