Yeah, just spent a while dealing with this issue. I spawn a plumbing fixture on a pipe face using that method: doc.Create.NewFamilyInstance(pipeface,pipefacecenter,XYZ(0,0,0),fixturefamilysymbol)
the fixturefamilysymbol is a simple cylinder with a connector set to "global".
The family has a "Nominal Diameter" family parameter set to instance and the connector diameter is referenced to it.
Then I try to connect the NewFamilyInstance's connector to the reference pipe connector using connecfrom = list(newfamilyinstance.MEPModel.ConnectorManager.UnusedConnectors)[0].ConnectTo( list(referencepipe.ConnectorManager.UnusedConnectors)[0])
(I did all the necessary filtering before to keep elements with only one UnusedConnector)
Sometime it does work. Sometimes not. Here is an example:
Here is my code (please forgive my brutal style and overkill boilerplate):
import clr clr.AddReference('ProtoGeometry') from Autodesk.DesignScript.Geometry import *
# Import RevitAPI clr.AddReference("RevitAPI") import Autodesk from Autodesk.Revit.DB import * from System.Collections.Generic import * from Autodesk.Revit.Attributes import*
import clr clr.AddReference("RevitAPIUI") from Autodesk.Revit.UI import *
# Import DocumentManager and TransactionManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument # The inputs to this node will be stored as a list in the IN variables.
defgetParamValue(element,param):
try: DB = element.GetOrderedParameters() for i in DB: if i.Definition.Name == param: if i.StorageType == 2: value = (i.AsDouble()) elif i.StorageType == 1: value = (i.AsInteger()) elif i.StorageType == 3: value = (i.AsString()) else: value = (i.AsElementId()) except: value = ("Aucune valeur trouvée") return value
defgetParamObj(element,param): valueout = 0 try: DB = element.GetOrderedParameters() for i in DB: if i.Definition.Name == param: valueout = i
except: pass return valueout
defgetpipefaceformagicconnector(wpipe): pipe = UnwrapElement(wpipe) cons = list(pipe.ConnectorManager.UnusedConnectors) iflen(cons) >0: geomenum = pipe.get_Geometry(opt) pipecurve = pipe.Location.Curve solids = [] pipefaces = [] for geobj in geomenum: solids.append(geobj) for solid in solids: flist = solid.Faces for fc in flist: if"PlanarFace"in fc.GetType().ToString(): pipefaces.append(fc)
for con in cons: for pipeface in pipefaces: if pipecurve.GetEndPoint(0).DistanceTo(con.Origin)==0and pipeface.Origin.DistanceTo(con.Origin) - getParamValue(pipe,"Outside Diameter")/2 < 0.01: return pipeface elif pipecurve.GetEndPoint(1).DistanceTo(con.Origin)==0and pipeface.Origin.DistanceTo(con.Origin)- getParamValue(pipe,"Outside Diameter")/2 < 0.01: return pipeface
return ["Error",UnwrapElement(wpipe)]
defgetpipefacecenterformagicconnector(wpipe): pipe = UnwrapElement(wpipe) cons = list(pipe.ConnectorManager.UnusedConnectors) iflen(cons) >0: geomenum = pipe.get_Geometry(opt) pipecurve = pipe.Location.Curve solids = [] pipefaces = [] for geobj in geomenum: solids.append(geobj) for solid in solids: flist = solid.Faces for fc in flist: if"PlanarFace"in fc.GetType().ToString(): pipefaces.append(fc)
for con in cons: for pipeface in pipefaces: if pipecurve.GetEndPoint(0).DistanceTo(con.Origin)==0and pipeface.Origin.DistanceTo(con.Origin) - getParamValue(pipe,"Outside Diameter")/2 < 0.01: return pipecurve.GetEndPoint(0) elif pipecurve.GetEndPoint(1).DistanceTo(con.Origin)==0and pipeface.Origin.DistanceTo(con.Origin)- getParamValue(pipe,"Outside Diameter")/2 < 0.01: return pipecurve.GetEndPoint(1)
defgetpipeconnectorformagicconnector(wpipe): pipe = UnwrapElement(wpipe) cons = list(pipe.ConnectorManager.UnusedConnectors) iflen(cons) >0: geomenum = pipe.get_Geometry(opt) pipecurve = pipe.Location.Curve solids = [] pipefaces = [] for geobj in geomenum: solids.append(geobj) for solid in solids: flist = solid.Faces for fc in flist: if"PlanarFace"in fc.GetType().ToString(): pipefaces.append(fc)
for con in cons: for pipeface in pipefaces: if pipecurve.GetEndPoint(0).DistanceTo(con.Origin)==0and pipeface.Origin.DistanceTo(con.Origin) - getParamValue(pipe,"Outside Diameter")/2 < 0.01: return con elif pipecurve.GetEndPoint(1).DistanceTo(con.Origin)==0and pipeface.Origin.DistanceTo(con.Origin)- getParamValue(pipe,"Outside Diameter")/2 < 0.01: return con
Many thanks for your message and sharing videos and code.
This Idea is about Revit API functionality which is not same to the Revit UI. "ConnectTo" Method works fine to connect elements only when the connection points are at exact same position between the elements and when no other modification in geometry size or other parameter is necessary for the connection to work.
Question:
Could you please confirm if in your first video that the pipe and fitting element are at the correct location having their connection points at exact same position?
To verify that the "connectTo" method works:
1. Connect the vertical pipe to the fitting using the Revit UI.
2. Disconnect the Pipe from the Fitting using the Revit UI.
(Now that your elements are in the position having connection points at exact same location and elements do not need adjustment in their geometry and size and other parameters)
3. Try to use the "connectTo" method to connect pipe to fitting.
verifying pipefacecenterXYZ.DistanceTo(pipeconnectorXYZ) I get 0. So I know that the pipe connector is really at the face center.
But then I verify my plumbing fixture connector and the distance to the pipe connector, I get something weird for the pipe I showed in my forst video: (list(MagicConnector.MEPModel.ConnectorManager.UnusedConnectors)[0].Origin).DistanceTo(pipeconnectorXYZ)/304.8 = 2.0588462975328
So I tried this test with another pipe where the script actually works without an error (as in my second video) and I got that:
But everything still works. So I will assume that this distance isn't the source of the problem.
Also checked the family to make sure that the connector was created on the plumbing fixture's origin:
My last assumption is that the problem would be caused by the vertical orientation of the pipes. I only get an error for pipes that have disconnected ends in the vertical position.
Just to be precise with the question you asked, yes I did try to:
1. Connect the vertical pipe to the fitting using the Revit UI.
2. Disconnect the Pipe from the Fitting using the Revit UI.
3. Try to use the "connectTo" method to connect pipe to fitting.
It does work. Only problem is that it implies that I have to connect my plumbing fixture manually using the UI, which is exactly what I am trying to automate.
I will try to make other experiments with NewFamilyInstance methods using different arguments.
Thank you for looking at this problem. To help any person investigating into this, here is what I mentionned to you on another page:
I tinkered something that works even though I dont't actually know why it does work. I noticed that the FamilyInstance that I spawn always had doc.GetElement(MagicConnector.Id).Location.Point = XYZ(0,0,0) All the time, even if I clearly see the object on the face of the pipe in the model. So i just added a line of code before the "ConnectTo" and a 1mm "Offset from Host" to the familyinstance: MagicConnOffsetfromHost = getParamObj(MagicConnector,"Offset from Host") MagicConnOffsetfromHost.Set(1/304.8) doc.GetElement(MagicConnector.Id).Location.Move(XYZ(0,0,0)) connecfrom = list(MagicConnector.MEPModel.ConnectorManager.UnusedConnectors) connecfrom[0].ConnectTo(Connecto)
TaDam! Now it works with vertical pipes.
I can send you the complete python code if needed.
Many thanks to all for your valuable input and patience with this.
The development team replied:
The ConnectTo API was the barebone connection only. It does not have the rich capability of the UI. It has been a known issue for a long time. We will take this as an API feature request to be prioritized.
Checking the forum discussion, it seems the customer found some workaround to move forward; not sure how that impacts the priority. The underlying issue is real, valid and requires a fair amount of effort. Ideally, we like the UI and API user calling into the same method. Unfortunately, that is not the case of ConnectTo at this point.
We may explore the possibility of putting a wrapper and validation around the internal method RbsEditorUtils::RbsConnectionElementPickEditorSolution to expose that to the API. We logged a development ticket REVIT-205160 for this under MEP Mechanical Routing Solutions.