Sei sulla pagina 1di 13

The Building Coder

May 17, 2010

Cable Tray Orientation and Fittings


In the overview of the Revit MEP 2011 API, I mentioned that new elements have been introduced to represent cable trays and conduits.
We already had a look at the creation of new conduit elements in the pipe to conduit converter.
The creation of cable tray elements is equally simple, making use of the static Create method on the CableTray class.
This is another example of the new element creation paradigm instead of the creation document classAutodesk.Revit.Creation.Document, a second generation API automatically generated with RIDL, the Revit Interface Definition Language.
In order to automatically create a whole cable tray run, however, we need both the straight segment cable tray elements and also the fittings to connect them with each other, elbows to turn corners and branching elements to represent junctions.
This is harder to implement for cable trays than for conduits, because the former have a rectangular cross section. To connect a horizontal cable tray segment with a vertical one requires the latter to be precisely oriented so that the elbow will line up
properly with both. This is simpler for conduits, which have a round cross section.
This led to the following questions and exhaustive exploration of cable tray fittings and orientation:
Question: We are trying to create cable tray objects through API. When we create them manually, the joints (cable tray fittings) are automatically created. How can we achieve this programmatically?
Answer: The situation for cable trays is the same as for duct and pipe elements, i.e. it is up to the application to explicitly create the fittings in between the segments as it sees fit.
There is no way to auto-generate the fittings. You can however use the fitting APIs to create cable tray fittings yourself. For example, NewElbowFitting, NewTeeFitting, NewTransitionFitting, NewCrossFitting, should all produce valid cable tray fittings
when connected to valid cable tray objects.
You can create a fitting to join two connectors (at end of cable tray) by calling the Revit.Creation.Document.NewXyxFitting methods such as NewElbowFitting, NewTeeFitting etc. Each end of the cable tray segment has a connector. These methods take
the connectors as input arguments. To use them, you can implement the following steps:
1. Retrieve the connectors belonging to the cable tray, e.g. via cableTray.ConnectorManager.Connectors.
2. Determine a pair of connectors which should be connected, for instance by checking their position, connector type, or something else.
3. Call the appropriate NewXyzFitting method to create the Fitting element to join the connectors.
The AutoRoute and AvoidObstruction Revit SDK samples demonstrate some uses of the new fitting methods.
This explanation immediately leads to the next question:
Question: When we create cable tray objects programmatically, they always appear parallel to X axis, like this:

This is the result of some initial test code that creates individual cable tray elements, but no fittings. It naively creates the cable trays one by one using CableTray.Create, and as you can see that does not generate the fittings nor rotate the trays into the
right position relative to each other either.
When we create them manually through the user interface, the cable trays automatically rotate to line up with their predecessor element, and the appropriate fittings are added to connect them:

How can we achieve this through the API, please? What is the correct way to programmatically create a run of connected cable trays, i.e. add fittings and correct relative positions? Can you provide a sample that shows how to do this?
Answer: As said, you can simply use the New*Fitting methods to place the fittings between runs. However, I assume that the application has to ensure that the alignment and correct orientation is set up before they can create the fitting to connect
properly.
I initially tried to use the following code based on the ideas used in the AutoRoute SDK sample, but it does not work as expected:
ElementId idType = new ElementId( 411325 );
ElementId idLevel = new ElementId( 311 );
XYZ start1 = new XYZ( -30.49, 38.42, 10.05 );
XYZ end1 = new XYZ( -20.43, 30.83, 10.05 );
CableTray tray1 = CableTray.Create(
doc, idType, start1, end1, idLevel );
XYZ start2 = new XYZ( -20.43, 30.83, 10.05 );
XYZ end2 = new XYZ( -20.43, 30.83, 13.33 );
CableTray tray2 = CableTray.Create(
doc, idType, start2, end2, idLevel );
Connector c1start, c1end = null;
foreach( Connector c in
tray1.MEPSystem.ConnectorManager.Connectors )
{
if( c.Origin.IsAlmostEqualTo( start1 ) )
{
c1start = c;
}
else if( c.Origin.IsAlmostEqualTo( end1 ) )
{
c1end = c;
}
}
Connector c2start = null, c2end;
foreach( Connector c in
tray2.MEPSystem.ConnectorManager.Connectors )

{
if( c.Origin.IsAlmostEqualTo( start2 ) )
{
c2start = c;
}
else if( c.Origin.IsAlmostEqualTo( end2 ) )
{
c2end = c;
}
}
if( null != c1end && null != c2start )
{
doc.Create.NewElbowFitting( c1end, c2start );
}
The first reason it does not work is that the cable tray MEPSystem property is null, so the connectors cannot be found. Instead, the cable tray element has its own ConnectorManager property, which provides access to the required connectors.
After modifying the code to use that and adding the call to connect the two connectors, it succeeds, but the call to insert the elbow fitting still fails. Also, if I skip that call, the cable tray is still not rotated so that it matches up with its connected
neighbour, even if the connectors are successfully connected:
Connector c1start, c1end = null;
foreach( Connector c in
tray1.ConnectorManager.Connectors )
{
if( c.Origin.IsAlmostEqualTo( start1 ) )
{
c1start = c;
}
else if( c.Origin.IsAlmostEqualTo( end1 ) )
{
c1end = c;
}
}
Connector c2start = null, c2end;
foreach( Connector c in
tray2.ConnectorManager.Connectors )
{
if( c.Origin.IsAlmostEqualTo( start2 ) )
{
c2start = c;
}
else if( c.Origin.IsAlmostEqualTo( end2 ) )
{
c2end = c;
}
}
if( null != c1end && null != c2start )

{
c1end.ConnectTo( c2start );
// this throws
// Autodesk.Revit.Exceptions
// .InvalidOperationException:
// "failed to insert elbow".
doc.Create.NewElbowFitting( c1end, c2start );
}
The call NewElbowFitting which is used in the AutoRoute sample throws the exception "failed to insert elbow".
I see that you are testing with cable tray elements, which may be adding geometrical complications to getting the test code right. I would suggest trying the same code using conduit elements first, since they have a simpler geometry shape. For example,
the conduit probably does not care which direction is "up", etc. Also, another idea would be to draw some geometry using the user interface and analyse that. For example, lay out a run including pipe, elbow, and pipe, then extract the coordinates from it
and try to recreate it through API. That way you will know the geometry is possible to create, the radius and ends points for the elbow are "correct", etc.
From the initial exploration, we see that an application wishing to create a complete connected cable tray run will have to ensure that the cable trays are correctly aligned and also connect them with each other.
The AutoRoute and AvoidObstructions Revit SDK samples provide a useful starting point, showing how to create a connected mechanical HVAC duct system and pipe deviations around obstructions. Similar principles apply for cable trays as well. The
main differences between duct and cable tray are:
1. Cable tray has no system.
2. Cable tray and fittings have specific orientations.
One needs to manually place and orient cable trays in the proper locations before creating the fittings between them. The connections can be created regardless, however.
In the AutoRoute sample, the method CreateDucts calls the Revit API method NewDuct. It inserts a new duct element into the model and returns its two connectors at the end points. After creating the individual duct elements, the connectors are
hooked up with each other using methods like
m_document.Create.NewElbowFitting(
connectors[2], baseConn2 );
The duct connectors are retrieved through their MEPSystem property and its connector manager, which returns the connector set.
In the case of cable trays, the MEPSystem property is null, so it cannot be used to retrieve the connectors. Happily, they have their own connector manager property themselves, so there is no need to go through the MEPSystem property.
When I tried to connect the cable trays in a manner similar to the ducts in the AutoRoute sample, an exception was thrown:
Autodesk.Revit.Exceptions.InvalidOperationException:
"failed to insert elbow".
During the further analysis of how to set up the cable trays appropriately to insert the fittings, one initial idea was that one should not be connecting the two connectors for the two items directly, but foreshortening them so that the connector can be
properly inserted, e.g. leaving some space for the fittings. E.g., if the elbow radius is 6', the end of the first tray should be 6' away from the nominal intersection point, and the start of the second one as well, so that the elbow fits into the gap.
The AutoRoute sample does indeed cut back the ducts by a certain distance:
private const double min1FittingLength = 1;
As we shall see below, however, this is not required for conduits and cable trays.
When exploring the orientation of the cable trays in more depth, the next question was how to know what the correct orientation is, and how to set it once it is known?
One can look at the cable tray's connectors, either the property 'Angle' or some part of 'CoordinateSystem', to determine its orientation.
It also looks like some of the content is built 'on its side':

There are separate fittings for the various bends in the 'Conduit Type' definition:

Looking at the Vertical Outside family, it is 'on its side', and would need to be rotated 90 degrees on the 'front/back' axis. One still needs to figure out if that is +90 or -90:

There may be more rules used to orient content. They can be determined by inspecting the various bend families.
To continue with the topic of connecting the cable tray segments:
For testing purposes, it is significantly easier to start exploring the issue with conduit instead of cable tray elements. Connecting the conduits is simpler, because they have no orientation, being symmetrical.
It does not appear that the Conduit or CableTray elements care about the "foreshortening" or "lengthening" of the objects to get a proper fit. The main issue is that the objects have to be "oriented" correctly, otherwise Revit issues a message indicating it
is not correct. Basically, it cannot create a proper fitting due to alignment.
I started by testing creating conduit runs using objects that were all created in the same level plane. This works fine with no additional orientation required and shows that the lengthening and shortening is performed automatically as required to make a
good fit. The command CmdConduit in the attached sample code demonstrates this.
I then moved to cable tray and that also worked with the same simple test data used for the conduits, only switching to creating cable trays instead. An example in the plane is provided by the sample command CmdCableTray2.
The problem comes when creating the cable trays in different planes, because then the cable trays are not oriented appropriately well by default by the creation methods, and there is no way to specify something like an "up" normal during creation.
So it started by looking like this where the second one going "up" is not well oriented to make a fitting:

Then in the code I "rotated" the location property to align it with the first one:

Once it was "aligned", the fitting connected it with no further problems. In fact, the cals made by CmdCableTray3 automatically provide the proper shortening and lengthening as required to make the fitting work:

Looking in more detail at the orientation of the cable trays relative to each other, we see that access to their coordinate systems is provided by the CoordinateSystem property on the Connector object. For the ladder cable tray, it seems that the connector
coordinate system Z axis points straight out of the cable tray, parallel to its location line. The X axis points to the left hand side of the cable tray, and the Y axis points down:

So, for instance in order to create a 'flat' or 'sideways' elbow between two cable trays lying in the XY plane, you need to ensure that both of their connector Y axes are vertical.
With the Z axis of the connector coordinate system pointing straight out of the tray, and the Y axis perpendicular to the direction of the ladder rungs, in order to create an elbow between a cable tray lying in the XY plane with a vertical one, one has to
rotate the vertical tray so that the Y axis of its end connector is aligned with the Z axis of its predecessor's starting connector:
Transform t1 = c1end.CoordinateSystem;
Transform t2 = c2start.CoordinateSystem;
double angle = t2.BasisY.AngleOnPlaneTo(
t1.BasisZ, XYZ.BasisZ );
Line axis = app.Create.NewLineUnbound(
start2, XYZ.BasisZ );
tray2.Location.Rotate( axis, angle );
c1end.ConnectTo( c2start );
doc.Create.NewElbowFitting( c1end, c2start );
The sample command CmdCableTray4 finally produces the following, which is exactly what we are aiming for:

One can also cause a direction swapping fitting to be created by rotating the vertical tray so that its start connector X axis is aligned with the predecessor direction:
double angle = t2.BasisX.AngleOnPlaneTo(
t1.BasisZ, XYZ.BasisZ );
This results in this direction swapping fitting:

Here is the entire CableTray source code and the Visual Studio solution used to create each of the examples discussed above. Each is implemented in its own separate command class. It also includes a cleaned up version of the initial code:
CmdCableTray initial sample code.
CmdConduit connecting conduits in the XY plane.
CmdCableTray2 connecting cable trays in the XY plane.
CmdCableTray3 connecting cable trays outside the XY plane.
CmdCableTray4 connecting the first 'twisted' cable trays from the initial sample code.
Posted at 02:00 in 2011, Element Creation, Geometry, RME, SDK Samples | Permalink
Technorati Tags: Jeremy Tammik, Revit API
TrackBack
TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00e553e168978833013480ea65db970c
Listed below are links to weblogs that reference Cable Tray Orientation and Fittings:

Comments

Dave said...
Jeremy, I'm curious of there was ever a bug or feature request to modify that exception message ("failed to insert elbow").
I am running into a similar situation with creating a cross fitting for 4 pipes. As far as I can tell, there should be nothing wrong with the way the pipes are oriented and sized, but a similar InvalidOperationException is thrown with a similarly vague
message "failed to insert cross".
Any chance these exception messages could be improved somewhat?
Reply November 11, 2010 at 16:06

Jeremy Tammik said in reply to Dave...


Dear Dave,
No, I am not aware of any such plan. In order to file such a request, we would need a reproducible test case. Please submit a minimal sample model and add-in source code including the complete solution so that we can reproduce the issue, and then we
can log a wish for more detailed messages. If you are an ADN member, please submit an ADN DevHelp Online request for it. Thank you!
Cheers, Jeremy.
Reply November 12, 2010 at 01:39

Dave said in reply to Jeremy Tammik...


Ok, will look into that. Thanks.
Reply November 12, 2010 at 08:03

Account Deleted said...


Dear Jeremy Sir,
How can i create elbow Programatically in Revit MEp 2012.
Pls. Suggest
Thanks & Regards
Namit Jain
Reply July 04, 2011 at 04:28

Jeremy Tammik said in reply to Account Deleted...


Dear Namit,
Simply insert the appropriate family instance.
Cheers, Jeremy.
Reply July 04, 2011 at 21:19

Account Deleted said...


Dear sir,
How to lock all the elements together programmatically?
For instance, i have two small pipes and i place coupling in between them so they should automatically get locked?
Is this possible with current API(2012)? How?
Thanks & Regards,
-Nitin

Reply August 03, 2011 at 01:47

Jeremy Tammik said in reply to Account Deleted...


Dear Nitin,
I think you have several options for this: you could pin the elements to lock them, or you could create a group of them. You could also use DMU, the dynamic model update mechanism, to ensure that they stay together.
Cheers, Jeremy.
Reply August 09, 2011 at 02:41

cable tray said...


Six real scale cable tray fire tests both in horizontal and vertical orientation to simulate actual end user's environments were carried out.
Reply May 18, 2012 at 19:24

plumbing said...
Electronic cables can create an unsightly mess underneath a desk. Cable trays are a convenient way of organizing your cables and getting them out of sight. These wire mesh trays can be easily installed onto the back of just about any desk (excluding
glasstop desks). Though all cable trays will be shaped a little bit differently, basic setup remains mostly the same. Your cable trays should come with all the necessary brackets, screws and bolts necessary for installation.
Reply September 01, 2012 at 05:17

Larry said...
Hi Jeremy!!
The first, I'm sorry for my english language. I'm starting to learn api for revit. i am trying to make a cable tray fitting and I have follow your code that it help me a lot of. But I've a lot of questions, here some of there:
How can I change the dimensions for each cable tray I create by api in revit?
Can I to indicate a level before create a cable tray?
Is it possible to change the units to S.I. in mm in the Api?
I need to change the units, because I've list of points in ascii files for the situation of the cable trays.
Thank you in advance
Thanks & Regards
Reply March 26, 2014 at 12:55

Olga said...
Hi Jeremy!
Could you help me please with advice.
I have to create several separate HVAC elements (that are not connected to any other elements). I have only coordinates of center points for Connectors (e.g. for Round Elbow I have 2 points - center of circles at the start and end of Elbow).
To create this elbow I can use method document.Create.NewFamilyInstance(location, FamilyType, StructuralType), but calculate location of Elbow by start and end is impossible.
To use method
document.Create.NewElbowFitting(connector1, connector2);
I need connectors... I can't create connectors, because, as I can understand, it is more logical concept, not physical.
I tried to create Elbow with any location (using NewFamilyInstance method) and then move its connectors to correct position.
(foreach( Connector con in f.MEPModel.ConnectorManager.Connectors ) - get access to connectors),
but all position properties in Connector are readonly.
Could you please give an advice - how can I solve this problem?
Regards,
Olga
Reply October 03, 2014 at 10:06

Jeremy Tammik said in reply to Olga...


Dear Olga,
You create connector objects in the project context by adding connector family elements to the family definition.
These elements are converted to non-element connectors when an instance is placed:
http://thebuildingcoder.typepad.com/blog/2009/04/mep-connectors.html
To change their location in space, you need to transform the entire family instance, e.g. using the functionality provided by the ElementTransformUtils class.
Here is an example:

http://thebuildingcoder.typepad.com/blog/2014/01/explicitly-placing-rolling-offset-pipe-elbow-fittings.html
Cheers, Jeremy.
Reply October 06, 2014 at 04:00

Olga said...
Thank you Jeremy!
This information was very helpful.
Seems, that I have to create "dummy" ducts and then connect them, using NewElbowFitting/NewTeeFitting.
The only one thing that confuses me - is that we can't manage what type of Fitting Revit will choose.. We have to create correct type of dummy ducts in some positions, that won't give a chance to Revit choose wrong Type of Fitting.
To solve this task automatically seems to me not easy...
Regards,
Olga
Reply October 10, 2014 at 04:22

Gavin Guo said...


Jeremy,
I download the code, and try to run it in my environment, but I always get "Failed insert elbow" error, I have posted one thread in Revit Forum, and attached my code. http://forums.autodesk.com/t5/revit-api/get-quot-failed-to-insert-elbowquot-when-calling/m-p/5398815
Could you help me to have a look if you have time?
Appreciate for your help.
Thanks,
Gavin
Reply November 12, 2014 at 19:55

Jeremy Tammik said in reply to Gavin Guo...


Dear Gavin,
Thank you for your query and posting to the Revit API discussion forum with sample code.
I am taking a look at the issue now for you.
Thank you for your patience.
Cheers, Jeremy.
Reply November 25, 2014 at 02:07
Comment below or sign in with
Typepad

Facebook

Twitter

Google+ and more...

(You can use HTML tags like <b> <i> and <ul> to style your text. URLs automatically
linked.)

E mail addres s is not dis played with c omment.

Name
Email Address
Web Site URL
Post

Prev iew

Potrebbero piacerti anche