How to Re-attach a Detached Connection to the Side of a FabricationPart?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I need to use my program to re-attach a connection that my program has just detached. I would like to know how to do this using Revit API.
I am having a problem exporting duct from Revit 2024 to a job file (MAJ file) if it has a tap attached to the side of the duct body. The problem is currently being investigated in Autodesk development team. But I cannot wait for them to figure this out and send out a patch. I need to find a workaround soon. One possible workaround is to detach the tap (such as Boot Tap or STO/STOD) before exporting it, and then re-attach the tap after exporting it.
This is easy to detach the tap from the duct body. But I don't know how to re-attach the tap back to the duct body.
I thought I could save the original connection, and simply used Connector.ConnectTo() command to re-attach the original connection. But this doesn't work. The reason is that the original connection is already destroyed, and the original connection that I have saved is not valid anymore.
The following is a sample program to select pieces, detaching any connection in the side of the duct body, exporting the pieces, and attempting (and failing) to re-attach the original connections:
[Transaction(TransactionMode.Manual)]
public class CCmdTestExport : IExternalCommand
{
public Result Execute( ExternalCommandData commandData, ref String refMsg, ElementSet elements )
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = commandData.Application.ActiveUIDocument.Document;
// Let the user to select duct.
List<ElementId> lisSelectElementIds = (List<ElementId>)uidoc.Selection.GetElementIds();
try
{
String sInstruction = "Select duct and hit 'Finish' button in Options Bar";
ISelectionFilter oSelectOnlyDuct = new CDuctSelectionFilter();
List<Reference> lisSelectReferences =
lisSelectElementIds.Select( x => new Reference( doc.GetElement( x ) ) ).ToList();
lisSelectReferences = (List<Reference>)uidoc.Selection.PickObjects( ObjectType.Element, oSelectOnlyDuct,
sInstruction, lisSelectReferences );
lisSelectElementIds = lisSelectReferences.Select( x => x.ElementId ).ToList();
// HANDLE_PHANTOM_SELECTION_AFTER_PICKOBJECT
List<ElementId> lisEmpty = new List<ElementId>();
uidoc.Selection.SetElementIds( lisEmpty );
}
catch( Autodesk.Revit.Exceptions.OperationCanceledException )
{
return Result.Cancelled;
}
// Convert the selected elements into a list of FabricationPart.
List<FabricationPart> lisFabPcMayHaveNull = lisSelectElementIds.Select( x => doc.GetElement(x) as FabricationPart ).ToList();
List<FabricationPart> lisFabPc = lisFabPcMayHaveNull.Where( x => x != null ).ToList();
if ( lisFabPc.Count == 0 )
return Result.Cancelled;
// Detach connection from the side of the duct body, export the duct, and then attempt to re-attach the
// detached connections.
using( Transaction trans = new Transaction( doc ) )
{
if ( trans.Start( "Test" ) == TransactionStatus.Started )
{
try
{
// Disconnect anything attached to the side of the duct bodies in the selected pieces.
List<Connector> lisDetachedDuctBodySideConnector = new List<Connector>();
List<Connector> lisDetachedOutsidePcConnector = new List<Connector>();
foreach( FabricationPart curPc in lisFabPc )
{
while( true )
{
Connector cnSide = cnGetFirstNonPrimaryOrSecondaryConnector( curPc.ConnectorManager );
// GetFirstNonPrimaryOrSecondaryConnector() is from sample
// program FabricationPartLayout in Revit SDK.
if ( cnSide == null )
break;
// Here, cnSide != null
Connector cnOriginalConnectToSideCn = cnDisconnectSideConnection( cnSide );
lisDetachedDuctBodySideConnector.Add( cnSide );
lisDetachedOutsidePcConnector .Add( cnOriginalConnectToSideCn );
} //while
} //foreach
// Export the selected pieces to a job file.
var lisSelectedPcElementId = new HashSet<ElementId>();
foreach( FabricationPart curPc in lisFabPc )
{
lisSelectedPcElementId.Add( curPc.Id );
}
Debug.Assert( lisSelectedPcElementId.Count > 0 );
String sFullPathJobFileName = "C:\\Temp\\TestExport.maj";
ISet<ElementId> lisExportedFabPcElementId =
FabricationPart.SaveAsFabricationJob( doc, lisSelectedPcElementId, sFullPathJobFileName,
new FabricationSaveJobOptions(true) );
if ( lisExportedFabPcElementId.Count == 0 )
{
// Does this situation ever happen?
}
// Re-attach the pieces that were detached.
if ( lisDetachedDuctBodySideConnector.Count > 0 )
{
////////////////////////
// Doesn't work because the saved connections have already been destroyed.
////////////////////////
for( int i = 0; i < lisDetachedDuctBodySideConnector.Count; i++ )
{
Connector cnDuctBodySideConnector = lisDetachedDuctBodySideConnector[ i ];
Connector cnOutsidePcConnector = lisDetachedOutsidePcConnector [ i ];
cnDuctBodySideConnector.ConnectTo( cnOutsidePcConnector );
}
////////////////////////
}
// Done with all the selected pieces. Need to commit the change.
if ( trans.Commit() != TransactionStatus.Committed )
return Result.Failed;
// OK - Fall through to exit.
} //try
catch( InvalidOperationException ex )
{
trans.RollBack();
return Result.Failed;
}
} //if
} //using
return Result.Succeeded;
}
static Connector cnGetFirstNonPrimaryOrSecondaryConnector( ConnectorManager cm )
// Get the first connector that is neither the primary connector nor the secondary connector.
{
foreach( Connector cn in cm.Connectors )
{
MEPConnectorInfo info = cn.GetMEPConnectorInfo();
if ( ! info.IsPrimary &&
! info.IsSecondary )
return cn;
}
return null;
}
static Connector cnDisconnectSideConnection( Connector cnDuctBodySide )
// Disconnect the connection in the specified connector.
{
if ( cnDuctBodySide == null )
return null;
Connector cnOriginalConnectTo = null;
// This code is copied from the sample in Connector definition in Revit API.
ConnectorSet connectorSetOfCnn = cnDuctBodySide.AllRefs;
ConnectorSetIterator csi = connectorSetOfCnn.ForwardIterator();
while( csi.MoveNext() )
// This connection is in the outside piece (may be a boot-tap or a STO/STOD) that is
// connected to the side of the duct body.
{
Connector cnConnected = csi.Current as Connector;
if ( cnConnected != null )
{
// Look for physical connection and disconnect it.
if ( cnConnected.ConnectorType == ConnectorType.End ||
cnConnected.ConnectorType == ConnectorType.Curve ||
cnConnected.ConnectorType == ConnectorType.Physical )
{
cnOriginalConnectTo = cnConnected;
cnDuctBodySide.DisconnectFrom( cnOriginalConnectTo );
break;
}
} //if
} //while
return( cnOriginalConnectTo );
}
} // class CCmdTestExport
public class CDuctSelectionFilter : ISelectionFilter
// We use this class to create a Selection-Filter that only selects duct.
{
public bool AllowElement( Element element )
{
return( (BuiltInCategory)element.Category.Id.Value == BuiltInCategory.OST_FabricationDuctwork );
// Note: ElementId.IntegerValue doesn't exist in Revit 2024.
// We have to use ElementId.Value.
}
public bool AllowReference( Reference refer, XYZ point )
{
return false;
}
} // class CDuctSelectionFilter
Please let me know if there is a way to re-attach the detached connections using Revit API. Thanks in advance!
By the way, for people who cannot wait to have this problem fix. They can use the "Exporter" in VM (a third party add-in). Somehow the Exporter in VM doesn't have the problem, and they don't need to use the trick of detach and re-attach to work around this problem.
JC_BL