Replace a Family instance with another Family Based on Family Instance Parameter Value

Replace a Family instance with another Family Based on Family Instance Parameter Value

fawzimasri7137
Advocate Advocate
6,921 Views
19 Replies
Message 1 of 20

Replace a Family instance with another Family Based on Family Instance Parameter Value

fawzimasri7137
Advocate
Advocate

Hi,

I am trying to select multiple family instances based on a their name and a certain value of one of their Family parameters, then i want to replace these families Instances with another family.

This can be done by the UI, but the parameter value requirement makes it manual intensive.

 

In the code below- based on responses in this forum and others - the code executes without any exceptions.  But the model does not reflect any changes. the old family is NOT replaced.

 

I am not sure what i am missing.  I have tried the symbol swap method and the ChangeTypeId method, with and without symbol activation, which was suggested in one of the posts, and i don't know what it means.

 

can someone point me in the correct direction?

 

public void Execute(UIApplication a)
        {
// i removed code for selecting familyinstances and loaded families
            #region activate a  replacement Symbol of the replacement Family 
            rplcFamSymbol = actvDoc.GetElement(rplcFam.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
            rplcFamSymbol.Activate();
            #endregion
            #region Loop Thru all family instances to be changed based on their "Angle 1" parameter and replace when found.
            foreach (Autodesk.Revit.DB.FamilyInstance famInst in allFamInst)
            {
                try
                {
                    using (Transaction rplcFamInst_trx = new Transaction(actvDoc, "Replace Family Instance"))
                    {
                        rplcFamInst_trx.Start();
                        // Check if current Family instance has the desired family name
                        if (selectString == famInst.Symbol.FamilyName)
                        {
                            // Check if current Family Instance has the desired parameter "Angle 1" with the desired value 45.
                            foreach (Parameter param in famInst.Parameters)
                            {
                                if (param_name == param.Definition.Name)
                                    if (param.AsValueString().Contains("45"))
                                    {
///////  This is where the Replacement Should Happen... ////////
                                        Element e = actvDoc.GetElement(famInst.Id);
                                        //e.ChangeTypeId(rplcFamSymbol.Id);
                                        famInst.Symbol = rplcFamSymbol;
                                        //famInst.Symbol.Activate();
                                    }
                            }
                        }

                        rplcFamInst_trx.Commit();
                    }//end transaction rplcFamInst_trx

                }//end try
                catch (System.Exception e)
                {
                    Autodesk.Revit.UI.TaskDialog.Show("rplcFamilyInstances_CN", "I am in rplcFamilies_CN.execte. Exception:  " + e.ToString());

                }// end catch
            }  // Foreach Family to edit
            #endregion

        }// end of Execute

 

0 Likes
6,922 Views
19 Replies
Replies (19)
Message 2 of 20

RPTHOMAS108
Mentor
Mentor

You need to step through to see what isn't being done but I see a couple of things:

  1. Rather than iterating all parameters use Element.LookupParameter or better still Element.get_Parameter(GUID) for shared parameter or Element.get_Parameter(BuiltInParameter) for built-in parameters.
  2. AsValueString will for value types return the parameter as displayed in the UI (does it have a degree symbol, how many decimal places set)? Better to use AsDouble and compare the angle between tolerance limits (will be in radians for AsDouble).

 

0 Likes
Message 3 of 20

fawzimasri7137
Advocate
Advocate

@RPTHOMAS108Thanks for the your pointers I am sure they will save me a lot of time.

Regarding the Stepping through, I removed all my debug code in my submittal here to keep it readable.  But i have been doing that , and as i said, everything works like i want it to, or as expected.  So i think i may be missing a bit of information.  do i need to add anything to this sequence:

 

1- Select your family Instance (famInst)

2- Find the unloaded Family and choose a symbol from its symbols' list (rplcFamSymbol)

3- Activate the unloaded family symbol: rplcFamSymbol.Activate(); ------ Is that necessary

4-a- Replace using assign symbols: famInst.Symbol = rplcFamSymbol;

4-8- OR replace using ChangeTypeId: Element e = actvDoc.GetElement(famInst.Id);  e.ChangeTypeId(rplcFamSymbol.Id);

 

Is that correct?  am i missing any other steps?

 

I am worried that i may be missing something since the family i am interested in replacing with is loaded but not instantiated.... so are steps 2 and 3 enough?

0 Likes
Message 4 of 20

RPTHOMAS108
Mentor
Mentor

Sequence seems fine, I would usually favour ChangeTypeId only because it is the more generic function.

 

Are you reaching the part of the code where ChangeTypeId is executed? Strange that you would not get some kind of exception. Implement suggested change to get actual parameter with LookupParameter then step through to ensure that line above is executed.

 

Also be careful a string of "45" is not equal to "90.45°" (your comparison using 'contains' is error-prone).

0 Likes
Message 5 of 20

fawzimasri7137
Advocate
Advocate

I am sorry i was late on replay, but i discovered a bug in my debugging.  i have been making modification to my code in visual studio, build and then run the macro in Revit repeatedly without ever restarting revit.  by fluke, i restarted revit and noticed that i am getting different results...see image showing my two attempts of replacing the fitting before i restarted Revit and after i restarted it.

 

I am not sure how to fix that, but i guess i will have to tackle it in another post.

 

Regarding the Replacement, My code is supposed to go thru my Document and find all plumbing Fitting instances of Family "Tee - Weld" . It checks the angle on that Tee, and if it is 45degrees, it will replace its symbol with that of Family "Wye - Weld".... (i will adjust the comparison per your suggestion later..thanks)

My code IS working. but the replacement fitting is placed in reverse, so no connection is made... i was just not able to see it for the longest time per above error in my debug.


So:

1- Do plumbing pipe fittings require a special attention?

2- Why does Revit not complain about the break in connection? if this happens in UI , Revit would complain and not allow it, or ask to break connection.

0 Likes
Message 6 of 20

RPTHOMAS108
Mentor
Mentor

I believe the fittings are point based family instances.

 

You could try matching the transform of the original since that determines it's orientation. I don't know why changing the type would affect that unless the type comes from family with different layout direction to original?

 

Fittings and pipes also have a system of connections so you could try investigating that functionality. Not sure why instances should change form upon restarting Revit. Perhaps if they are not connected they are placed in a default arrangement? Also seems they have shrunk in size?

 

I recall being able to manually disconnect fittings previously without error level failures. I would assume you would generally get the same failures with the API as in UI. Although API methods are not as intelligent as UI functionality in terms of automatic placement of bends between pipe segments etc.

0 Likes
Message 7 of 20

fawzimasri7137
Advocate
Advocate

ok, i will research "Point Based Families" and Pipe Connectors.

 

What is "Matching the  Transform..."?

 

The replacement family, is as the same original family, only renamed, just while i am testing.

The fitting looks like it has shrunk since there is no connection to the pipes which would dictate the size of that fitting. so it will take the default size which happens to be at 1/2" while the pipe is at 6".

0 Likes
Message 8 of 20

MarryTookMyCoffe
Collaborator
Collaborator

ok, you have a lot to add to your app. Two weeks ago I was making some thing similar. here some pointers

If symbol is from different family, you will lose all parameters(I suggest assuming that parameter will be lost and just save them before and rewrite them again)

 famInst.Symbol = rplcFamSymbol;

before you change symbol use this

if (familySymbol.IsActive == false)
    familySymbol.Activate();

check if FamilyPlacementType is the same before changing it

 

Now to your questions:

1- Do plumbing pipe fittings require a special attention?

Yes, and you not gonna like it. for exemple you can't set a size of reduction, but union you can. How family work will depend on how you set a connectors in family.

 

2- Why does Revit not complain about the break in connection? if this happens in UI , Revit would complain and not allow it, or ask to break connection.

in api you can connectTo elements that are far away connectors don't have to be in the same place to be connected(believe me it is better that way). it is possible that this connectors are still connected. The UI just check if element is connected before changing it and if connection is lost you will  get warning, if the same case was in api we would lose a lots of speed just to ignore all this warnings.

some how I miss method "ChangeTypeId" when I was doing my app, so I'm curious how much they messed up this one.

It will be a lots of work but you before changing a type get a pairs of connectors and just recreate a connection after swamping(there are 3 type of connectors primery secondary and unlinked).

Good luck, you will needed.

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 9 of 20

fawzimasri7137
Advocate
Advocate

Thanks for your great response... i feel better now knowing that my efforts are not in vain, and that this is a major issue to solve.

So i will adjust the steps to the Following:

1- Select your family Instance (famInst)

2- Find the unloaded Family and choose a symbol from its symbols' list (rplcFamSymbol)

3- Activate the unloaded family symbol: rplcFamSymbol.Activate(); ------ that IS necessary

4- Copy all Paramaters from famInst.Symbol.Parameters to rplcFamSymbol.Parameters.

5-a- Replace using assign symbols: famInst.Symbol = rplcFamSymbol;

5-b- OR replace using ChangeTypeId: Element e = actvDoc.GetElement(famInst.Id); e.ChangeTypeId(rplcFamSymbol.Id);

6- Recreat the Fitting symbol with new connection ( i guess using connectorManager here)

 

Two things though:

1- Instead of depending on luck ( and the thanks for your wishes) does autodesk not have a class specifically for Revit API PIPE?  i can't find any... and our autodesk reps seems to be as knowledgeable as i am.. my last experience with them was terrible.

2- Does it make sense to not swap the family, but delete it and recreate the connection with the new one?  I am thinking of using something like this, although i have no idea how to extend it from an Elbow to a Tee:

doc.Create.NewElbowFitting(con, con1);

 

 

0 Likes
Message 10 of 20

MarryTookMyCoffe
Collaborator
Collaborator

@fawzimasri7137 wrote:

Two things though:

1- Instead of depending on luck ( and the thanks for your wishes) does autodesk not have a class specifically for Revit API PIPE?  i can't find any... and our autodesk reps seems to be as knowledgeable as i am.. my last experience with them was terrible.

2- Does it make sense to not swap the family, but delete it and recreate the connection with the new one?  I am thinking of using something like this, although i have no idea how to extend it from an Elbow to a Tee:

doc.Create.NewElbowFitting(con, con1);


1) you have the same experience about reps as I have.  Everything about pipes is in

using Autodesk.Revit.DB.Plumbing;
Autodesk.Revit.DB.Plumbing.PlumbingUtils

 I had to make my own Builder for piping, just because they put so many thing in different places. doc.Create have all connection for pipes, PlumbingUtils for pipe plcaeholders(praticly it work the same way as PIPE).

2) If you what to change elbow to a tee, you delete elbow and use

doc.Create.NewTeeFitting

remember that first two connectors is one pipe, and 3 is a branch. If you have Y tee you need to make input and changes on the your self.

for transition is

NewTransitionFitting

but have in mind that making a tree of transitions is close to be impossible(you can't change connector size), I for example if I had to use many transitions, to go from big DN to small, I use Union.
If you what to use specific fitting for connection you can ether swap type after creating or change order in PipeType RoutingPreference before creating it.

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 11 of 20

fawzimasri7137
Advocate
Advocate

Thanks @MarryTookMyCoffe for you detailed  response.

Considering how tricky this is, do you i sense that you prefer swapping  family instances over CreateNewElbow?

I got side tracked to another Revit activity, but i will get back on this next week and was thinking of trying out the CreateNewElbow path  ( of course swapping an elbow with a tee, i will put on the back burner for now, i will just focus on swapping a Tee with Wye for now)

0 Likes
Message 12 of 20

fawzimasri7137
Advocate
Advocate

Hi Guys,

 

I finally was able to get on this test it out a bit...i hope you can help with this...

So i am trying to replace a Tee with a Wye.

I am using this approach:

- Find the Tee that you want.

- Find the connectors on that Tee

- Find the their Opposing (or Paired) connectors on the pipes connected to that fitting.

- Delete the Tee instance

- Create a New Tee fitting using the above pipe connectors .. of course, i set my pipe preferences to use Wye as it preferred Tee.

- commit transaction... done.

 

I tested everything i could think of, all connectors on the fitting and on the pipes are correct.  but i get an exception that the "...angle is too small or Too large..." 

My angle on that Tee is 45 degrees, so it should pass for a Wye, plus i did check it in the UI and it does get replaced by a wye.

 

any ideas???

 

 

               try
                {
                    using (Transaction rplcFamInst_trx = new Transaction(actvDoc, "Replace Family Instance"))
                    {
                        rplcFamInst_trx.Start();

                        // Check if current Family instance has the desired family name
                        if (selectString == famInst.Symbol.FamilyName)
                        {
                            // Get Element Id since i need it later.
                               Element famInst_el = actvDoc.GetElement(famInst.Id);
                            
                            // Check if current Family Instance has the desired parameter "Angle 1" with the desired value 45.

                            if (famInst_el.LookupParameter(param_name).AsValueString().Contains("45"))
                            {
                                                                
                                // Get a List of the Connectors of the Current fitting family instance: famInst.
                                List<Connector> famInst_connectors = famInst.MEPModel.ConnectorManager.Connectors.Cast<Connector>().ToList();

                                // I know i had 3 since it is a Tee and i tested the count so i could do below:
                                Connector conn_0 = famInst_connectors[0];
                                Connector conn_1 = famInst_connectors[1];
                                Connector conn_2 = famInst_connectors[2];

                                // Get the PIPE connectors that pair with the current fitting connectors ( using connector.AllRef) 
                                // This is tested and does return the correct pipe owners of these connectors.
                                Connector conn_A = getOpposingConnector(uidoc, famInst_connectors[0]);
                                Connector conn_B = getOpposingConnector(uidoc, famInst_connectors[1]);
                                Connector conn_C = getOpposingConnector(uidoc, famInst_connectors[2]);

                                // Delete current Family instance, now that i have the Pipe connectors that i need for its replacement.
                                actvDoc.Delete(famInst_el.Id);
                                // Finally i can not create a new Wye fitting instead of a Tee Fitting. (granted based on my pipe preferences, but will deal with that later).
                                // However noe of the below attempts ran without the same exception of angle too large or too small.
                                // Doing the same thing in the I works fine- the Tee is replaced with a Wye.
                                actvDoc.Create.NewTeeFitting(conn_A, conn_B, conn_C);
                                //actvDoc.Create.NewTeeFitting(conn_A, conn_C, conn_B);
                                //actvDoc.Create.NewTeeFitting(conn_B, conn_A, conn_C);
                                //actvDoc.Create.NewTeeFitting(conn_B, conn_C, conn_A);
                                //actvDoc.Create.NewTeeFitting(conn_C, conn_A, conn_B);
                                //actvDoc.Create.NewTeeFitting(conn_C, conn_B, conn_A);
                                
                             }
                           }
                        rplcFamInst_trx.Commit();
                    }   //end transaction rplcFamInst_trx
                    
                }   //end try
                catch (System.Exception e)
                {
                    Autodesk.Revit.UI.TaskDialog.Show("rplcFamilyInstances_CN", "I am in rplcFamilies_CN.execte. Exception:  " + e.ToString());

                }// end catch

 

0 Likes
Message 13 of 20

adam.krug
Advocate
Advocate

Hi,

 

I must admit that I've never worked with wyes thus far but maybe I can help you a bit with some tips that I know from working with tees.

 

1. Revit API would only let you insert a 90 deg tee via Create.NewTee()

2. If you wish to work around that - it's better to insert a dummy branching pipe at 90 degree, insert 90 deg tee, remove the dummy pipe, modify the angle of the tee, adjust the original branching pipe and connect it with the tee

3. It's safer to use Connector.Angle property to modify the tee's angle than use the parameter associated with the angle.

 

It could be that Revit API has similar constraint with wyes so you'd need to experiment to understand it and proceed accordingly with such hack.

 

Maybe one more thing: are you sure that the wye that you're working with is properly build in family editor? E.g. connector numbers are set correctly, angle associated with correct connector(s) etc.. Is the wye family working properly with normal Revit UI? E.g. it routes without any problem, can be swapped with different tees etc.

0 Likes
Message 14 of 20

fawzimasri7137
Advocate
Advocate

Thanks @adam.krug ...

I am trying to follow the steps you outlined above.  i am having trouble in finding the proper API function to connect an existing pipe to an existing fitting.

here is my take on it:

1- dummy branching pipe at 90 degree --->  (pipe.create )

2- insert 90 deg tee  ---> (create.newTeeFitting)

3- remove the dummy pipe ---> (pipeID.delete)

4- modify the angle of the tee ---> connector.Angle

5- adjust the original branching pipe ---> Pipe exists already.. so no need to adjust, just obtain connector, correct?

6- connect it with the tee ---> What API function  do i use here, since both pipe and fitting exist, so i cannot use newTeeFitting or pipe.create?

 

 

0 Likes
Message 15 of 20

adam.krug
Advocate
Advocate
To connect pipe with fitting (same with fitting - fitting or pipe - pipe) you need to use Connector.ConnectTo(Connector) . So upfront you need to find which connectors to connect with each other from the fitting and pipe.

I also noticed that sometimes Revit will throw an exception during that process despite the tee is placed and connected, but it will look strange - the pipe won't be trimmed accordingly. Then you'd need to disconnect the branching pipe and reconnect again.

Cheers,
Adam
0 Likes
Message 16 of 20

MarryTookMyCoffe
Collaborator
Collaborator

I see that you did a lot already. I was busy with work so I couldn't  observe forum.
When I was doing my import to revit items from another program, I observe that Inserting tee or elbow was to unpredictable. I end up making my own methods to input fitting(thx to that I can make from 3 to 360 elbows 😄 ).
I notice that they change something in revit 2021(I was doing most of code in Revit 2017), so it may work little different in new version.

If you what to make your own method, you need to:
A)know angles between connectors,
B)calculate a axis of rotation(learning it again and again just for revit is my nightmare )

C)calculate angle based on axis

D)when you create it on chosen level family will be toward axis X, but in side family primery connector can face different directions. from where the primery and unlinked is facing you will have to change calculated angle (usually some one would drat tee up or down)
E) rotate it
F) change angle parameters in family(im not sure but i thing angle can be change in connectors)

G) connect this nightmare(and this is a time when i would use sometimes NewTransitionFitting)
last thing sometimes when you connect or change size of connector revit will change it back, because we clearly don't know better in their mind.

Wye can have unlink connector face in different way then tee so it may be possible you will be force to cut pipe and make elbow. I don't thing they fix it but when connectors of pipes are in the same position, method to make a elbow or tee will not work.

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 17 of 20

fawzimasri7137
Advocate
Advocate

HI @MarryTookMyCoffe , @adam.krug ,  Thanks for your responses... i am going thru your suggestions... it seems there is a lot to learn to do this simple not-so-simple-thing... 

0 Likes
Message 18 of 20

MarryTookMyCoffe
Collaborator
Collaborator

remember, rememberer, nothing in revit is simple and don't use isAlmostEqualTo

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 19 of 20

shehab_fekry
Enthusiast
Enthusiast

Hello, I'm trying your solution, I've managed to create a tee fitting and after deleting the perpendicular pipe and changing the angle of the 3rd connector to 45 degree .. when I try to make a 3rd pipe with a 45 deg rotation .. the tee fitting is changed from its place , do you have a solution for that? 

here's the code sample 

   te.Start();
                                    var teeFitting = CreateTeeFitting(doc, new PipeConnection(createdDummy, movedFirstOnPipe), createdPipes);
                                    doc.Delete(createdDummy.Id);
                                    var unusedConn = teeFitting.MEPModel.ConnectorManager.UnusedConnectors.Cast<Connector>().FirstOrDefault();
                                    unusedConn.Angle = Math.PI / 4;
                                    var createdFirstPipe = Pipe.Create( doc, pipe.GetTypeId(), MainPipe.LevelId, unusedConn, finalTestPt);
                                    te.Commit();
0 Likes
Message 20 of 20

MarryTookMyCoffe
Collaborator
Collaborator

try this -> regenerate document to update connectors data, see if it change anything
if not try to create pipe not base on connectors but base on XYZ to see how this pipe is made

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes