Introduction
In the many Industry, some customers need to route a very large number of parts in the Pipe/Tube/HVAC Duct/Raceway Tray discipline, the route point coordinates and other part characteristics coming from an input file (csv, Excel or xml files for instance).
Dedicated new EKL functions/methods to support automatic routing are now available from 2017x FD09.
In this demonstration, we use :
- an xml file containing the definition of 3 HVAC ducts and 1 pipe, definition in terms of nodes position, size (width, height, wall thickness), section shape, and orientation for the HVAC duct, and just size (outside diameter & wall thickness) for the pipe.
- an Expert Rule containing an EKL script to read the xml file and translate the information contained in the file into a structured data supplied to the different EKL methods to generate the duct and pipe parts.
- a product containing a HVAC Spool and a Piping Spool in which the duct and pipe parts will be automatically created.
The choice of Expert Rule to generate the ducts and pipes is motivated by the fact that it could be run in batch mode (KnowledgeExpertReport batch), allowing to process files containing the definition of hundreds of pipes, ducts or raceway trays.
Video 1:
Video2:
In the second video, we just modify a few things in the file and we generate the parts again, just to show the difference.
Dataset
EKL commands for pipe from DS doc
EKL CODE
/* inputName: ProdToGenerateRoutes inputType: VPMReference*/
if ProdToGenerateRoutes.V_Name->Search( "ProductToGenerateRoutes" ) == -1
{
exit
}
/* Prepare variables containing spools */
let instProductForHvac (VPMInstance)
instProductForHvac = ProdToGenerateRoutes.Children.GetItem(1)
let refProductForHvac (VPMReference)
refProductForHvac = instProductForHvac.Reference
let instHvacSpool (HVAC_Spool_Inst)
instHvacSpool = refProductForHvac.Children.GetItem(1)
let refHvacSpool (HVAC_Spool)
set refHvacSpool = instHvacSpool.Reference
let instProductForPipe (VPMInstance)
instProductForPipe = ProdToGenerateRoutes.Children.GetItem(2)
let refProductForPipe (VPMReference)
refProductForPipe = instProductForPipe.Reference
let instPipingSpool (VPMInstance)
instPipingSpool = refProductForPipe.Children.GetItem(1)
let refPipingSpool (Piping_Spool)
set refPipingSpool = instPipingSpool.Reference
/* Read the xml file containing routes data */
let xmlDocRoutes (XMLDocument)
xmlDocRoutes = CreateXMLDocument( "E:\\Work\\Customers\\Astrium\\V6Introduction\\WaveGuide\\2017x\\Tests\\AutomaticRouting\\Routes.xml" )
// Access the root node
let xmlRoot (XMLNode)
xmlRoot = xmlDocRoutes.Root
// Access the xmlElement named Routes
let xmlRoutes (XMLElement)
xmlRoutes = xmlRoot.Access( "Routes", "XMLElement" )
// Process all the routes found in the file
let n (Integer)
let lXmlRoutes (List)
lXmlRoutes = xmlRoutes.Children.Filter( "XMLElement", "" )
n = lXmlRoutes.Size()
let xmlPart (XMLElement)
let xmlSize (XMLElement)
let xmlWidth, xmlHeight, xmlCornerRadius, xmlThickness, xmlOuterDiameter (XMLElement)
let xmlWidthValue, xmlHeightValue, xmlCornerRadiusValue, xmlThicknessValue, xmlOuterDiameterValue (XMLText)
let xmlShape (XMLElement)
let xmlShapeValue (XMLText)
let xmlOrientation (XMLElement)
let xmlOrientationValue (XMLText)
let sPartName (String)
let sType (String)
let sWidth, sHeight, sCornerRadius, sWallThickness, sOuterDiameter (String)
let Width, Height, CornerRadius, WallThickness, OuterDiameter (LENGTH)
let hvOrientation (HvacOrientation)
let hvCurOrientation (HvacOrientation)
let hvShape (V_Shape_Enum)
let xmlPoints (XMLElement)
let lXmlPoints (List)
let xmlBendRadius (XMLElement)
let xmlBendRadiusValue (XMLText)
let BendRadius (LENGTH)
let xmlCoordinates (XMLElement)
let xmlCoordinatesValue (XMLText)
let sCoordinates (String)
let lCoordinates (List)
let X, Y, Z (LENGTH)
let p (Integer)
let xmlPoint (XMLElement)
let sPointName (String)
let i (Integer)
let refNewHvacPart (HVAC_Rigid_Duct)
let instNewHvacPart (HVAC_Rigid_Duct_Inst)
let refNewPipingPart (Piping_Rigid_Pipe)
let instNewPipingPart (Piping_Rigid_Pipe_Inst)
let lPoints (List)
let lBendRadii (List)
let P (Point)
let gsmP (GSMPoint)
let gsmPcoord (GSMPointCoord)
let obPoints (OpenBodyFeature)
let lRouteNodes (List)
let nPoints (Integer)
let retCode (Integer)
i = 1
for i while i <= n
{
// Get info on the first level node
xmlPart = lXmlRoutes[i]
// Read part name and type (HVAC or Pipe)
sPartName = xmlPart.GetAttributeString( "id" )
sType = xmlPart.GetAttributeString( "type" )
Trace( 1, "Processing # which is a #", sPartName, sType )
// Get the size node
xmlSize = xmlPart.Access( "Size", "XMLElement" )
if sType == "HVAC"
{
// If HVAC, read Width, Height, CornerRadius, Shape and Orientation information from the xml node
xmlWidth = xmlSize.Access( "Width", "XMLElement" )
set xmlWidthValue = xmlWidth.Find( "XMLText", "x.Text <> \\"\\"", true )
sWidth = xmlWidthValue.Text
Width = sWidth.ToDimension( "Length" )
xmlHeight = xmlSize.Access( "Height", "XMLElement" )
set xmlHeightValue = xmlHeight.Find( "XMLText", "x.Text <> \\"\\"", true )
sHeight = xmlHeightValue.Text
Height = sHeight.ToDimension( "Length" )
Trace( 2, "Size read: # x #", Width, Height )
xmlShape = xmlPart.Access( "Shape", "XMLElement" )
set xmlShapeValue = xmlShape.Find( "XMLText", "x.Text <> \\"\\"", true )
hvShape = xmlShapeValue.Text
Trace( 2, "Shape = #", hvShape )
if hvShape == "Rectangle"
{
xmlCornerRadius = xmlSize.Access( "CornerRadius", "XMLElement" )
if xmlCornerRadius <> NULL
{
set xmlCornerRadiusValue = xmlCornerRadius.Find( "XMLText", "x.Text <> \\"\\"", true )
sCornerRadius = xmlCornerRadiusValue.Text
CornerRadius = sCornerRadius.ToDimension( "Length" )
}
else
{
// Set a default value to the corner radius in case the tag is not present in the file.
CornerRadius = 0.1mm
Trace( 3, "The tag was not found for the shape Rectangle as specified, a default value of 0.1mm is used." )
}
}
xmlOrientation = xmlPart.Access( "Orientation", "XMLElement" )
set xmlOrientationValue = xmlOrientation.Find( "XMLText", "x.Text <> \\"\\"", true )
if xmlOrientationValue.Text == "Default"
{
hvOrientation = "CATHvaDuctVerticalUp"
}
else
{
hvOrientation = "CATHvaDuctHorizontalLeft"
}
Trace( 2, "Orientation = #", hvOrientation )
}
else
{
// If Pipe, read outer diameter from the xml node
xmlOuterDiameter = xmlSize.Access( "OutsideDiameter", "XMLElement" )
set xmlOuterDiameterValue = xmlOuterDiameter.Find( "XMLText", "x.Text <> \\"\\"", true )
sOuterDiameter = xmlOuterDiameterValue.Text
OuterDiameter = sOuterDiameter.ToDimension( "Length" )
Trace( 2, "Diameter read: #", OuterDiameter )
}
xmlThickness = xmlSize.Access( "Thickness", "XMLElement" )
set xmlThicknessValue = xmlThickness.Find( "XMLText", "x.Text <> \\"\\"", true )
sWallThickness = xmlThicknessValue.Text
WallThickness = sWallThickness.ToDimension( "Length" )
Trace( 2, "WallThickness = #", WallThickness )
/* ========== Read the Points =========== */
// Access the Points xml node
xmlPoints = xmlPart.Access( "Points", "XMLElement" )
// Get all the points info (coordinates, bend radius)
lXmlPoints = xmlPoints.Children.Filter( "XMLElement", "" )
p = 1
for p while p <= lXmlPoints.Size()
{
// Accessing xml element related to one point
xmlPoint = lXmlPoints[p]
// Get its name
sPointName = xmlPoint.GetAttributeString( "id" )
// Read Bend Radius
xmlBendRadius = xmlPoint.Access( "BendRadius", "XMLElement" )
set xmlBendRadiusValue = xmlBendRadius.Find( "XMLText", "x.Text <> \\"\\"", true )
BendRadius = xmlBendRadiusValue.Text->ToDimension( "Length" )
// Read coordinates
xmlCoordinates = xmlPoint.Access( "Coordinates", "XMLElement" )
set xmlCoordinatesValue = xmlCoordinates.Find( "XMLText", "x.Text <> \\"\\"", true )
sCoordinates = xmlCoordinatesValue.Text
lCoordinates = SplitString( sCoordinates, ", " )
X = ( lCoordinates[1] ) ->ToDimension( "Length" )
Y = ( lCoordinates[2] ) ->ToDimension( "Length" )
Z = ( lCoordinates[3] ) ->ToDimension( "Length" )
Trace( 3, "# (Bend radius = #, X=#, Y=#, Z=#)", sPointName, BendRadius, X, Y, Z )
lPoints.Append( List( sPointName, X, Y, Z ) )
lBendRadii.Append( BendRadius )
}
// Create the part
if sType == "HVAC"
{
// Create the new duct reference
refNewHvacPart = new( "HVAC_Rigid_Duct", sPartName, NULL )
// Instantiate it under the HVAC spool
instNewHvacPart = new( "HVAC_Rigid_Duct_Inst", "", refHvacSpool, refNewHvacPart )
// Create it as a "New Reference"
instNewHvacPart.SetNewReference( NULL )
// Set its shape
refNewHvacPart.SetShape( hvShape )
// Set its dimensions
refNewHvacPart.V_Width = Width
refNewHvacPart.V_Height = Height
if hvShape == "Rectangle"
{
refNewHvacPart.V_CornerRadius = CornerRadius
}
refNewHvacPart.V_WallThickness = WallThickness
// Create the points in the duct inside the first geometrical set
set obPoints = refNewHvacPart.Find( "OpenBodyFeature", "", true )
if obPoints == NULL
{
Trace( 3, "Warning!! No geometrical set was created upon the creation of the part. Please check your Preferences > Infrastructure > 3DShape Infrastructure > 3DShape" )
Trace( 3, "We can not generate this duct, skipping to the next one." )
continue
}
p = 1
nPoints = lPoints.Size()
for p while p <= nPoints
{
// First get information on the point to create (name and coordinates)
sPointName = lPoints[p][1]
X = lPoints[p][2]
Y = lPoints[p][3]
Z = lPoints[p][4]
// Create a new point
P = new( "Point", sPointName, obPoints )
set gsmP = P
if gsmP <> NULL
{
// Make this point a "Point with coordinates" type
gsmP.PointType = "Coordinates"
set gsmPcoord = gsmP
}
if gsmPcoord <> NULL
{
// Define the coordinates to the point, and its name
gsmPcoord.X = X
gsmPcoord.Y = Y
gsmPcoord.Z = Z
gsmPcoord.Name = sPointName
gsmPcoord.Update()
gsmPcoord.Show = false
// Populate the list of nodes to be used to route the duct
lRouteNodes.Append( P )
}
}
// Route the duct through the points
retCode = refNewHvacPart.Add3DPoint( lRouteNodes, 0 )
Trace( 2, "Adding the points resulted in the return code: #", retCode )
// Remove the two initial points that are automatically created upon the creation of the new duct.
retCode = refNewHvacPart.RemoveNode( List( nPoints+1, nPoints+2 ) )
Trace( 2, "Removing the two inial points resulted in the return code: #", retCode )
// Assign the right bend radius on each node
p = 1
for p while p <= lBendRadii.Size()
{
BendRadius = lBendRadii[p]
retCode = refNewHvacPart.SetNodeBendRadius( p, BendRadius )
Trace( 3, "Assign the radius # to node # resulted in the return code: #", BendRadius, p, retCode )
}
// Set its orientation
refNewHvacPart.SetSectionOrientation( hvOrientation )
// Propagate the geometry
retCode = instNewHvacPart->PropagateReferenceToGeometry()
Trace( 2, "Propagating the geometry resulted in the return code: #", retCode )
}
else
{
// Create the new pipe reference
refNewPipingPart = new( "Piping_Rigid_Pipe", sPartName, NULL )
// Instantiate it under the Piping spool
instNewPipingPart = new( "Piping_Rigid_Pipe_Inst", "", refPipingSpool, refNewPipingPart )
// Create it as a "New Reference"
instNewPipingPart.SetNewReference( NULL )
// Set its dimensions
refNewPipingPart.V_OutsideDiameter = OuterDiameter
refNewPipingPart.V_WallThickness = WallThickness
// Create the points in the pipe inside the first geometrical set
set obPoints = refNewPipingPart.Find( "OpenBodyFeature", "", true )
if obPoints == NULL
{
Trace( 3, "Warning!! No geometrical set was created upon the creation of the part. Please check your Preferences > Infrastructure > 3DShape Infrastructure > 3DShape" )
Trace( 3, "We can not generate this pipe, skipping to the next one." )
continue
}
p = 1
nPoints = lPoints.Size()
for p while p <= nPoints
{
// First get information on the point to create (name and coordinates)
sPointName = lPoints[p][1]
X = lPoints[p][2]
Y = lPoints[p][3]
Z = lPoints[p][4]
// Create a new point
P = new( "Point", sPointName, obPoints )
set gsmP = P
if gsmP <> NULL
{
// Make this point a "Point with coordinates" type
gsmP.PointType = "Coordinates"
set gsmPcoord = gsmP
}
if gsmPcoord <> NULL
{
// Define the coordinates to the point, and its name
gsmPcoord.X = X
gsmPcoord.Y = Y
gsmPcoord.Z = Z
gsmPcoord.Name = sPointName
gsmPcoord.Update()
gsmPcoord.Show = false
// Populate the list of nodes to be used to route the duct
lRouteNodes.Append( P )
}
}
// Route the duct through the points
retCode = refNewPipingPart.Add3DPoint( lRouteNodes, 0 )
Trace( 2, "Adding the points resulted in the return code: #", retCode )
// Remove the two initial points that are automatically created upon the creation of the new duct.
retCode = refNewPipingPart.RemoveNode( List( nPoints+1, nPoints+2 ) )
Trace( 2, "Removing the two initial points resulted in the return code: #", retCode )
// Set a default radius, otherwise the assignment of different bend radius doesn't work ...
retCode = refNewPipingPart.SetDefaultBendRadius( 10mm )
Trace( 2, "Setting a default radius of 10mm resulted in the return code: #", retCode )
// Assign the right bend radius on each node
p = 1
for p while p <= lBendRadii.Size()
{
BendRadius = lBendRadii[p]
retCode = refNewPipingPart.SetNodeBendRadius( p, BendRadius )
Trace( 3, "Assign the radius # to node # resulted in the return code: #", BendRadius, p, retCode )
}
// Propagate the geometry
retCode = instNewPipingPart->PropagateReferenceToGeometry()
Trace( 2, "Propagating the geometry resulted in the return code: #", retCode )
}
lRouteNodes.RemoveAll()
lPoints.RemoveAll()
lBendRadii.RemoveAll()
}
ReframeOn( ProdToGenerateRoutes )
