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 tagwas 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 )